package org.multibit.mbm.core.model;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.joda.time.DateTime;
import org.multibit.mbm.auth.Authority;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Map;
import java.util.Set;
/**
* <p>DTO to provide the following to application:</p>
* <ul>
* <li>Storage of state for the User entity</li>
* </ul>
* <p>This is the main server side User entity, it extends the much simplified client side
* User entity </p>
*
* @since 0.0.1
*
*/
@Entity
@Table(name = "users")
public class User implements Serializable {
private static final long serialVersionUID = 38345280321234L;
/**
* Numerical ID to allow faster indexing (for internal use)
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
protected Long id = null;
/**
* <p>UUID to allow public User reference without
* revealing a sequential ID that could be guessed.
* Typically used as an API key</p>
*/
@Column(name = "api_key", nullable = false)
protected String apiKey = null;
/**
* <p>Used as a shared secret to authenticate this user to the upstream server. Typically
* part of an HMAC authentication scheme.</p>
*/
@Column(name = "secret_key", nullable = true)
protected String secretKey = null;
/**
* <p>A user password (not plaintext and optional for anonymity reasons)</p>
*/
@Column(name = "password", nullable = true)
protected String passwordDigest = null;
/**
* <p>A username (optional for anonymity reasons)</p>
*/
@Column(name = "username", nullable = true)
protected String username = null;
/**
* A shared secret between this client the user's browser that is revoked when the session ends
*/
@Column(name = "session_key", nullable = true)
private String sessionKey;
@OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.EAGER,
orphanRemoval = true
)
@MapKeyEnumerated()
private Map<ContactMethod, ContactMethodDetail> contactMethodMap = Maps.newLinkedHashMap();
/**
* Manages password resets (optional)
*/
@Column(name = "password_reset_at", nullable = true)
private DateTime passwordResetAt = null;
/**
* Tracks when the user was created (optional for anonymity reasons)
*/
@Column(name = "created_at", nullable = true)
private DateTime createdAt = null;
/**
* Indicates if the User has been locked (defaults to unlocked)
*/
@Column(name = "locked", nullable = false)
private boolean locked = false;
/**
* Indicates if the User has been deleted (archived)
*/
@Column(name = "deleted", nullable = false)
private boolean deleted = false;
/**
* Provides a reason for being deleted
*/
@Column(name = "reasonForDelete", nullable = true)
private String reasonForDelete = null;
/**
* Indicates if the User is a staff member (defaults to no)
*/
@Column(name = "staff", nullable = false)
private boolean staffMember = false;
/**
* A User may be linked to a Customer (bi-directional)
*/
@OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Customer customer = null;
/**
* A User may be linked to a Supplier (bi-directional)
*/
@OneToOne(
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Supplier supplier = null;
/**
* @return The internal unique ID
*/
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/**
* @return The public API key
*/
public String getApiKey() {
return apiKey;
}
public void setApiKey(String apiKey) {
this.apiKey = apiKey;
}
/**
* @return The private shared secret for upstream communications
*/
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
}
/**
* @return The user name to authenticate with the client
*/
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
/**
* @return The digested password to authenticate with the client
*/
public String getPasswordDigest() {
return passwordDigest;
}
/**
* <h3>Note that it is expected that Jasypt or similar is used prior to storage</h3>
* @param passwordDigest The password digest
*/
public void setPasswordDigest(String passwordDigest) {
this.passwordDigest = passwordDigest;
}
/**
* @return The session key
*/
public String getSessionKey() {
return sessionKey;
}
public void setSessionKey(String sessionKey) {
this.sessionKey = sessionKey;
}
/**
* This collection provides additional optional fields so can be lazy
*/
@OneToMany(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
orphanRemoval = true
)
@MapKeyEnumerated
private Map<UserField, UserFieldDetail> userFieldMap = Maps.newLinkedHashMap();
@OneToMany(
targetEntity = UserRole.class,
cascade = {CascadeType.ALL},
mappedBy = "primaryKey.user",
fetch = FetchType.EAGER,
orphanRemoval = true
)
private Set<UserRole> userRoles = Sets.newLinkedHashSet();
/**
* Default constructor for Hibernate
*/
public User() {
}
/**
* @return The {@link ContactMethod} map
*/
public Map<ContactMethod, ContactMethodDetail> getContactMethodMap() {
return contactMethodMap;
}
public void setContactMethodMap(Map<ContactMethod, ContactMethodDetail> contactMethodMap) {
this.contactMethodMap = contactMethodMap;
}
/**
* @param contactMethod The contact method (e.g. "EMAIL", "VOIP")
*
* @return The {@link ContactMethodDetail} providing the information, or null if none available
*/
@Transient
public ContactMethodDetail getContactMethodDetail(ContactMethod contactMethod) {
return contactMethodMap.get(contactMethod);
}
/**
* @param contactMethod The contact method (e.g. "EMAIL", "VOIP")
* @param contactMethodDetail The contact method details providing the email address, or VOIP address etc
*/
@Transient
public void setContactMethodDetail(ContactMethod contactMethod, ContactMethodDetail contactMethodDetail) {
contactMethodMap.put(contactMethod, contactMethodDetail);
}
public DateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(DateTime createdAt) {
this.createdAt = createdAt;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Supplier getSupplier() {
return supplier;
}
public void setSupplier(Supplier supplier) {
this.supplier = supplier;
}
public boolean isLocked() {
return locked;
}
public void setLocked(boolean locked) {
this.locked = locked;
}
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
public String getReasonForDelete() {
return reasonForDelete;
}
public void setReasonForDelete(String reasonForDelete) {
this.reasonForDelete = reasonForDelete;
}
public DateTime getPasswordResetAt() {
return passwordResetAt;
}
public void setPasswordResetAt(DateTime passwordResetAt) {
this.passwordResetAt = passwordResetAt;
}
public boolean isStaffMember() {
return staffMember;
}
public void setStaffMember(boolean staffMember) {
this.staffMember = staffMember;
}
public Map<UserField, UserFieldDetail> getUserFieldMap() {
return userFieldMap;
}
public void setUserFieldMap(Map<UserField, UserFieldDetail> userFieldMap) {
this.userFieldMap = userFieldMap;
}
public Set<UserRole> getUserRoles() {
return userRoles;
}
public void setUserRoles(Set<UserRole> userRoles) {
this.userRoles = userRoles;
}
@Override
public String toString() {
return String.format("User[id=%s, apiKey='%s', secretKey='***']]", id, apiKey);
}
/**
* TODO Consider making this cacheable
*
* @param authorities The required authorities
* @return True if the user has all the required authorities
*
*/
public boolean hasAllAuthorities(Authority[] authorities) {
Set<Authority> requiredAuthorities = Sets.newHashSet(authorities);
Set<Authority> grantedAuthorities = Sets.newHashSet();
for (UserRole userRole : userRoles) {
grantedAuthorities.addAll(userRole.getRole().getAuthorities());
}
return grantedAuthorities.containsAll(requiredAuthorities);
}
public boolean hasAuthority(Authority authority) {
return hasAllAuthorities(new Authority[] {authority});
}
}