/*
* 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.ticket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import org.jasig.cas.authentication.Authentication;
import org.jasig.cas.authentication.principal.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
/**
* Concrete implementation of a TicketGrantingTicket. A TicketGrantingTicket is
* the global identifier of a principal into the system. It grants the Principal
* single-sign on access to any service that opts into single-sign on.
* Expiration of a TicketGrantingTicket is controlled by the ExpirationPolicy
* specified as object creation.
*
* @author Scott Battaglia
* @since 3.0
*/
@Entity
@Table(name="TICKETGRANTINGTICKET")
public final class TicketGrantingTicketImpl extends AbstractTicket implements TicketGrantingTicket {
/** Unique Id for serialization. */
private static final long serialVersionUID = -8608149809180911599L;
/** Logger instance. */
private static final Logger LOGGER = LoggerFactory.getLogger(TicketGrantingTicketImpl.class);
/** The authenticated object for which this ticket was generated for. */
@Lob
@Column(name="AUTHENTICATION", nullable=false)
private Authentication authentication;
/** Flag to enforce manual expiration. */
@Column(name="EXPIRED", nullable=false)
private Boolean expired = false;
/** The services associated to this ticket. */
@Lob
@Column(name="SERVICES_GRANTED_ACCESS_TO", nullable=false)
private final HashMap<String, Service> services = new HashMap<String, Service>();
@Lob
@Column(name="SUPPLEMENTAL_AUTHENTICATIONS", nullable=false)
private final ArrayList<Authentication> supplementalAuthentications = new ArrayList<Authentication>();
public TicketGrantingTicketImpl() {
// nothing to do
}
/**
* Constructs a new TicketGrantingTicket.
* May throw an {@link IllegalArgumentException} if the Authentication object is null.
*
* @param id the id of the Ticket
* @param ticketGrantingTicket the parent ticket
* @param authentication the Authentication request for this ticket
* @param policy the expiration policy for this ticket.
*/
public TicketGrantingTicketImpl(final String id,
final TicketGrantingTicket ticketGrantingTicket,
final Authentication authentication, final ExpirationPolicy policy) {
super(id, ticketGrantingTicket, policy);
Assert.notNull(authentication, "authentication cannot be null");
this.authentication = authentication;
}
/**
* Constructs a new TicketGrantingTicket without a parent
* TicketGrantingTicket.
*
* @param id the id of the Ticket
* @param authentication the Authentication request for this ticket
* @param policy the expiration policy for this ticket.
*/
public TicketGrantingTicketImpl(final String id,
final Authentication authentication, final ExpirationPolicy policy) {
this(id, null, authentication, policy);
}
/**
* {@inheritDoc}
*/
@Override
public Authentication getAuthentication() {
return this.authentication;
}
/**
* {@inheritDoc}
* <p>The state of the ticket is affected by this operation and the
* ticket will be considered used. The state update subsequently may
* impact the ticket expiration policy in that, depending on the policy
* configuration, the ticket may be considered expired.
*/
@Override
public synchronized ServiceTicket grantServiceTicket(final String id,
final Service service, final ExpirationPolicy expirationPolicy,
final boolean credentialsProvided) {
final ServiceTicket serviceTicket = new ServiceTicketImpl(id, this,
service, this.getCountOfUses() == 0 || credentialsProvided,
expirationPolicy);
updateState();
final List<Authentication> authentications = getChainedAuthentications();
service.setPrincipal(authentications.get(authentications.size()-1).getPrincipal());
this.services.put(id, service);
return serviceTicket;
}
/**
* Gets an immutable map of service ticket and services accessed by this ticket-granting ticket.
*
* @return an immutable map of service ticket and services accessed by this ticket-granting ticket.
*/
@Override
public synchronized Map<String, Service> getServices() {
final Map<String, Service> map = new HashMap<String, Service>(services.size());
for (final String ticket : services.keySet()) {
map.put(ticket, services.get(ticket));
}
return Collections.unmodifiableMap(map);
}
/**
* Remove all services of the TGT (at logout).
*/
@Override
public void removeAllServices() {
services.clear();
}
/**
* Return if the TGT has no parent.
*
* @return if the TGT has no parent.
*/
@Override
public boolean isRoot() {
return this.getGrantingTicket() == null;
}
/** {@inheritDoc} */
@Override
public void markTicketExpired() {
this.expired = true;
}
/** {@inheritDoc} */
@Override
public TicketGrantingTicket getRoot() {
TicketGrantingTicket current = this;
TicketGrantingTicket parent = current.getGrantingTicket();
while (parent != null) {
current = parent;
parent = current.getGrantingTicket();
}
return current;
}
/**
* Return if the TGT is expired.
*
* @return if the TGT is expired.
*/
@Override
public boolean isExpiredInternal() {
return this.expired;
}
/** {@inheritDoc} */
@Override
public List<Authentication> getSupplementalAuthentications() {
return this.supplementalAuthentications;
}
/** {@inheritDoc} */
@Override
public List<Authentication> getChainedAuthentications() {
final List<Authentication> list = new ArrayList<Authentication>();
list.add(getAuthentication());
if (getGrantingTicket() == null) {
return Collections.unmodifiableList(list);
}
list.addAll(getGrantingTicket().getChainedAuthentications());
return Collections.unmodifiableList(list);
}
/** {@inheritDoc} */
@Override
public boolean equals(final Object object) {
if (object == null
|| !(object instanceof TicketGrantingTicket)) {
return false;
}
final Ticket ticket = (Ticket) object;
return ticket.getId().equals(this.getId());
}
}