package org.apereo.cas.web.flow.resolver.impl;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CasProtocolConstants;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.AuthenticationException;
import org.apereo.cas.authentication.AuthenticationResult;
import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan;
import org.apereo.cas.authentication.AuthenticationSystemSupport;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.services.MultifactorAuthenticationProviderSelector;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.ticket.AbstractTicketException;
import org.apereo.cas.ticket.ServiceTicket;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import org.apereo.cas.web.flow.CasWebflowConstants;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.CookieGenerator;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import java.util.Collections;
import java.util.Set;
/**
* This is {@link ServiceTicketRequestWebflowEventResolver}
* that creates the next event responding to requests that are service-ticket requests.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class ServiceTicketRequestWebflowEventResolver extends AbstractCasWebflowEventResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceTicketRequestWebflowEventResolver.class);
public ServiceTicketRequestWebflowEventResolver(final AuthenticationSystemSupport authenticationSystemSupport,
final CentralAuthenticationService centralAuthenticationService, final ServicesManager servicesManager,
final TicketRegistrySupport ticketRegistrySupport, final CookieGenerator warnCookieGenerator,
final AuthenticationServiceSelectionPlan authenticationSelectionStrategies,
final MultifactorAuthenticationProviderSelector selector) {
super(authenticationSystemSupport, centralAuthenticationService, servicesManager, ticketRegistrySupport, warnCookieGenerator,
authenticationSelectionStrategies, selector);
}
@Override
public Set<Event> resolveInternal(final RequestContext context) {
if (isRequestAskingForServiceTicket(context)) {
LOGGER.debug("Authentication request is asking for service tickets");
return Collections.singleton(grantServiceTicket(context));
}
return null;
}
/**
* Is request asking for service ticket?
*
* @param context the context
* @return true, if both service and tgt are found, and the request is not asking to renew.
* @since 4.1.0
*/
protected boolean isRequestAskingForServiceTicket(final RequestContext context) {
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
LOGGER.debug("Located ticket-granting ticket [{}] from the request context", ticketGrantingTicketId);
final Service service = WebUtils.getService(context);
LOGGER.debug("Located service [{}] from the request context", service);
final String renewParam = context.getRequestParameters().get(CasProtocolConstants.PARAMETER_RENEW);
LOGGER.debug("Provided value for [{}] request parameter is [{}]", CasProtocolConstants.PARAMETER_RENEW, renewParam);
if (StringUtils.isNotBlank(renewParam) && StringUtils.isNotBlank(ticketGrantingTicketId) && service != null) {
LOGGER.debug("Request identifies itself as one asking for service tickets. Checking for authentication context validity...");
final boolean validAuthn = ticketRegistrySupport.getAuthenticationFrom(ticketGrantingTicketId) != null;
if (validAuthn) {
LOGGER.debug("Existing authentication context linked to ticket-granting ticket [{}] is valid. "
+ "CAS should begin to issue service tickets for [{}] once credentials are renewed", ticketGrantingTicketId, service);
return false;
}
LOGGER.debug("Existing authentication context linked to ticket-granting ticket [{}] is NOT valid. "
+ "CAS will not issue service tickets for [{}] just yet without renewing the authentication context",
ticketGrantingTicketId, service);
return false;
}
LOGGER.debug("Request is not eligible to be issued service tickets just yet");
return false;
}
/**
* Grant service ticket for the given credential based on the service and tgt
* that are found in the request context.
*
* @param context the context
* @return the resulting event. Warning, authentication failure or error.
* @since 4.1.0
*/
protected Event grantServiceTicket(final RequestContext context) {
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
final Credential credential = getCredentialFromContext(context);
try {
final Service service = WebUtils.getService(context);
final AuthenticationResult authenticationResult =
this.authenticationSystemSupport.handleAndFinalizeSingleAuthenticationTransaction(service, credential);
final ServiceTicket serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, authenticationResult);
WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
WebUtils.putWarnCookieIfRequestParameterPresent(this.warnCookieGenerator, context);
return newEvent(CasWebflowConstants.TRANSITION_ID_WARN);
} catch (final AuthenticationException | AbstractTicketException e) {
return newEvent(CasWebflowConstants.TRANSITION_ID_AUTHENTICATION_FAILURE, e);
}
}
}