package org.apereo.cas.ticket; import com.fasterxml.jackson.annotation.JsonIdentityInfo; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.ObjectIdGenerators; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.springframework.util.Assert; import javax.persistence.Column; import javax.persistence.Id; import javax.persistence.Lob; import javax.persistence.MappedSuperclass; import java.time.ZoneOffset; import java.time.ZonedDateTime; /** * Abstract implementation of a ticket that handles all ticket state for * policies. Also incorporates properties common among all tickets. As this is * an abstract class, it cannot be created. It is recommended that * implementations of the Ticket interface extend the AbstractTicket as it * handles common functionality amongst different ticket types (such as state * updating). * <p> * AbstractTicket does not provide a logger instance to * avoid instantiating many such Loggers at runtime (there will be many instances * of subclasses of AbstractTicket in a typical running CAS server). Instead * subclasses should use static Logger instances. * * @author Scott Battaglia * @since 3.0.0 */ @MappedSuperclass @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) @JsonIgnoreProperties(ignoreUnknown = true) public abstract class AbstractTicket implements Ticket, TicketState { private static final long serialVersionUID = -8506442397878267555L; /** * The {@link ExpirationPolicy} this ticket is associated with. **/ @Lob @Column(name = "EXPIRATION_POLICY", length = Integer.MAX_VALUE, nullable = false) private ExpirationPolicy expirationPolicy; /** * The unique identifier for this ticket. */ @Id @Column(name = "ID", nullable = false) private String id; /** * The last time this ticket was used. */ @Column(name = "LAST_TIME_USED") private ZonedDateTime lastTimeUsed; /** * The previous last time this ticket was used. */ @Column(name = "PREVIOUS_LAST_TIME_USED") private ZonedDateTime previousLastTimeUsed; /** * The time the ticket was created. */ @Column(name = "CREATION_TIME") private ZonedDateTime creationTime; /** * The number of times this was used. */ @Column(name = "NUMBER_OF_TIMES_USED") private int countOfUses; /** * Instantiates a new abstract ticket. */ protected AbstractTicket() { // nothing to do } /** * Constructs a new Ticket with a unique id, a possible parent Ticket (can * be null) and a specified Expiration Policy. * * @param id the unique identifier for the ticket * @param expirationPolicy the expiration policy for the ticket. * @throws IllegalArgumentException if the id or expiration policy is null. */ public AbstractTicket(final String id, final ExpirationPolicy expirationPolicy) { Assert.notNull(expirationPolicy, "expirationPolicy cannot be null"); Assert.notNull(id, "id cannot be null"); this.id = id; this.creationTime = ZonedDateTime.now(ZoneOffset.UTC); this.lastTimeUsed = ZonedDateTime.now(ZoneOffset.UTC); this.expirationPolicy = expirationPolicy; } @Override public String getId() { return this.id; } @Override public void update() { this.previousLastTimeUsed = this.lastTimeUsed; this.lastTimeUsed = ZonedDateTime.now(ZoneOffset.UTC); this.countOfUses++; if (getGrantingTicket() != null && !getGrantingTicket().isExpired()) { final TicketState state = TicketState.class.cast(getGrantingTicket()); state.update(); } } @Override public int getCountOfUses() { return this.countOfUses; } @Override public ZonedDateTime getCreationTime() { return this.creationTime; } @Override public ZonedDateTime getLastTimeUsed() { return this.lastTimeUsed; } @Override public ZonedDateTime getPreviousTimeUsed() { return this.previousLastTimeUsed; } @Override public boolean isExpired() { final TicketGrantingTicket tgt = getGrantingTicket(); return this.expirationPolicy.isExpired(this) || tgt != null && tgt.isExpired() || isExpiredInternal(); } protected boolean isExpiredInternal() { return false; } @Override public int hashCode() { return new HashCodeBuilder(13, 133).append(this.getId()).toHashCode(); } @Override public String toString() { return this.getId(); } @Override public ExpirationPolicy getExpirationPolicy() { return this.expirationPolicy; } @Override public int compareTo(final Ticket o) { return getId().compareTo(o.getId()); } }