package org.apereo.cas.authentication; 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.ArrayList; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.function.Predicate; /** * Constructs immutable {@link Authentication} objects using the builder pattern. * * @author Marvin S. Addison * @since 4.0.0 */ public class DefaultAuthenticationBuilder implements AuthenticationBuilder { private static final long serialVersionUID = -8504842011648432398L; /** Authenticated principal. */ private Principal principal; /** Credential metadata. */ private List<CredentialMetaData> credentials = new ArrayList<>(); /** Authentication metadata attributes. */ private Map<String, Object> attributes = new LinkedHashMap<>(); /** Map of handler names to authentication successes. */ private Map<String, HandlerResult> successes = new LinkedHashMap<>(); /** Map of handler names to authentication failures. */ private Map<String, Class<? extends Exception>> failures = new LinkedHashMap<>(); /** Authentication date. */ private ZonedDateTime authenticationDate; /** * Creates a new instance using the current date for the authentication date. */ public DefaultAuthenticationBuilder() { this.authenticationDate = ZonedDateTime.now(); } /** * Creates a new instance using the current date for the authentication date and the given * principal for the authenticated principal. * * @param p Authenticated principal. */ public DefaultAuthenticationBuilder(final Principal p) { this(); this.principal = p; } /** * Gets the authentication date. * * @return Authentication date. */ public ZonedDateTime getAuthenticationDate() { return this.authenticationDate; } /** * Sets the authentication date and returns this instance. * * @param d Authentication date. * * @return This builder instance. */ @Override public AuthenticationBuilder setAuthenticationDate(final ZonedDateTime d) { this.authenticationDate = d; return this; } /** * Gets the authenticated principal. * * @return Principal. */ @Override public Principal getPrincipal() { return this.principal; } @Override public AuthenticationBuilder addCredentials(final List<CredentialMetaData> credentials) { this.credentials.addAll(credentials); return this; } /** * Sets the principal returns this instance. * * @param p Authenticated principal. * * @return This builder instance. */ @Override public AuthenticationBuilder setPrincipal(final Principal p) { this.principal = p; return this; } /** * Gets the list of credentials that were attempted to be authenticated. * * @return Non-null list of credentials that attempted authentication. */ public List<CredentialMetaData> getCredentials() { return this.credentials; } /** * Sets the list of metadata about credentials presented for authentication. * * @param credentials Non-null list of credential metadata. * * @return This builder instance. */ public AuthenticationBuilder setCredentials(final List<CredentialMetaData> credentials) { Assert.notNull(credentials, "Credential cannot be null"); this.credentials.clear(); this.credentials.addAll(credentials); return this; } /** * Adds metadata about a credential presented for authentication. * * @param credential Credential metadata. * * @return This builder instance. */ @Override public AuthenticationBuilder addCredential(final CredentialMetaData credential) { this.credentials.add(credential); return this; } /** * Gets the authentication attribute map. * * @return Non-null authentication attribute map. */ public Map<String, Object> getAttributes() { return this.attributes; } /** * Sets the authentication metadata attributes. * * @param attributes Non-null map of authentication metadata attributes. * * @return This builder instance. */ @Override public AuthenticationBuilder setAttributes(final Map<String, Object> attributes) { this.attributes.clear(); this.attributes.putAll(attributes); return this; } @Override public AuthenticationBuilder mergeAttribute(final String key, final Object value) { final Object currentValue = this.attributes.get(key); if (currentValue == null) { return addAttribute(key, value); } final Collection collection = CollectionUtils.toCollection(currentValue); collection.addAll(CollectionUtils.toCollection(value)); return addAttribute(key, collection); } @Override public boolean hasAttribute(final String name, final Predicate<Object> predicate) { if (this.attributes.containsKey(name)) { final Object value = this.attributes.get(name); final Collection valueCol = CollectionUtils.toCollection(value); return valueCol.stream().filter(predicate).count() > 0; } return false; } /** * Adds an authentication metadata attribute key-value pair. * * @param key Authentication attribute key. * @param value Authentication attribute value. * * @return This builder instance. */ @Override public AuthenticationBuilder addAttribute(final String key, final Object value) { this.attributes.put(key, value); return this; } /** * Gets the authentication success map. * * @return Non-null map of handler names to successful handler authentication results. */ @Override public Map<String, HandlerResult> getSuccesses() { return this.successes; } /** * Sets the authentication handler success map. * * @param successes Non-null map of handler names to successful handler authentication results. * * @return This builder instance. */ @Override public AuthenticationBuilder setSuccesses(final Map<String, HandlerResult> successes) { Assert.notNull(successes, "Successes cannot be null"); this.successes.clear(); return addSuccesses(successes); } @Override public AuthenticationBuilder addSuccesses(final Map<String, HandlerResult> successes) { successes.entrySet().stream().forEach(entry -> addSuccess(entry.getKey(), entry.getValue())); return this; } /** * Adds an authentication success to the map of handler names to successful authentication handler results. * * @param key Authentication handler name. * @param value Successful authentication handler result produced by handler of given name. * * @return This builder instance. */ @Override public AuthenticationBuilder addSuccess(final String key, final HandlerResult value) { this.successes.put(key, value); return this; } /** * Gets the authentication failure map. * * @return Non-null authentication failure map. */ @Override public Map<String, Class<? extends Exception>> getFailures() { return this.failures; } /** * Sets the authentication handler failure map. * * @param failures Non-null map of handler name to authentication failures. * * @return This builder instance. */ @Override public AuthenticationBuilder setFailures(final Map<String, Class<? extends Exception>> failures) { Assert.notNull(failures, "Failures cannot be null"); this.failures.clear(); return addFailures(failures); } @Override public AuthenticationBuilder addFailures(final Map<String, Class<? extends Exception>> failures) { failures.entrySet().stream().forEach(entry -> addFailure(entry.getKey(), entry.getValue())); return this; } /** * Adds an authentication failure to the map of handler names to the authentication handler failures. * * @param key Authentication handler name. * @param value Exception raised on handler failure to authenticate credential. * * @return This builder instance. */ @Override public AuthenticationBuilder addFailure(final String key, final Class<? extends Exception> value) { this.failures.put(key, value); return this; } /** * Creates an immutable authentication instance from builder data. * * @return Immutable authentication. */ @Override public Authentication build() { return new DefaultAuthentication( this.authenticationDate, this.credentials, this.principal, this.attributes, this.successes, this.failures); } /** * Creates a new builder initialized with data from the given authentication source. * * @param source Authentication source. * * @return New builder instance initialized with all fields in the given authentication source. */ public static AuthenticationBuilder newInstance(final Authentication source) { final DefaultAuthenticationBuilder builder = new DefaultAuthenticationBuilder(source.getPrincipal()); builder.setAuthenticationDate(source.getAuthenticationDate()); builder.setCredentials(source.getCredentials()); builder.setSuccesses(source.getSuccesses()); builder.setFailures(source.getFailures()); builder.setAttributes(source.getAttributes()); return builder; } /** * Creates a new builder. * * @return New builder instance */ public static AuthenticationBuilder newInstance() { return new DefaultAuthenticationBuilder(); } }