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);
}
}