nl.vpro.domain.user.Editor Maven / Gradle / Ivy
Show all versions of user-domain Show documentation
/*
* Copyright (C) 2012 Licensed under the Apache License, Version 2.0
* VPRO The Netherlands
*/
package nl.vpro.domain.user;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import java.io.Serial;
import java.time.Instant;
import java.util.*;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import jakarta.persistence.*;
import jakarta.validation.Valid;
import jakarta.xml.bind.annotation.*;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import nl.vpro.domain.Accountable;
/**
*
* An {@link Editor} is the entity that is used in POMS (and related system) to contain information about the user or
* 'process' that is performing the actions (e.g. the person who is logged in in the GUI) . It contains authentication and authorization information (like {@link #getRoles()} , {@link #getEmployer()} and {@link #getAllowedBroadcasters()}) about this person or user.
*
*
* In the poms database it is the database entity that is linked to objects to indicate meta data like {@link Accountable#getCreatedBy()} and {@link Accountable#getLastModifiedBy()}
*
*
* It serves as the main information source to determin whether certain actions are permitted or not (e.g. implemented in a nl.vpro.spring.security.acl.MediaPermissionEvaluator
*
*/
@Inheritance(strategy = InheritanceType.JOINED)
@Cacheable(true)
@XmlAccessorType(XmlAccessType.FIELD)
@Entity
@Slf4j
public class Editor extends AbstractUser {
@Serial
private static final long serialVersionUID = -4381169912123229285L;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "editor")
@Valid
@XmlTransient
Set broadcasters = new TreeSet<>();
@OneToMany(cascade = CascadeType.ALL, mappedBy = "editor")
@Valid
@XmlTransient
protected Set portals = new TreeSet<>();
@OneToMany(cascade = CascadeType.ALL, mappedBy = "editor")
@Valid
@XmlTransient
@OrderBy("organization.id asc")
protected Set thirdParties = new TreeSet<>();
@Transient
@Nullable
private SortedSet allowedBroadcasterCache;
@Transient
@Nullable
private SortedSet activeBroadcasterCache;
@Transient
@Nullable
private SortedSet allowedPortalCache;
@Transient
@Nullable
private SortedSet activePortalCache;
@Transient
@Nullable
private SortedSet allowedThirdPartyCache;
@Transient
@Nullable
private SortedSet activeThirdPartyCache;
@Transient
@XmlTransient
@Nullable
private Supplier> rolesProvider = null;
@Transient
@MonotonicNonNull
private Set roles = null;
//@Version
@Getter
@XmlTransient
protected int version = 0;
public Editor(Editor editor) {
super(editor);
this.broadcasters.addAll(editor.broadcasters);
this.portals.addAll(editor.portals);
this.thirdParties.addAll(editor.thirdParties);
this.rolesProvider = editor.rolesProvider;
this.roles = editor.roles;
this.lastLogin = editor.lastLogin;
this.loginCount = editor.loginCount;
this.version = editor.version;
}
protected Editor() {
}
public Editor(String principalId, String displayName, String email, Broadcaster broadcaster, Set roles) {
this(principalId, displayName, null, null, email, broadcaster, roles, null);
}
Editor(String principalId, String displayName, String email, Broadcaster broadcaster, String givenName, String familyName, Instant lastLogin) {
this(principalId, displayName, givenName, familyName, email, broadcaster, Collections.emptySet(), lastLogin);
}
@lombok.Builder(builderClassName = "Builder")
Editor(
String principalId,
String displayName,
String givenName,
String familyName,
String email,
Broadcaster broadcaster,
@lombok.Singular Set roles,
Instant lastLogin
) {
super(principalId, displayName, email);
if (broadcaster != null) {
broadcasters.add(new BroadcasterEditor(this, broadcaster, true));
}
if (roles == null) {
log.warn("No roles for {}", this.principalId);
}
this.roles = roles == null ? Collections.emptySet() : Collections.unmodifiableSet(roles);
this.givenName = givenName;
this.familyName = familyName;
this.lastLogin = lastLogin;
}
public Editor copy() {
return new Editor(this);
}
public boolean hasEqualRights(Editor editor) {
return editor != null
&&
Objects.equals(this.getPrincipalId(), editor.getPrincipalId()) &&
Objects.equals(this.getRoles(), editor.getRoles()) &&
Objects.equals(this.getAllowedBroadcasters(), editor.getAllowedBroadcasters()) &&
Objects.equals(this.getAllowedPortals(), editor.getAllowedPortals()) &&
Objects.equals(this.getAllowedThirdParties(), editor.getAllowedThirdParties());
}
public void setRoles(Set roles) {
if (roles == null && this.rolesProvider != null) {
log.warn("Setting roles to null!");
}
this.roles = roles;
this.rolesProvider = null;
}
/**
* @see nl.vpro.domain.Roles
*/
public Set getRoles() {
if (roles == null && rolesProvider != null) {
roles = rolesProvider.get();
rolesProvider = null;
}
if (roles == null) {
throw new IllegalStateException("This user object does not have role information");
}
return this.roles;
}
public void supplyRoles(Supplier> roles) {
this.rolesProvider = roles;
this.roles = null;
}
public void supplyRolesIfNeeded(Supplier> roles) {
if (this.rolesProvider == null && this.roles == null) {
supplyRoles(roles);
}
}
public boolean rolesLoaded() {
return roles != null;
}
@Nullable
public Broadcaster getEmployer() {
for(BroadcasterEditor rel : broadcasters) {
if(rel.isEmployee()) {
return rel.getOrganization();
}
}
return null;
}
public void setEmployer(final Broadcaster broadcaster) {
BroadcasterEditor toAdd = broadcaster == null ? null : new BroadcasterEditor(this, broadcaster, true);
boolean found = false;
for (BroadcasterEditor existing : broadcasters) {
if (toAdd != null && toAdd.equals(existing)) {
found = true;
existing.setEmployee(true);
} else {
existing.setEmployee(false);
}
}
if (! found && toAdd != null) {
broadcasters.add(toAdd);
}
allowedBroadcasterCache = null;
}
public SortedSet getAllowedBroadcasters() {
if(allowedBroadcasterCache == null) {
allowedBroadcasterCache = new TreeSet<>();
Broadcaster broadcaster = getEmployer();
if(broadcaster != null) {
allowedBroadcasterCache.add(broadcaster);
}
for(BroadcasterEditor rel : broadcasters) {
final Broadcaster organization = rel.getOrganization();
if(organization != null) {
allowedBroadcasterCache.add(organization);
}
}
allowedBroadcasterCache = Collections.unmodifiableSortedSet(allowedBroadcasterCache);
}
return allowedBroadcasterCache;
}
public SortedSet getActiveBroadcasters() {
if(activeBroadcasterCache == null) {
activeBroadcasterCache = new TreeSet<>();
for(BroadcasterEditor rel : broadcasters) {
if(rel.isActive()) {
final Broadcaster organization = rel.getOrganization();
if(organization != null) {
activeBroadcasterCache.add(organization);
}
}
}
activeBroadcasterCache = Collections.unmodifiableSortedSet(activeBroadcasterCache);
}
return activeBroadcasterCache;
}
public boolean isActive(Broadcaster broadcaster) {
for (BroadcasterEditor be : broadcasters) {
if (broadcaster.equals(be.getOrganization())) {
return be.isActive();
}
}
return false;
}
boolean setActiveBroadcaster(String broadcasterId, boolean value) {
return setActive(new Broadcaster(broadcasterId, broadcasterId), value);
}
public boolean setActive(Broadcaster broadcaster, boolean value) {
for (BroadcasterEditor be : broadcasters) {
if (broadcaster.equals(be.getOrganization())) {
if (be.active != value) {
be.setActive(value);
activeBroadcasterCache = null;
return true;
} else {
return false;
}
}
}
if (value) {
throw new IllegalArgumentException("No broadcaster " + broadcaster + " found in " + broadcasters);
} else {
// nothing to do
return false;
}
}
public boolean addBroadcaster(Broadcaster broadcaster) {
BroadcasterEditor toAdd = new BroadcasterEditor(this, broadcaster);
if (broadcasters.add(toAdd)) {
allowedBroadcasterCache = null;
activeBroadcasterCache = null;
return true;
} else {
return false;
}
}
@Nullable
public BroadcasterEditor removeBroadcaster(Broadcaster broadcaster) {
BroadcasterEditor toRemove = remove(broadcasters, broadcaster);
if (toRemove != null) {
activeBroadcasterCache = null;
allowedBroadcasterCache = null;
}
return toRemove;
}
@Nullable
private static > T remove(Collection collection, S organization) {
T toRemove = null;
for (T e : collection) {
if (e.getOrganization().equals(organization)) {
toRemove = e;
break;
}
}
if (toRemove != null) {
collection.remove(toRemove);
}
return toRemove;
}
/**
* Returns the set of Portals this editor is a member of.
*/
public SortedSet getAllowedPortals() {
if(allowedPortalCache== null) {
allowedPortalCache = new TreeSet<>();
for(PortalEditor rel : portals) {
if (rel.getOrganization() != null) {
allowedPortalCache.add(rel.getOrganization());
}
}
allowedPortalCache = Collections.unmodifiableSortedSet(allowedPortalCache);
}
return allowedPortalCache;
}
/**
* Returns the set of Portals this editor currenly configure to be 'active'
* This mains that new objects will receive this portal on default.
*/
SortedSet getActivePortals() {
if(activePortalCache == null) {
activePortalCache = new TreeSet<>();
for(PortalEditor rel : portals) {
if(rel.isActive()) {
activePortalCache.add(rel.getOrganization());
}
}
activePortalCache = Collections.unmodifiableSortedSet(activePortalCache);
}
return activePortalCache;
}
boolean isActive(Portal portal) {
for (PortalEditor be : portals) {
if (portal.equals(be.getOrganization())) {
return be.isActive();
}
}
return false;
}
boolean setActivePortal(String portalId, boolean value) {
return setActive(new Portal(portalId, null), value);
}
boolean setActive(Portal portal, boolean value) {
for (PortalEditor be : portals) {
if (portal.equals(be.getOrganization())) {
if (be.isActive() != value) {
be.setActive(value);
activePortalCache = null;
return true;
} else {
return false;
}
}
}
if (value) {
throw new IllegalArgumentException();
} else {
return false;
}
}
public boolean addPortal(Portal portal) {
if (portal == null) {
log.warn("Cannot add null to {}", this);
return false;
}
PortalEditor toAdd = new PortalEditor(this, portal);
if(portals.add(toAdd)) {
allowedPortalCache = null;
activePortalCache = null;
return true;
}
return false;
}
@Nullable
public PortalEditor removePortal(Portal portal) {
PortalEditor toRemove = remove(portals, portal);
if (toRemove != null) {
allowedPortalCache = null;
activePortalCache = null;
}
return toRemove;
}
/**
* Returns the set of {@link ThirdParty}s this editor is a member of.
*/
public SortedSet getAllowedThirdParties() {
if(allowedThirdPartyCache == null) {
allowedThirdPartyCache = new TreeSet<>();
for(ThirdPartyEditor rel : thirdParties) {
allowedThirdPartyCache.add(rel.getOrganization());
}
allowedThirdPartyCache = Collections.unmodifiableSortedSet(allowedThirdPartyCache);
}
return allowedThirdPartyCache;
}
public boolean addThirdParty(ThirdParty thirdParty) {
if (thirdParty == null) {
log.warn("Cannot add null to {}", this);
return false;
}
ThirdPartyEditor toAdd = new ThirdPartyEditor(this, thirdParty);
toAdd.setActive(true);
if(thirdParties.add(toAdd)) {
allowedThirdPartyCache = null;
activeThirdPartyCache = null;
return true;
} else {
return false;
}
}
@Nullable
public ThirdPartyEditor removeThirdParty(ThirdParty thirdParty) {
ThirdPartyEditor toRemove = remove(thirdParties, thirdParty);
if (toRemove != null) {
allowedThirdPartyCache = null;
activeThirdPartyCache = null;
}
return toRemove;
}
Collection getOrganizations() {
return Stream.concat(
Stream.concat(
getAllowedBroadcasters().stream(),
getAllowedPortals().stream()),
getAllowedThirdParties().stream())
.collect(Collectors.toSet());
}
void addOrganization(Organization organization) {
if (organization instanceof Broadcaster) {
addBroadcaster((Broadcaster) organization);
} else if (organization instanceof Portal) {
addPortal((Portal) organization);
} else if (organization instanceof ThirdParty) {
addThirdParty((ThirdParty) organization);
} else {
throw new IllegalArgumentException("Unknown organization type: " + organization);
}
}
@Nullable
String getOrganization() {
Broadcaster b = getEmployer();
return b == null ? null : b.getId();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("Editor");
sb.append("{principalId='").append(principalId).append('\'');
sb.append(", displayName='").append(displayName).append('\'');
sb.append(", email='").append(email).append('\'');
sb.append(", version='").append(version).append('\'');
sb.append('}');
return sb.toString();
}
@PreUpdate
protected void preupdate() {
this.version++;
}
public static Optional getDomain(Editor editor) {
return getDomain(editor.getEmail());
}
public static Optional getDomain(@Nullable String mail) {
if (mail == null) {
return Optional.empty();
}
int i = mail.indexOf('@');
if (i > 0) {
return Optional.of(mail.substring(i + 1));
} else {
return Optional.empty();
}
}
}