package edu.harvard.iq.dataverse.authorization.users;
import edu.harvard.iq.dataverse.DatasetLock;
import edu.harvard.iq.dataverse.ValidateEmail;
import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo;
import edu.harvard.iq.dataverse.authorization.AuthenticatedUserLookup;
import edu.harvard.iq.dataverse.authorization.RoleAssigneeDisplayInfo;
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinAuthenticationProvider;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.List;
import java.util.Objects;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
@NamedQueries({
@NamedQuery( name="AuthenticatedUser.findAll",
query="select au from AuthenticatedUser au"),
@NamedQuery( name="AuthenticatedUser.findSuperUsers",
query="SELECT au FROM AuthenticatedUser au WHERE au.superuser = TRUE"),
@NamedQuery( name="AuthenticatedUser.findByIdentifier",
query="select au from AuthenticatedUser au WHERE au.userIdentifier=:identifier"),
@NamedQuery( name="AuthenticatedUser.findByEmail",
query="select au from AuthenticatedUser au WHERE LOWER(au.email)=LOWER(:email)"),
@NamedQuery( name="AuthenticatedUser.countOfIdentifier",
query="SELECT COUNT(a) FROM AuthenticatedUser a WHERE a.userIdentifier=:identifier"),
@NamedQuery( name="AuthenticatedUser.filter",
query="select au from AuthenticatedUser au WHERE ("
+ "au.userIdentifier like :query OR "
+ "lower(concat(au.firstName,' ',au.lastName)) like lower(:query))"),
@NamedQuery( name="AuthenticatedUser.findAdminUser",
query="select au from AuthenticatedUser au WHERE "
+ "au.superuser = true "
+ "order by au.id")
})
@Entity
public class AuthenticatedUser implements User, Serializable {
public static final String IDENTIFIER_PREFIX = "@";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
/**
* @todo Shouldn't there be some constraints on what the userIdentifier is
* allowed to be? It can't be as restrictive as the "userName" field on
* BuiltinUser because we can't predict what Shibboleth Identity Providers
* (IdPs) will send (typically in the "eppn" SAML assertion) but perhaps
* spaces, for example, should be disallowed. Right now "elisah.da mota" can
* be persisted as a userIdentifier per
* https://github.com/IQSS/dataverse/issues/2945
*/
@NotNull
@Column(nullable = false, unique=true)
private String userIdentifier;
@ValidateEmail(message = "Please enter a valid email address.")
@NotNull
@Column(nullable = false, unique=true)
private String email;
private String affiliation;
private String position;
private String lastName;
private String firstName;
@Column(nullable = true)
private Timestamp emailConfirmed;
private boolean superuser;
/**
* @todo Remove? Check for accuracy? For Solr JOINs we used to care about
* the modification times of users but now we don't index users at all.
*/
private Timestamp modificationTime;
/**
* @todo Consider storing a hash of *all* potentially interesting Shibboleth
* attribute key/value pairs, not just the Identity Provider (IdP).
*/
@Transient
private String shibIdentityProvider;
@Override
public String getIdentifier() {
return IDENTIFIER_PREFIX + userIdentifier;
}
@OneToMany(mappedBy = "user", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST})
private List<DatasetLock> datasetLocks;
public List<DatasetLock> getDatasetLocks() {
return datasetLocks;
}
public void setDatasetLocks(List<DatasetLock> datasetLocks) {
this.datasetLocks = datasetLocks;
}
@Override
public RoleAssigneeDisplayInfo getDisplayInfo() {
return new AuthenticatedUserDisplayInfo(firstName, lastName, email, affiliation, position);
}
/**
* Takes the passed info object and updated the internal fields according to it.
* @param inf the info from which we update the fields.
*/
public void applyDisplayInfo( AuthenticatedUserDisplayInfo inf ) {
setFirstName(inf.getFirstName());
setLastName(inf.getLastName());
setEmail(inf.getEmailAddress());
setAffiliation( inf.getAffiliation() );
setPosition( inf.getPosition());
}
@Override
public boolean isAuthenticated() { return true; }
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUserIdentifier() {
return userIdentifier;
}
public void setUserIdentifier(String userIdentifier) {
this.userIdentifier = userIdentifier;
}
public String getName() {
return firstName + " " + lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getAffiliation() {
return affiliation;
}
public void setAffiliation(String affiliation) {
this.affiliation = affiliation;
}
public String getPosition() {
return position;
}
public void setPosition(String position) {
this.position = position;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public Timestamp getEmailConfirmed() {
return emailConfirmed;
}
public void setEmailConfirmed(Timestamp emailConfirmed) {
this.emailConfirmed = emailConfirmed;
}
@Override
public boolean isSuperuser() {
return superuser;
}
public void setSuperuser(boolean superuser) {
this.superuser = superuser;
}
public void setModificationTime(Timestamp modificationTime) {
this.modificationTime = modificationTime;
}
public boolean isBuiltInUser() {
String authProviderString = authenticatedUserLookup.getAuthenticationProviderId();
if (authProviderString != null && authProviderString.equals(BuiltinAuthenticationProvider.PROVIDER_ID)) {
return true;
}
return false;
}
@OneToOne(mappedBy = "authenticatedUser")
private AuthenticatedUserLookup authenticatedUserLookup;
public AuthenticatedUserLookup getAuthenticatedUserLookup() {
return authenticatedUserLookup;
}
public void setAuthenticatedUserLookup(AuthenticatedUserLookup authenticatedUserLookup) {
this.authenticatedUserLookup = authenticatedUserLookup;
}
@Override
public int hashCode() {
int hash = 0;
hash += (id != null ? id.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object object) {
// TODO: Warning - this method won't work in the case the id fields are not set
if (!(object instanceof AuthenticatedUser)) {
return false;
}
AuthenticatedUser other = (AuthenticatedUser) object;
return Objects.equals(getId(), other.getId());
}
public String getShibIdentityProvider() {
return shibIdentityProvider;
}
public void setShibIdentityProvider(String shibIdentityProvider) {
this.shibIdentityProvider = shibIdentityProvider;
}
@Override
public String toString() {
return "[AuthenticatedUser identifier:" + getIdentifier() + "]";
}
public String getSortByString() {
return this.getLastName() + " " + this.getFirstName() + " " + this.getUserIdentifier();
}
}