package org.apereo.cas.ticket.support;
import com.fasterxml.jackson.annotation.JsonCreator;
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 javax.annotation.PostConstruct;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
/**
* Provides the Ticket Granting Ticket expiration policy. Ticket Granting Tickets
* can be used any number of times, have a fixed lifetime, and an idle timeout.
*
* @author William G. Thompson, Jr.
* @since 3.4.10
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY)
public class TicketGrantingTicketExpirationPolicy extends AbstractCasExpirationPolicy {
/**
* Serialization support.
*/
private static final long serialVersionUID = 7670537200691354820L;
/**
* 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(TicketGrantingTicketExpirationPolicy.class);
/**
* Maximum time this ticket is valid.
*/
private long maxTimeToLiveInSeconds;
/**
* Time to kill in seconds.
*/
private long timeToKillInSeconds;
public TicketGrantingTicketExpirationPolicy() {
}
/**
* Instantiates a new Ticket granting ticket expiration policy.
*
* @param maxTimeToLive the max time to live
* @param timeToKill the time to kill
*/
@JsonCreator
public TicketGrantingTicketExpirationPolicy(@JsonProperty("timeToLive") final long maxTimeToLive,
@JsonProperty("timeToIdle") final long timeToKill) {
this.maxTimeToLiveInSeconds = maxTimeToLive;
this.timeToKillInSeconds = timeToKill;
}
/**
* After properties set.
*/
@PostConstruct
public void afterPropertiesSet() {
Assert.isTrue(this.maxTimeToLiveInSeconds >= this.timeToKillInSeconds,
"maxTimeToLiveInSeconds must be greater than or equal to timeToKillInSeconds.");
}
@Override
public boolean isExpired(final TicketState ticketState) {
final ZonedDateTime currentSystemTime = ZonedDateTime.now(ZoneOffset.UTC);
final ZonedDateTime creationTime = ticketState.getCreationTime();
final ZonedDateTime lastTimeUsed = ticketState.getLastTimeUsed();
// Ticket has been used, check maxTimeToLive (hard window)
ZonedDateTime expirationTime = creationTime.plus(this.maxTimeToLiveInSeconds, ChronoUnit.SECONDS);
if (currentSystemTime.isAfter(expirationTime)) {
LOGGER.debug("Ticket is expired because the time since creation is greater than maxTimeToLiveInSeconds");
return true;
}
expirationTime = lastTimeUsed.plus(this.timeToKillInSeconds, ChronoUnit.SECONDS);
if (currentSystemTime.isAfter(expirationTime)) {
LOGGER.debug("Ticket is expired because the time since last use is greater than timeToKillInSeconds");
return true;
}
return false;
}
@Override
public Long getTimeToLive() {
return this.maxTimeToLiveInSeconds;
}
@Override
public Long getTimeToIdle() {
return this.timeToKillInSeconds;
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj == this) {
return true;
}
if (obj.getClass() != getClass()) {
return false;
}
final TicketGrantingTicketExpirationPolicy rhs = (TicketGrantingTicketExpirationPolicy) obj;
return new EqualsBuilder()
.append(this.maxTimeToLiveInSeconds, rhs.maxTimeToLiveInSeconds)
.append(this.timeToKillInSeconds, rhs.timeToKillInSeconds)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.append(this.maxTimeToLiveInSeconds)
.append(this.timeToKillInSeconds)
.toHashCode();
}
}