package org.apereo.cas.support.wsfederation.authentication.principal;
import org.apereo.cas.authentication.Credential;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;
/**
* This class represents the basic elements of the WsFederation token.
*
* @author John Gasper
* @since 4.2.0
*/
public class WsFederationCredential implements Credential {
private static final long serialVersionUID = -824605020472810939L;
private static final Logger LOGGER = LoggerFactory.getLogger(WsFederationCredential.class);
private String audience;
private String authenticationMethod;
private String id;
private String issuer;
private ZonedDateTime issuedOn;
private ZonedDateTime notBefore;
private ZonedDateTime notOnOrAfter;
private ZonedDateTime retrievedOn;
private Map<String, List<Object>> attributes;
public String getAuthenticationMethod() {
return this.authenticationMethod;
}
public void setAuthenticationMethod(final String authenticationMethod) {
this.authenticationMethod = authenticationMethod;
}
public String getAudience() {
return this.audience;
}
public void setAudience(final String audience) {
this.audience = audience;
}
public Map<String, List<Object>> getAttributes() {
return this.attributes;
}
public void setAttributes(final Map<String, List<Object>> attributes) {
this.attributes = attributes;
}
@Override
public String getId() {
return this.id;
}
public void setId(final String id) {
this.id = id;
}
public ZonedDateTime getIssuedOn() {
return this.issuedOn;
}
public void setIssuedOn(final ZonedDateTime issuedOn) {
this.issuedOn = issuedOn;
}
public String getIssuer() {
return this.issuer;
}
public void setIssuer(final String issuer) {
this.issuer = issuer;
}
public ZonedDateTime getNotBefore() {
return this.notBefore;
}
public void setNotBefore(final ZonedDateTime notBefore) {
this.notBefore = notBefore;
}
public ZonedDateTime getNotOnOrAfter() {
return this.notOnOrAfter;
}
public void setNotOnOrAfter(final ZonedDateTime notOnOrAfter) {
this.notOnOrAfter = notOnOrAfter;
}
public ZonedDateTime getRetrievedOn() {
return this.retrievedOn;
}
public void setRetrievedOn(final ZonedDateTime retrievedOn) {
this.retrievedOn = retrievedOn;
}
/**
* toString produces a human readable representation of the WsFederationCredential.
*
* @return a human readable representation of the WsFederationCredential
*/
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.NO_CLASS_NAME_STYLE)
.append("ID", this.id)
.append("Issuer", this.issuer)
.append("Audience", this.audience)
.append("Authentication Method", this.authenticationMethod)
.append("Issued On", this.issuedOn)
.append("Valid After", this.notBefore)
.append("Valid Before", this.notOnOrAfter)
.append("Attributes", this.attributes)
.build();
}
/**
* isValid validates the credential.
*
* @param expectedAudience the audience that the token was issued to (CAS Server)
* @param expectedIssuer the issuer of the token (the IdP)
* @param timeDrift the amount of acceptable time drift
* @return true if the credentials are valid, otherwise false
*/
public boolean isValid(final String expectedAudience, final String expectedIssuer, final long timeDrift) {
if (!this.getAudience().equalsIgnoreCase(expectedAudience)) {
LOGGER.warn("Audience is invalid: [{}]", this.getAudience());
return false;
}
if (!this.issuer.equalsIgnoreCase(expectedIssuer)) {
LOGGER.warn("Issuer is invalid: [{}]", this.issuer);
return false;
}
final ZonedDateTime retrievedOnTimeDrift = this.getRetrievedOn().minus(timeDrift, ChronoUnit.MILLIS);
if (this.issuedOn.isBefore(retrievedOnTimeDrift)) {
LOGGER.warn("Ticket is issued before the allowed drift. Issued on [{}] while allowed drift is [{}]",
this.issuedOn, retrievedOnTimeDrift);
return false;
}
final ZonedDateTime retrievedOnTimeAfterDrift = this.retrievedOn.plus(timeDrift, ChronoUnit.MILLIS);
if (this.issuedOn.isAfter(retrievedOnTimeAfterDrift)) {
LOGGER.warn("Ticket is issued after the allowed drift. Issued on [{}] while allowed drift is [{}]",
this.issuedOn, retrievedOnTimeAfterDrift);
return false;
}
if (this.retrievedOn.isAfter(this.notOnOrAfter)) {
LOGGER.warn("Ticket is too late because it's retrieved on [{}] which is after [{}].",
this.retrievedOn, this.notOnOrAfter);
return false;
}
LOGGER.debug("WsFed Credential is validated for [{}] and [{}].", expectedAudience, expectedIssuer);
return true;
}
}