package org.apereo.cas.authentication; import com.fasterxml.jackson.annotation.JsonTypeInfo; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.util.CollectionUtils; import org.springframework.util.Assert; import java.time.ZonedDateTime; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Immutable authentication event whose attributes may not change after creation. * This class is designed for serialization and is suitable for long-term storage. * * @author Dmitriy Kopylenko * @author Scott Battaglia * @author Marvin S. Addison * @since 3.0.0 */ @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY) public class DefaultAuthentication implements Authentication { private static final long serialVersionUID = 3206127526058061391L; /** * Authentication date stamp. */ private ZonedDateTime authenticationDate; /** * List of metadata about credentials presented at authentication. */ private List<CredentialMetaData> credentials; /** * Authenticated principal. */ private Principal principal; /** * Authentication metadata attributes. */ private Map<String, Object> attributes = new ConcurrentHashMap<>(); /** * Map of handler name to handler authentication success event. */ private Map<String, HandlerResult> successes; /** * Map of handler name to handler authentication failure cause. */ private Map<String, Class<? extends Exception>> failures; /** * No-arg constructor for serialization support. */ private DefaultAuthentication() { this.authenticationDate = null; this.credentials = null; this.principal = null; this.attributes = null; this.successes = null; this.failures = null; } /** * Creates a new instance with the given data. * * @param date Non-null authentication date. * @param principal Non-null authenticated principal. * @param attributes Nullable map of authentication metadata. * @param successes Non-null map of authentication successes containing at least one entry. */ public DefaultAuthentication( final ZonedDateTime date, final Principal principal, final Map<String, Object> attributes, final Map<String, HandlerResult> successes) { Assert.notNull(date, "Date cannot be null"); Assert.notNull(principal, "Principal cannot be null"); Assert.notNull(successes, "Successes cannot be null"); Assert.notEmpty(successes, "Successes cannot be empty"); this.authenticationDate = date; this.principal = principal; this.attributes = attributes; this.successes = successes; this.credentials = null; this.failures = null; } /** * Creates a new instance with the given data. * * @param date Non-null authentication date. * @param credentials Non-null list of credential metadata containing at least one entry. * @param principal Non-null authenticated principal. * @param attributes Nullable map of authentication metadata. * @param successes Non-null map of authentication successes containing at least one entry. * @param failures Nullable map of authentication failures. */ public DefaultAuthentication( final ZonedDateTime date, final List<CredentialMetaData> credentials, final Principal principal, final Map<String, Object> attributes, final Map<String, HandlerResult> successes, final Map<String, Class<? extends Exception>> failures) { this(date, principal, attributes, successes); Assert.notNull(credentials, "Credential cannot be null"); Assert.notEmpty(credentials, "Credential cannot be empty"); this.credentials = credentials; this.failures = failures.isEmpty() ? Collections.emptyMap() : failures; } @Override public Principal getPrincipal() { return this.principal; } @Override public ZonedDateTime getAuthenticationDate() { return this.authenticationDate; } @Override public Map<String, Object> getAttributes() { return CollectionUtils.wrap(this.attributes); } @Override public List<CredentialMetaData> getCredentials() { return Collections.unmodifiableList(this.credentials); } @Override public Map<String, HandlerResult> getSuccesses() { return new HashMap<>(this.successes); } @Override public Map<String, Class<? extends Exception>> getFailures() { return CollectionUtils.wrap(this.failures); } @Override public int hashCode() { final HashCodeBuilder builder = new HashCodeBuilder(97, 31); builder.append(this.principal); builder.append(this.authenticationDate); builder.append(this.attributes); builder.append(this.credentials); builder.append(this.successes); builder.append(this.failures); return builder.toHashCode(); } @Override public boolean equals(final Object obj) { if (!(obj instanceof Authentication)) { return false; } if (obj == this) { return true; } final Authentication other = (Authentication) obj; final EqualsBuilder builder = new EqualsBuilder(); builder.append(this.principal, other.getPrincipal()); builder.append(this.credentials, other.getCredentials()); builder.append(this.successes, other.getSuccesses()); builder.append(this.authenticationDate, other.getAuthenticationDate()); builder.append(CollectionUtils.wrap(this.attributes), other.getAttributes()); builder.append(CollectionUtils.wrap(this.failures), other.getFailures()); return builder.isEquals(); } @Override public void update(final Authentication authn) { this.attributes.putAll(authn.getAttributes()); this.authenticationDate = authn.getAuthenticationDate(); } @Override public void updateAll(final Authentication authn) { this.attributes.clear(); update(authn); } }