/* * Copyright (C) 2014 GG-Net GmbH - Oliver Günther * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.ggnet.dwoss.rights.entity; import java.io.Serializable; import java.util.*; import javax.persistence.*; import javax.validation.constraints.NotNull; import eu.ggnet.dwoss.rights.api.AtomicRight; import eu.ggnet.dwoss.util.persistence.EagerAble; import eu.ggnet.dwoss.util.persistence.entity.IdentifiableEntity; import javafx.beans.property.*; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.*; import javafx.collections.ListChangeListener.Change; import lombok.*; import static javax.persistence.FetchType.EAGER; /** * This Entity represent a Operator in the Database with his rights. * <p> * @author Bastian Venz */ @Entity @NoArgsConstructor @ToString(exclude = "personas") @NamedQueries({ @NamedQuery(name = "Operator.byUsername", query = "Select i from Operator as i where i.username = ?1"), @NamedQuery(name = "Operator.byUsernameAndPasswordAndSalt", query = "Select i from Operator as i where i.username = ?1 AND i.password = ?2 AND i.salt = ?3")}) public class Operator extends IdentifiableEntity implements Serializable, EagerAble { @Id @GeneratedValue @Getter private long id; /** * Integer value for optimistic locking. */ @Version @Getter @Setter private int optLock; @Getter @Setter private int quickLoginKey; @ElementCollection @NotNull private List<AtomicRight> rights = new ArrayList<>(); @NotNull @Getter @Setter private String username; /** * The byte[] which holds the salt of the Password. * Can be null if no Password is required. */ @Getter @Setter private byte[] salt; /** * The byte[] which holds the Password. * Can be null if no Password is required. */ @Getter private byte[] password; @ManyToMany(fetch = EAGER) @NotNull private List<Persona> personas = new ArrayList<>(); @Transient private transient ReadOnlyLongProperty idProperty; @Transient private transient StringProperty usernameProperty; @Transient private transient IntegerProperty quickLoginKeyProperty; @Transient private transient ListProperty<AtomicRight> rightsProperty; @Transient private transient StringProperty saltProperty; @Transient private transient StringProperty passwordProperty; @Transient private transient ListProperty<Persona> personasProperty; /** * This is a CopyConstructor which copys the Data from the DTO {@link eu.ggnet.dwoss.rights.api.Operator} to this class, EXCEPT for the Persona List! * <p> * @param dtoOperator */ public Operator(eu.ggnet.dwoss.rights.api.Operator dtoOperator) { this.quickLoginKey = dtoOperator.getQuickLoginKey(); this.rights = dtoOperator.getRights(); this.username = dtoOperator.getUsername(); } public Operator(String username) { this.username = username; } public ReadOnlyLongProperty idProperty() { if ( idProperty == null ) { idProperty = new ReadOnlyLongWrapper(id); } return idProperty; } public StringProperty usernameProperty() { if ( usernameProperty == null ) { usernameProperty = new SimpleStringProperty(username); usernameProperty.addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) { username = newValue; } }); } return usernameProperty; } public IntegerProperty quickLoginKeyProperty() { if ( quickLoginKeyProperty == null ) { quickLoginKeyProperty = new SimpleIntegerProperty(quickLoginKey); quickLoginKeyProperty.addListener(new ChangeListener<Number>() { @Override public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) { quickLoginKey = newValue.intValue(); } }); } return quickLoginKeyProperty; } public ListProperty<AtomicRight> rightsProperty() { if ( rightsProperty == null ) { rightsProperty = new SimpleListProperty<>(FXCollections.observableList(rights)); rightsProperty.get().addListener(new ListChangeListener<AtomicRight>() { @Override public void onChanged(Change<? extends AtomicRight> change) { while (change.next()) { if ( !change.wasAdded() ) continue; for (AtomicRight addedRight : change.getAddedSubList()) { for (Persona persona : personas) { if ( persona.getPersonaRights().contains(addedRight) ) { change.getList().remove(addedRight); } } if ( containsMoreThanOnce(rights, addedRight) ) { change.getList().remove(addedRight); } } } } }); } return rightsProperty; } private static <T> boolean containsMoreThanOnce(Collection<T> collection, T elem) { int count = 0; for (T t : collection) { if ( Objects.equals(t, elem) ) count++; } return count > 1; } public ListProperty<Persona> personasProperty() { if ( personasProperty == null ) { personasProperty = new SimpleListProperty<>(FXCollections.observableList(personas)); personasProperty.get().addListener(new ListChangeListener<Persona>() { @Override public void onChanged(Change<? extends Persona> change) { while (change.next()) { if ( change.wasAdded() ) { for (Persona persona : change.getAddedSubList()) { // Remove local duplicates for (AtomicRight personaRight : persona.getPersonaRights()) { if ( getRights().contains(personaRight) ) { remove(personaRight); } } } } } } }); } return personasProperty; } public StringProperty saltProperty() { if ( saltProperty == null ) { String saltString = (salt != null) ? new String(salt) : ""; saltProperty = new SimpleStringProperty(saltString); saltProperty.addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) { salt = newValue.getBytes(); } }); } return saltProperty; } public StringProperty passwordProperty() { if ( passwordProperty == null ) { String pw = (password != null) ? new String(password) : ""; passwordProperty = new SimpleStringProperty(pw); passwordProperty.addListener(new ChangeListener<String>() { @Override public void changed(ObservableValue<? extends String> ov, String oldValue, String newValue) { password = newValue.getBytes(); } }); } return passwordProperty; } public void setPassword(byte[] password) { if ( passwordProperty != null ) passwordProperty.set((password != null) ? new String(password) : ""); else this.password = password; } /** * Defensive copy return of rights. * <p> * @return defensive copy return of rights */ public List<AtomicRight> getRights() { return new ArrayList<>(rights); } /** * Returns all active rights, meaning the rights of this operator and all rights of assosiated personas. * <p> * @return all active rights. */ public EnumSet<AtomicRight> getAllActiveRights() { EnumSet<AtomicRight> resultRights = EnumSet.noneOf(AtomicRight.class); resultRights.addAll(rights); for (Persona persona : personas) { resultRights.addAll(persona.getPersonaRights()); } return resultRights; } /** * Defensive copy return of persona. * <p> * @return defensive copy return of persona */ public List<Persona> getPersonas() { return new ArrayList<>(personas); } /** * This method add a {@link AtomicRight} IF it not null or already in the List. * <p> * @param atomicRight */ public void add(AtomicRight atomicRight) { if ( atomicRight != null && !rights.contains(atomicRight) ) { rights().add(atomicRight); } } public void remove(AtomicRight atomicRight) { rights().remove(atomicRight); } /** * This method add all {@link AtomicRight}'s IF it not null or already in the List. * <p> * @param right */ public void addAllRight(Collection<AtomicRight> right) { for (AtomicRight atomicRight : right) { add(atomicRight); } } public void removeAllRight(Collection<AtomicRight> right) { rights().removeAll(right); } /** * This method add a {@link Persona} IF it not null or already in the List. * <p> * @param persona */ public void add(Persona persona) { if ( persona != null && !personas.contains(persona) ) personas().add(persona); } public void remove(Persona persona) { personas().remove(persona); } public void removeAllPersona(Collection<Persona> persona) { personas().removeAll(persona); } /** * This method add all {@link Persona}'s IF it not null or already in the List. * <p> * @param persona */ public void addAllPersona(Collection<Persona> persona) { for (Persona persona1 : persona) { add(persona1); } } /** * Create a {@link eu.ggnet.dwoss.rights.api.Operator} object from this instance. * <p> * @return */ public eu.ggnet.dwoss.rights.api.Operator toDto() { return new eu.ggnet.dwoss.rights.api.Operator(username, quickLoginKey, new ArrayList<>(getAllActiveRights())); } /** * This method will be called bevor persisting and will remove all duplicated rights in the rights list and remove all rights that are already in the * Personas. */ @PrePersist @PreUpdate public void preStrored() { for (Persona persona : personas) { rights.removeAll(persona.getPersonaRights()); } } @Override public void fetchEager() { rights.size(); for (Persona persona : personas) { persona.fetchEager(); } } private List<AtomicRight> rights() { if ( rightsProperty != null ) return rightsProperty.get(); else return rights; } private List<Persona> personas() { if ( personasProperty != null ) return personasProperty.get(); else return personas; } }