/* * See LICENSE for licensing and NOTICE for copyright. */ package net.shibboleth.idp.cas.flow; import net.shibboleth.idp.cas.protocol.ProtocolError; import net.shibboleth.idp.cas.ticket.TicketContext; import net.shibboleth.idp.profile.AbstractProfileAction; import net.shibboleth.idp.session.IdPSession; import net.shibboleth.idp.session.SessionException; import net.shibboleth.idp.session.SessionResolver; import net.shibboleth.idp.session.context.SessionContext; import net.shibboleth.idp.session.criterion.SessionIdCriterion; import net.shibboleth.utilities.java.support.logic.Constraint; import net.shibboleth.utilities.java.support.resolver.CriteriaSet; import net.shibboleth.utilities.java.support.resolver.ResolverException; import org.opensaml.profile.context.ProfileRequestContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; import javax.annotation.Nonnull; /** * IdP session validation for back-channel ticket request and validation. Possible outcomes: * <ul> * <li>{@link ProtocolError#SessionExpired sessionExpired}</li> * <li>{@link ProtocolError#SessionRetrievalError sessionRetrievalError}</li> * <li>{@link Events#Success success}</li> * </ul> * <p> * Requires a {@link net.shibboleth.idp.cas.ticket.TicketContext} bound to the * {@link org.opensaml.profile.context.ProfileRequestContext} that is provided to the action. * <p> * On success, adds the current {@link net.shibboleth.idp.session.IdPSession} * as request scope parameter under the key {@value FlowStateSupport#IDP_SESSION_KEY} and also to a * {@link net.shibboleth.idp.session.context.SessionContext} that is a subcontext of the input * {@link org.opensaml.profile.context.ProfileRequestContext}. * * @author Marvin S. Addison */ public class ValidateIdpSessionAction extends AbstractProfileAction { /** Class logger. */ private final Logger log = LoggerFactory.getLogger(ValidateIdpSessionAction.class); /** Looks up IdP sessions. */ @Nonnull private final SessionResolver sessionResolver; /** * Creates a new instance. * @param resolver IdP session resolver component. */ public ValidateIdpSessionAction(@Nonnull SessionResolver resolver) { this.sessionResolver = Constraint.isNotNull(resolver, "Session resolver cannot be null."); } @Nonnull @Override protected Event doExecute( final @Nonnull RequestContext springRequestContext, final @Nonnull ProfileRequestContext profileRequestContext) { final TicketContext ticketContext = profileRequestContext.getSubcontext(TicketContext.class); if (ticketContext == null) { log.info("TicketContext not found in context tree."); return ProtocolError.IllegalState.event(this); } final String sessionId = ticketContext.getTicket().getSessionId(); final IdPSession session; try { log.debug("Attempting to retrieve session {}", sessionId); session = sessionResolver.resolveSingle(new CriteriaSet(new SessionIdCriterion(sessionId))); } catch (ResolverException e) { log.debug("IdP session retrieval failed with error: {}", e); return ProtocolError.SessionRetrievalError.event(this); } boolean expired = (session == null); if (session != null) { try { expired = !session.checkTimeout(); log.debug("Session {} expired={}", sessionId, expired); } catch (SessionException e) { log.debug("Error performing session timeout check. Assuming session has expired.", e); expired = true; } } if (expired) { return ProtocolError.SessionExpired.event(this); } FlowStateSupport.setIdpSession(springRequestContext, session); final SessionContext sessionContext = new SessionContext(); sessionContext.setIdPSession(session); profileRequestContext.addSubcontext(sessionContext); return Events.Success.event(this); } }