/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a
* copy of the License at the following location:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jasig.cas.authentication;
import java.io.Serializable;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.jasig.cas.authentication.principal.Principal;
import org.springframework.util.Assert;
/**
* 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
*/
public final class ImmutableAuthentication implements Authentication, Serializable {
/** UID for serializing. */
private static final long serialVersionUID = 3206127526058061391L;
/** Authentication date stamp. */
private final long authenticatedDate;
/** List of metadata about credentials presented at authentication. */
private final List<CredentialMetaData> credentials;
/** Authenticated principal. */
private final Principal principal;
/** Authentication metadata attributes. */
private final Map<String, Object> attributes;
/** Map of handler name to handler authentication success event. */
private final Map<String, HandlerResult> successes;
/** Map of handler name to handler authentication failure cause. */
private final Map<String, Class<? extends Exception>> failures;
/** No-arg constructor for serialization support. */
private ImmutableAuthentication() {
this.authenticatedDate = 0;
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 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 ImmutableAuthentication(
final Date 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) {
Assert.notNull(date, "Date cannot be null");
Assert.notNull(credentials, "Credential cannot be null");
Assert.notNull(principal, "Principal cannot be null");
Assert.notNull(successes, "Successes cannot be null");
Assert.notEmpty(credentials, "Credential cannot be empty");
Assert.notEmpty(successes, "Successes cannot be empty");
this.authenticatedDate = date.getTime();
this.credentials = credentials;
this.principal = principal;
this.attributes = attributes.isEmpty() ? null : attributes;
this.successes = successes;
this.failures = failures.isEmpty() ? null : failures;
}
@Override
public Principal getPrincipal() {
return this.principal;
}
public Date getAuthenticatedDate() {
return new ImmutableDate(this.authenticatedDate);
}
@Override
public Map<String, Object> getAttributes() {
return wrap(this.attributes);
}
@Override
public List<CredentialMetaData> getCredentials() {
return Collections.unmodifiableList(this.credentials);
}
@Override
public Map<String, HandlerResult> getSuccesses() {
return Collections.unmodifiableMap(this.successes);
}
@Override
public Map<String, Class<? extends Exception>> getFailures() {
return wrap(this.failures);
}
@Override
public int hashCode() {
final HashCodeBuilder builder = new HashCodeBuilder(97, 31);
builder.append(this.principal);
builder.append(this.authenticatedDate);
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.authenticatedDate, other.getAuthenticatedDate().getTime());
builder.append(wrap(this.attributes), other.getAttributes());
builder.append(wrap(this.failures), other.getFailures());
return builder.isEquals();
}
/**
* Wraps a possibly null map in an immutable wrapper.
*
* @param source Nullable map to wrap.
*
* @return {@link Collections#unmodifiableMap(java.util.Map)} if given map is not null, otherwise
* {@link java.util.Collections#emptyMap()}.
*/
private static <K, V> Map<K, V> wrap(final Map<K, V> source) {
if (source != null) {
return Collections.unmodifiableMap(source);
}
return Collections.emptyMap();
}
/**
* Immutable date implementation that throws {@link UnsupportedOperationException} for setter methods.
*/
private static final class ImmutableDate extends Date {
/** No-arg constructor for serialization support. */
private ImmutableDate() {}
/**
* Creates a new instance with the given epoch time in milliseconds.
*
* @param instant Milliseconds since the Unix epoch.
*/
public ImmutableDate(final long instant) {
super(instant);
}
@Override
public void setYear(final int year) {
throw new UnsupportedOperationException();
}
@Override
public void setDate(final int date) {
throw new UnsupportedOperationException();
}
@Override
public void setHours(final int hours) {
throw new UnsupportedOperationException();
}
@Override
public void setMinutes(final int minutes) {
throw new UnsupportedOperationException();
}
@Override
public void setSeconds(final int seconds) {
throw new UnsupportedOperationException();
}
@Override
public void setTime(final long time) {
throw new UnsupportedOperationException();
}
}
}