/*
* See LICENSE for licensing and NOTICE for copyright.
*/
package net.shibboleth.idp.cas.flow;
import java.net.URI;
import java.net.URISyntaxException;
import javax.annotation.Nonnull;
import net.shibboleth.idp.cas.authn.Authenticator;
import net.shibboleth.idp.cas.authn.ProxyIdentifiers;
import net.shibboleth.idp.cas.config.ProxyGrantingTicketConfiguration;
import net.shibboleth.idp.cas.protocol.ProtocolError;
import net.shibboleth.idp.cas.protocol.ProtocolParam;
import net.shibboleth.idp.cas.protocol.TicketValidationRequest;
import net.shibboleth.idp.cas.protocol.TicketValidationResponse;
import net.shibboleth.idp.cas.ticket.ProxyTicket;
import net.shibboleth.idp.cas.ticket.ServiceTicket;
import net.shibboleth.idp.cas.ticket.Ticket;
import net.shibboleth.idp.cas.ticket.TicketContext;
import net.shibboleth.idp.cas.ticket.TicketService;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.utilities.java.support.logic.Constraint;
import org.apache.http.client.utils.URIBuilder;
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;
/**
* Validates the proxy callback URL provided in the service ticket validation request and creates a PGT when
* the proxy callback is successfully authenticated. Possible outcomes:
*
* <ul>
* <li>{@link Events#Success success}</li>
* <li>{@link Events#Failure failure}</li>
* </ul>
*
* On success, the PGTIOU is placed in the {@link TicketValidationResponse#getPgtIou()}.
*
* @author Marvin S. Addison
*/
public class ValidateProxyCallbackAction
extends AbstractProfileAction<TicketValidationRequest, TicketValidationResponse> {
/** Class logger. */
private final Logger log = LoggerFactory.getLogger(ValidateProxyCallbackAction.class);
/** Proxy configuration container. */
@Nonnull private final ProxyGrantingTicketConfiguration configuration;
/** Validates the proxy callback endpoint. */
@Nonnull private final Authenticator<URI, Void> proxyAuthenticator;
/** Manages CAS tickets. */
@Nonnull private final TicketService ticketService;
/**
* Creates a new instance.
*
* @param configuration Proxy granting ticket configuration.
* @param proxyAuthenticator Component that validates the proxy callback endpoint.
* @param ticketService Ticket service component.
*/
public ValidateProxyCallbackAction(
@Nonnull ProxyGrantingTicketConfiguration configuration,
@Nonnull Authenticator<URI, Void> proxyAuthenticator,
@Nonnull TicketService ticketService) {
this.configuration = Constraint.isNotNull(configuration, "ProxyGrantingTicketConfiguration cannot be null");
this.proxyAuthenticator = Constraint.isNotNull(proxyAuthenticator, "ProxyAuthenticator cannot be null");
this.ticketService = Constraint.isNotNull(ticketService, "TicketService cannot be null");
}
@Nonnull
@Override
protected Event doExecute(
final @Nonnull RequestContext springRequestContext,
final @Nonnull ProfileRequestContext profileRequestContext) {
final TicketValidationRequest request = FlowStateSupport.getTicketValidationRequest(springRequestContext);
if (request == null) {
log.info("TicketValidationRequest not found in flow state.");
return ProtocolError.IllegalState.event(this);
}
final TicketValidationResponse response =
FlowStateSupport.getTicketValidationResponse(springRequestContext);
if (response == null) {
log.info("TicketValidationResponse not found in flow state.");
return ProtocolError.IllegalState.event(this);
}
final TicketContext ticketContext = profileRequestContext.getSubcontext(TicketContext.class);
if (ticketContext == null) {
log.info("TicketContext not found in profile request context.");
return ProtocolError.IllegalState.event(this);
}
final Ticket ticket = ticketContext.getTicket();
final ProxyIdentifiers proxyIds = new ProxyIdentifiers(
configuration.getSecurityConfiguration().getIdGenerator().generateIdentifier(),
configuration.getPGTIOUGenerator().generateIdentifier());
final URI proxyCallbackUri;
try {
proxyCallbackUri = new URIBuilder(request.getPgtUrl())
.addParameter(ProtocolParam.PgtId.id(), proxyIds.getPgtId())
.addParameter(ProtocolParam.PgtIou.id(), proxyIds.getPgtIou())
.build();
} catch (URISyntaxException e) {
throw new RuntimeException("Error creating proxy callback URL", e);
}
try {
log.debug("Attempting proxy authentication to {}", proxyCallbackUri);
proxyAuthenticator.authenticate(proxyCallbackUri);
if (ticket instanceof ServiceTicket) {
ticketService.createProxyGrantingTicket((ServiceTicket) ticket, proxyIds.getPgtId());
} else {
ticketService.createProxyGrantingTicket((ProxyTicket) ticket, proxyIds.getPgtId());
}
response.setPgtIou(proxyIds.getPgtIou());
} catch (Exception e) {
log.info("Proxy authentication failed for " + request.getPgtUrl() + ": " + e);
return Events.Failure.event(this);
}
return Events.Success.event(this);
}
}