User.java
package com.tradecloud.authentication;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.tradecloud.common.base.PersistenceBase;
import com.tradecloud.common.externalreference.IntegratedSystem;
import com.tradecloud.domain.base.utils.DateUtils;
import com.tradecloud.domain.configuration.*;
import com.tradecloud.domain.model.organisationalunit.OrganisationalUnit;
import com.tradecloud.domain.party.Employee;
import com.tradecloud.domain.party.ServiceProvider;
import com.tradecloud.domain.party.base.Contact;
import com.tradecloud.domain.place.Region;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.ForeignKey;
import org.hibernate.annotations.NaturalId;
import org.springframework.security.core.userdetails.UserDetails;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.xml.bind.annotation.*;
import java.io.Serializable;
import java.security.Principal;
import java.util.*;
/**
* We'd normally call DB tables by the singular e.g. user But user is a reserved
* word in PSQL so we make the exception here and use users.
*/
@Entity
@Table(name = "users", uniqueConstraints = {@UniqueConstraint(columnNames = {"username"})})
@Access(AccessType.FIELD)
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement
public class User extends PersistenceBase implements Serializable, UserDetails, Principal, Comparable<User> {
private static final long serialVersionUID = 1L;
public static final int PASSWORD_EXPIRY_DAYS = 30;
public static final int PASSWORD_EXPIRY_WARNING_DAYS = 5;
public static final int MAX_INCORRECT_LOGIN_ATTEMPTS = 3;
@XmlAttribute
@NotNull(message = "Username is required")
@Size(max = 255, message = "Username must be less than 255 characters")
@NaturalId
private String username;
@NotNull(message = "Password is required")
@XmlAttribute
private String password;
@XmlAttribute
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date passwordChangedDate;
@XmlAttribute
@Transient
private String plainCurrentPassword;
@XmlAttribute
@Transient
private String plainNewPassword;
@NotNull(message = "Primary client is required")
@XmlElement
private String primaryClient;
@XmlAttribute
private String hash = SHA1;
// @Autowired
@OneToOne
private Contact contact;
private boolean superUser;
//This user can manage attributes and information hidden from clients
//must be set only via the script,client users should always be false
private boolean internalAdmin;
private transient boolean supplier;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "authority")})
@ForeignKey(name = "fk_role_user", inverseName = "fk_user_role")
@XmlElementWrapper(name = "Roles")
@XmlElement(name = "Role")
private Set<Role> roles = new HashSet<>();
// these events had to be made xmltransient for
// JAXB to covert the user to XML in UIResourceImpl#whoAmI()
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_orderevents")
private Set<OrderEvents> orderEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_costingevents")
private Set<CostingEvents> costingEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_logisticsevents")
private Set<LogisticsEvents> logisticsEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_accountingevents")
private Set<AccountingEvents> accountingEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_supplierevents")
private Set<SupplierEvents> supplierEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_productevents")
private Set<ProductEvents> productEvents = new HashSet<>();
@XmlTransient
@ManyToMany(cascade = CascadeType.ALL, mappedBy = "users")
@ForeignKey(name = "fk_integrated_user", inverseName = "fk_dealeventconfig")
private Set<DealEventConfig> dealEventConfigs = new HashSet<>();
@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@ForeignKey(name = "fk_userinfo")
@XmlElement(name = "UserInfo")
private UserInfo userInfo;
@ManyToOne
@XmlElement(name = "ServiceProvider")
private ServiceProvider serviceProvider;
@ManyToOne
@XmlElement(name = "IntegratedSystem")
private IntegratedSystem integratedSystem;
@XmlAttribute
private boolean enabled;
@XmlAttribute
private boolean accountNonExpired = true;
/* @ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "costingclientconfig_organisationalUnits", joinColumns = {@JoinColumn(name = "costingclientconfig_id", unique = false)},
inverseJoinColumns = {@JoinColumn(name = "organisationalUnit_id", unique = false)})
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)*/
//@Fetch(value = FetchMode.SUBSELECT)
//private Set<OrganisationalUnit> organisationalUnit = new HashSet<OrganisationalUnit>();
@XmlElementWrapper(name = "organisationalUnits")
@XmlElement(name = "OrganisationalUnit")
@NotNull(message = "user must linked to org unit")
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)
@JoinTable(name = "organisationalunit_user", joinColumns = {@JoinColumn(name = "user_id", unique = false)},
inverseJoinColumns = {@JoinColumn(name = "organisationalunit_id", unique = false)})
private Set<OrganisationalUnit> organisationalUnit = new HashSet<>();
@XmlAttribute
private String passwordHint;
@NotNull(message = "Incorrect login attempts is required")
private int incorrectLoginAttempts = 0;
// Integration users shouldn't have their password expire. I think.
private boolean passwordExpires = true;
@XmlAttribute
@Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date lastLoginDate;
@ManyToOne
@ForeignKey(name = "fk_createdby")
private User createdBy;
@ManyToOne
@ForeignKey(name = "fk_lastupdateby")
private User lastUpdateBy;
@XmlTransient
@ElementCollection(fetch = FetchType.LAZY)
@CollectionTable(name = "user_passwordhistories", joinColumns = {@JoinColumn(name = "user_id", unique = false)})
@ForeignKey(name = "fk_user")
@Fetch(value = FetchMode.SELECT)
private List<PasswordHistory> passwordHistory = new ArrayList<>();
@Enumerated(EnumType.STRING)
private UserStyle userStyle;
private static final String SHA1 = "SHA-1";
@Enumerated(value = EnumType.STRING)
private CostingSnapShot costingSnapShot;
private transient Set<OrganisationalUnit> organisationalUnits = new HashSet<>();
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
private Employee employee;
// @XmlTransient
// @OneToMany
// @JsonIgnore
// @JoinTable(name = "user_finalDestinations")
// private Set<FinalDestination> finalDestinations= new HashSet<>();
@XmlTransient
@OneToMany
@JsonIgnore
@JoinTable(name = "user_regions",
joinColumns = @JoinColumn(name = "users_id"),
inverseJoinColumns = @JoinColumn(name = "regions_code", unique = false))
@Fetch(value = FetchMode.SELECT)
private Set<Region> regions= new HashSet<>();
public User(String password, String username, Collection<Role> roles, boolean enabled, boolean accountNonExpired,
String primaryClient, String hash) {
encodePassword(password);
this.username = username;
this.roles = new HashSet<>(roles);
this.enabled = enabled;
this.accountNonExpired = accountNonExpired;
this.primaryClient = primaryClient;
this.hash = hash;
}
@Override
public Collection<Role> getAuthorities() {
return getRoles();
}
public void setAuthorities(Collection<Role> newAuthorities) {
this.setRoles(new HashSet<>(newAuthorities));
}
@Override
public String getPassword() {
return password;
}
@Override
public boolean isEnabled() {
return enabled;
}
@Override
public boolean isAccountNonExpired() {
return accountNonExpired;
}
public boolean setAccountNonExpired(boolean accountNonExpired) {
return this.accountNonExpired = accountNonExpired;
}
@Override
public boolean isAccountNonLocked() {
boolean locked = !enabled || (incorrectLoginAttempts > MAX_INCORRECT_LOGIN_ATTEMPTS);
return !locked;
}
@Override
public boolean isCredentialsNonExpired() {
boolean expired =
passwordExpires && passwordChangedDate != null
&& (DateUtils.getDaysBetween2(passwordChangedDate, new Date()) >= PASSWORD_EXPIRY_DAYS);
return !expired;
}
public User() {
}
/**
* Constructor for creating temp user object just for authentication against the framework.
*
* @param username The Username
* @param clientCode The ClientCode
*/
public User(String username, String clientCode) {
this.username = username;
this.password = username;
this.primaryClient = clientCode;
}
public static User create(String username, String password, String primaryClient, List<String> roleStrings) {
List<Role> roles = new ArrayList<>();
for (String role : roleStrings) {
roles.add(new Role(role));
}
return new User(password, username, roles, true, true, primaryClient, PasswordEncode.SHA_1);
}
public boolean hasAuthority(final Role authority) {
return getRoles().contains(authority);
}
public boolean hasAuthority(final String authority) {
return getRoles().contains(new Role(authority));
}
public boolean hasAuthority(final String... authorities) {
for (String authority : authorities) {
if (getRoles().contains(new Role(authority))) {
return true;
}
}
return false;
}
public int getIncorrectLoginAttempts() {
return incorrectLoginAttempts;
}
public void setIncorrectLoginAttempts(int incorrectLoginAttempts) {
this.incorrectLoginAttempts = incorrectLoginAttempts;
}
public boolean getPasswordExpires() {
return passwordExpires;
}
public void setPasswordExpires(boolean passwordExpires) {
this.passwordExpires = passwordExpires;
}
public List<PasswordHistory> getPasswordHistory() {
return passwordHistory;
}
public void setPasswordHistory(List<PasswordHistory> passwordHistory) {
this.passwordHistory = passwordHistory;
}
public String getPlainCurrentPassword() {
return plainCurrentPassword;
}
public void setPlainCurrentPassword(String plainCurrentPassword) {
this.plainCurrentPassword = plainCurrentPassword;
}
public UserStyle getUserStyle() {
return userStyle;
}
public void setUserStyle(UserStyle userStyle) {
this.userStyle = userStyle;
}
@Embeddable
@Access(AccessType.FIELD)
public static class Password {
private String password;
/**
* Used by Hibernate.
*/
private Password() {
}
public static Password encode(String plaintext) {
return new Password(PasswordEncode.createPasswordHash(SHA1, PasswordEncode.BASE16_ENCODING, "UTF-16", "", plaintext));
}
private Password(String password) {
this.password = password;
}
@Override
public boolean equals(final Object other) {
if (!(other instanceof Password)) {
return false;
}
Password castOther = (Password) other;
return new EqualsBuilder().append(password, castOther.password).isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder().append(password).toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this).append("password", password).toString();
}
}
public UserInfo getUserInfo() {
return userInfo;
}
public void setUserInfo(UserInfo userInfo) {
this.userInfo = userInfo;
}
@Override
public String getName() {
return getUsername();
}
public String getHash() {
return hash;
}
public void setHash(String hash) {
this.hash = hash;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
/**
* Can the user login with the supplied plain text password.
*
* @param plaintext
* @return
*/
public boolean canLogin(String plaintext) {
return this.password.equals(Password.encode(plaintext).password);
}
/**
* Encodes the given plaintext into the password.
*
* @param password
*/
public final void encodePassword(String password) {
// A bit weird, but use null as the case where the user doesn't want to change the password
if (password != null) {
this.password = PasswordEncode.encode(password);
}
}
/**
* Password it encoded. Only to be used when sending to the backend.
* Have moved this logic into encodePassword. Clearer, and will probably
* interfer with hibernate and jaxb less.
*
* @param password
*/
public void setPassword(String password) {
this.password = password;
}
public String getPlainNewPassword() {
return plainNewPassword;
}
public void setPlainNewPassword(String plainNewPassword) {
this.plainNewPassword = plainNewPassword;
}
public String getPasswordHint() {
return passwordHint;
}
public void setPasswordHint(String passwordHint) {
this.passwordHint = passwordHint;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public void setPasswordChangedDate(Date now) {
passwordChangedDate = now;
}
public Date getPasswordChangedDate() {
return passwordChangedDate;
}
/**
* Not using any potentially lazy fields.
*
* @return
*/
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(username)
.append(enabled)
.append(passwordChangedDate)
.append(passwordHint)
.append(primaryClient)
.append(passwordExpires)
.hashCode();
}
/**
* Not using any potentially lazy fields.
*
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof User)) {
return false;
}
User other = (User) obj;
return new EqualsBuilder()
.append(username, other.getUsername())
.append(enabled, other.isEnabled())
.append(passwordChangedDate, other.getPasswordChangedDate())
.append(passwordHint, other.getPasswordHint())
.append(primaryClient, other.getPrimaryClient())
.append(passwordExpires, other.getPasswordExpires())
.isEquals();
}
/**
* If the allowMultipleClients variable is falst then always return the
* first element of the allowedClients. Otherwise return the logged in
* client.
*/
public String getActiveClient() {
return primaryClient;
}
public String getPrimaryClient() {
return primaryClient;
}
public void setPrimaryClient(String primaryClient) {
this.primaryClient = primaryClient;
}
/**
* For debug only. Do not put fields in here that might be cached or lazy loaded.
*
* @return
*/
@Override
public String toString() {
return new ToStringBuilder(this)
.append("username", username)
.append("enabled", enabled)
.append("passwordChangeDate", passwordChangedDate)
.append("passwordHint", passwordHint)
.append("primaryClient", primaryClient)
.append("passwordExpires", passwordExpires)
.toString();
}
public Set<Role> getRoles() {
return roles;
}
public List<Role> getRolesList() {
List<Role> list = new ArrayList<>(roles);
Collections.sort(list);
return list;
}
public void setRoles(Set<Role> roles) {
this.roles = new HashSet<>(roles);
}
public void setRolesList(List<Role> roles) {
this.roles = new HashSet<>(roles);
}
@Override
public int compareTo(User o) {
return getUsername().compareToIgnoreCase(o.getUsername());
}
public Set<OrderEvents> getOrderEvents() {
return orderEvents;
}
public void setOrderEvents(Set<OrderEvents> orderEvents) {
this.orderEvents = orderEvents;
}
public List<OrderEvents> getOrderEventsAsList() {
return new ArrayList<>(getOrderEvents());
}
public void setOrderEventsAsList(List<OrderEvents> orderEvents) {
setOrderEvents(new HashSet<>(orderEvents));
}
public Set<SupplierEvents> getSupplierEvents() {
return supplierEvents;
}
public List<SupplierEvents> getSupplierEventsAsList() {
return new ArrayList<>(getSupplierEvents());
}
public void setSupplierEventsAsList(List<SupplierEvents> supplierEvents) {
setSupplierEvents(new HashSet<>(supplierEvents));
}
public void setSupplierEvents(Set<SupplierEvents> supplierEvents) {
this.supplierEvents = supplierEvents;
}
public Set<ProductEvents> getProductEvents() {
return productEvents;
}
public void setProductEvents(Set<ProductEvents> productEvents) {
this.productEvents = productEvents;
}
public List<ProductEvents> getProductEventsAsList() {
return new ArrayList<>(getProductEvents());
}
public void setProductEventsAsList(List<ProductEvents> productEvents) {
setProductEvents(new HashSet<>(productEvents));
}
public Set<CostingEvents> getCostingEvents() {
return costingEvents;
}
public void setCostingEvents(Set<CostingEvents> costingEvents) {
this.costingEvents = costingEvents;
}
public List<CostingEvents> getCostingEventsAsList() {
return new ArrayList<>(getCostingEvents());
}
public void setCostingEventsAsList(List<CostingEvents> costingEvents) {
setCostingEvents(new HashSet<>(costingEvents));
}
public Set<LogisticsEvents> getLogisticsEvents() {
return logisticsEvents;
}
public void setLogisticsEvents(Set<LogisticsEvents> logisticsEvents) {
this.logisticsEvents = logisticsEvents;
}
public List<LogisticsEvents> getLogisticsEventsAsList() {
return new ArrayList<>(getLogisticsEvents());
}
public void setLogisticsEventsAsList(List<LogisticsEvents> logisticsEvents) {
setLogisticsEvents(new HashSet<>(logisticsEvents));
}
public Set<AccountingEvents> getAccountingEvents() {
return accountingEvents;
}
public void setAccountingEvents(Set<AccountingEvents> accountingEvents) {
this.accountingEvents = accountingEvents;
}
public List<AccountingEvents> getAccountingEventsAsList() {
return new ArrayList<>(getAccountingEvents());
}
public void setAccountingEventsAsList(List<AccountingEvents> costingEvents) {
setAccountingEvents(new HashSet<>(costingEvents));
}
public List<DealEventConfig> getDealEventConfigsAsList() {
return new ArrayList<>(getDealEventConfigs());
}
public Set<OrganisationalUnit> getOrganisationalUnit() {
return organisationalUnit;
}
public void setOrganisationalUnit(Set<OrganisationalUnit> organisationalUnit) {
this.organisationalUnit = organisationalUnit;
}
public ServiceProvider getServiceProvider() {
return serviceProvider;
}
public void setServiceProvider(ServiceProvider serviceProvider) {
this.serviceProvider = serviceProvider;
}
public IntegratedSystem getIntegratedSystem() {
return integratedSystem;
}
public void setIntegratedSystem(IntegratedSystem integratedSystem) {
this.integratedSystem = integratedSystem;
}
public Date getLastLoginDate() {
return lastLoginDate;
}
public void setLastLoginDate(Date lastLoginDate) {
this.lastLoginDate = lastLoginDate;
}
public User getCreatedBy() {
return createdBy;
}
public void setCreatedBy(User createBy) {
this.createdBy = createBy;
}
public User getLastUpdateBy() {
return lastUpdateBy;
}
public void setLastUpdateBy(User lastUpdateBy) {
this.lastUpdateBy = lastUpdateBy;
}
public Set<DealEventConfig> getDealEventConfigs() {
return dealEventConfigs;
}
public void setDealEventConfigs(Set<DealEventConfig> dealEventConfigs) {
this.dealEventConfigs = dealEventConfigs;
}
public CostingSnapShot getCostingSnapShot() {
return costingSnapShot;
}
public void setCostingSnapShot(CostingSnapShot costingSnapShot) {
this.costingSnapShot = costingSnapShot;
}
public Set<OrganisationalUnit> getOrganisationalUnits() {
return organisationalUnits;
}
public void setOrganisationalUnits(Set<OrganisationalUnit> organisationalUnits) {
this.organisationalUnits = organisationalUnits;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
public boolean isSuperUser() {
return superUser;
}
public void setSuperUser(boolean superUser) {
//can only be set by script
}
public boolean isInternalAdmin() {
return internalAdmin;
}
public void _setInternalAdmin(boolean internalAdmin) {
//can only be set by script
//can only for test cases
this.internalAdmin = internalAdmin;
}
public boolean isSupplier() {
return supplier;
}
public void setSupplier(boolean supplier) {
this.supplier = supplier;
}
// public Set<FinalDestination> getFinalDestinations() {
// return finalDestinations;
// }
//
// public void setFinalDestinations(Set<FinalDestination> finalDestinations) {
// this.finalDestinations = finalDestinations;
// }
public Set<Region> getRegions() {
return regions;
}
public void setRegions(Set<Region> regions) {
this.regions = regions;
}
}