package org.apereo.cas.ticket.support;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apereo.cas.ticket.TicketState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
/**
* ExpirationPolicy that is based on certain number of uses of a ticket or a
* certain time period for a ticket to exist.
*
* @author Scott Battaglia
* @since 3.0.0
*/
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include= JsonTypeInfo.As.PROPERTY)
public class MultiTimeUseOrTimeoutExpirationPolicy extends AbstractCasExpirationPolicy {
private static final long serialVersionUID = -5704993954986738308L;
/**
* The Logger instance for this class. Using a transient instance field for the Logger doesn't work, on object
* deserialization the field is null.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(MultiTimeUseOrTimeoutExpirationPolicy.class);
@JsonProperty("timeToLive")
private long timeToKillInSeconds;
@JsonProperty("numberOfUses")
private int numberOfUses;
/**
* No-arg constructor for serialization support.
*/
private MultiTimeUseOrTimeoutExpirationPolicy() {
this.timeToKillInSeconds = 0;
this.numberOfUses = 0;
}
/**
* Instantiates a new multi time use or timeout expiration policy.
*
* @param numberOfUses the number of uses
* @param timeToKillInSeconds the time to kill in seconds
*/
@JsonCreator
public MultiTimeUseOrTimeoutExpirationPolicy(@JsonProperty("numberOfUses") final int numberOfUses,
@JsonProperty("timeToLive") final long timeToKillInSeconds) {
this.timeToKillInSeconds = timeToKillInSeconds;
this.numberOfUses = numberOfUses;
Assert.isTrue(this.numberOfUses > 0, "numberOfUses must be greater than 0.");
Assert.isTrue(this.timeToKillInSeconds > 0, "timeToKillInSeconds must be greater than 0.");
}
@Override
public boolean isExpired(final TicketState ticketState) {
if (ticketState == null) {
LOGGER.debug("Ticket state is null for [{}]", this.getClass().getSimpleName());
return true;
}
final long countUses = ticketState.getCountOfUses();
if (countUses >= this.numberOfUses) {
LOGGER.debug("Ticket usage count [{}] is greater than or equal to [{}]", countUses, this.numberOfUses);
return true;
}
final ZonedDateTime systemTime = ZonedDateTime.now(ZoneOffset.UTC);
final ZonedDateTime lastTimeUsed = ticketState.getLastTimeUsed();
final ZonedDateTime expirationTime = lastTimeUsed.plus(this.timeToKillInSeconds, ChronoUnit.SECONDS);
if (systemTime.isAfter(expirationTime)) {
LOGGER.debug("Ticket has expired because the difference between current time [{}] "
+ "and ticket time [{}] is greater than or equal to [{}]", systemTime, lastTimeUsed,
this.timeToKillInSeconds);
return true;
}
return false;
}
@Override
public Long getTimeToLive() {
return this.timeToKillInSeconds;
}
@JsonIgnore
@Override
public Long getTimeToIdle() {
return 0L;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
final MultiTimeUseOrTimeoutExpirationPolicy rhs = (MultiTimeUseOrTimeoutExpirationPolicy) obj;
return new EqualsBuilder()
.append(this.timeToKillInSeconds, rhs.timeToKillInSeconds)
.append(this.numberOfUses, rhs.numberOfUses)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(timeToKillInSeconds)
.append(numberOfUses)
.toHashCode();
}
/**
* The Proxy ticket expiration policy.
*/
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include= JsonTypeInfo.As.PROPERTY)
public static class ProxyTicketExpirationPolicy extends MultiTimeUseOrTimeoutExpirationPolicy {
private static final long serialVersionUID = -5814201080268311070L;
private ProxyTicketExpirationPolicy() {
}
/**
* Instantiates a new proxy ticket expiration policy.
*
* @param numberOfUses the number of uses
* @param timeToKillInSeconds the time to kill in seconds
*/
@JsonCreator
public ProxyTicketExpirationPolicy(@JsonProperty("numberOfUses") final int numberOfUses,
@JsonProperty("timeToKillInSeconds") final long timeToKillInSeconds) {
super(numberOfUses, timeToKillInSeconds);
}
}
/**
* The Service ticket expiration policy.
*/
@JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include= JsonTypeInfo.As.PROPERTY)
public static class ServiceTicketExpirationPolicy extends MultiTimeUseOrTimeoutExpirationPolicy {
private static final long serialVersionUID = -5814201080268311070L;
private ServiceTicketExpirationPolicy() {
}
/**
* Instantiates a new Service ticket expiration policy.
*
* @param numberOfUses the number of uses
* @param timeToKillInSeconds the time to kill in seconds
*/
@JsonCreator
public ServiceTicketExpirationPolicy(@JsonProperty("numberOfUses") final int numberOfUses,
@JsonProperty("timeToLive")final long timeToKillInSeconds) {
super(numberOfUses, timeToKillInSeconds);
}
}
}