All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jasig.cas.web.AbstractServiceValidateController Maven / Gradle / Ivy

package org.jasig.cas.web;

import org.jasig.cas.CasProtocolConstants;
import org.jasig.cas.CentralAuthenticationService;
import org.jasig.cas.authentication.AuthenticationContext;
import org.jasig.cas.authentication.AuthenticationContextBuilder;
import org.jasig.cas.authentication.AuthenticationSystemSupport;
import org.jasig.cas.authentication.AuthenticationException;
import org.jasig.cas.authentication.AuthenticationTransaction;
import org.jasig.cas.authentication.Credential;
import org.jasig.cas.authentication.DefaultAuthenticationContextBuilder;
import org.jasig.cas.authentication.DefaultAuthenticationSystemSupport;
import org.jasig.cas.authentication.HttpBasedServiceCredential;
import org.jasig.cas.authentication.principal.Service;
import org.jasig.cas.authentication.principal.WebApplicationService;
import org.jasig.cas.services.RegisteredService;
import org.jasig.cas.services.ServicesManager;
import org.jasig.cas.services.UnauthorizedProxyingException;
import org.jasig.cas.services.UnauthorizedServiceException;
import org.jasig.cas.CasViewConstants;
import org.jasig.cas.ticket.AbstractTicketException;
import org.jasig.cas.ticket.AbstractTicketValidationException;
import org.jasig.cas.ticket.ServiceTicket;
import org.jasig.cas.ticket.TicketGrantingTicket;
import org.jasig.cas.ticket.proxy.ProxyHandler;
import org.jasig.cas.validation.Assertion;
import org.jasig.cas.validation.Cas20ProtocolValidationSpecification;
import org.jasig.cas.validation.ValidationResponseType;
import org.jasig.cas.validation.ValidationSpecification;
import org.jasig.cas.web.support.ArgumentExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.net.URL;
import java.util.Collections;
import java.util.Map;

/**
 * Process the /validate , /serviceValidate , and /proxyValidate URL requests.
 * 

* Obtain the Service Ticket and Service information and present them to the CAS * validation services. Receive back an Assertion containing the user Principal * and (possibly) a chain of Proxy Principals. Store the Assertion in the Model * and chain to a View to generate the appropriate response (CAS 1, CAS 2 XML, * SAML, ...). * * @author Scott Battaglia * @author Misagh Moayyed * @since 3.0.0 */ @Component("serviceValidateController") public abstract class AbstractServiceValidateController extends AbstractDelegateController { /** View if Service Ticket Validation Fails. */ public static final String DEFAULT_SERVICE_FAILURE_VIEW_NAME = "cas2ServiceFailureView"; /** View if Service Ticket Validation Succeeds. */ public static final String DEFAULT_SERVICE_SUCCESS_VIEW_NAME = "cas2ServiceSuccessView"; /** JSON View if Service Ticket Validation Succeeds and if service requires JSON. */ public static final String DEFAULT_SERVICE_VIEW_NAME_JSON = "cas3ServiceJsonView"; @Autowired private ApplicationContext context; @NotNull @Autowired(required=false) @Qualifier("defaultAuthenticationSystemSupport") private AuthenticationSystemSupport authenticationSystemSupport = new DefaultAuthenticationSystemSupport(); /** Implementation of Service Manager. */ @NotNull @Autowired @Qualifier("servicesManager") private ServicesManager servicesManager; /** The CORE which we will delegate all requests to. */ @NotNull @Autowired @Qualifier("centralAuthenticationService") private CentralAuthenticationService centralAuthenticationService; /** The validation protocol we want to use. */ @NotNull private Class validationSpecificationClass = Cas20ProtocolValidationSpecification.class; /** The proxy handler we want to use with the controller. */ @NotNull private ProxyHandler proxyHandler; /** The view to redirect to on a successful validation. */ @NotNull private String successView = DEFAULT_SERVICE_SUCCESS_VIEW_NAME; /** The view to redirect to on a validation failure. */ @NotNull private String failureView = DEFAULT_SERVICE_FAILURE_VIEW_NAME; /** Extracts parameters from Request object. */ @NotNull @Autowired @Qualifier("defaultArgumentExtractor") private ArgumentExtractor argumentExtractor; /** * Instantiates a new Service validate controller. */ public AbstractServiceValidateController() {} /** * Overrideable method to determine which credentials to use to grant a * proxy granting ticket. Default is to use the pgtUrl. * * @param service the webapp service requesting proxy * @param request the HttpServletRequest object. * @return the credentials or null if there was an error or no credentials * provided. */ protected Credential getServiceCredentialsFromRequest(final WebApplicationService service, final HttpServletRequest request) { final String pgtUrl = request.getParameter(CasProtocolConstants.PARAMETER_PROXY_CALLBACK_URL); if (StringUtils.hasText(pgtUrl)) { try { final RegisteredService registeredService = this.servicesManager.findServiceBy(service); verifyRegisteredServiceProperties(registeredService, service); return new HttpBasedServiceCredential(new URL(pgtUrl), registeredService); } catch (final Exception e) { logger.error("Error constructing pgtUrl", e); } } return null; } /** * Inits the binder with the required fields. {@code renew} is required. * * @param request the request * @param binder the binder */ protected void initBinder(final HttpServletRequest request, final ServletRequestDataBinder binder) { binder.setRequiredFields("renew"); } /** * Handle proxy granting ticket delivery. * * @param serviceTicketId the service ticket id * @param credential the service credential * @return the ticket granting ticket */ private TicketGrantingTicket handleProxyGrantingTicketDelivery(final String serviceTicketId, final Credential credential) { TicketGrantingTicket proxyGrantingTicketId = null; try { final ServiceTicket serviceTicket = this.centralAuthenticationService.getTicket(serviceTicketId, ServiceTicket.class); final AuthenticationContextBuilder builder = new DefaultAuthenticationContextBuilder( this.authenticationSystemSupport.getPrincipalElectionStrategy()); final AuthenticationTransaction transaction = AuthenticationTransaction.wrap(credential); this.authenticationSystemSupport.getAuthenticationTransactionManager() .handle(transaction, builder); final AuthenticationContext authenticationContext = builder.build(serviceTicket.getService()); proxyGrantingTicketId = this.centralAuthenticationService.createProxyGrantingTicket(serviceTicketId, authenticationContext); logger.debug("Generated proxy-granting ticket [{}] off of service ticket [{}] and credential [{}]", proxyGrantingTicketId.getId(), serviceTicketId, credential); } catch (final AuthenticationException e) { logger.warn("Failed to authenticate service credential {}", credential); } catch (final AbstractTicketException e) { logger.error("Failed to create proxy granting ticket for {}", credential, e); } return proxyGrantingTicketId; } @Override protected ModelAndView handleRequestInternal(final HttpServletRequest request, final HttpServletResponse response) throws Exception { final WebApplicationService service = this.argumentExtractor.extractService(request); final String serviceTicketId = service != null ? service.getArtifactId() : null; if (service == null || serviceTicketId == null) { logger.debug("Could not identify service and/or service ticket for service: [{}]", service); return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, CasProtocolConstants.ERROR_CODE_INVALID_REQUEST, null, request, service); } try { TicketGrantingTicket proxyGrantingTicketId = null; final Credential serviceCredential = getServiceCredentialsFromRequest(service, request); if (serviceCredential != null) { proxyGrantingTicketId = handleProxyGrantingTicketDelivery(serviceTicketId, serviceCredential); if (proxyGrantingTicketId == null) { return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK, CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK, new Object[]{serviceCredential.getId()}, request, service); } } final Assertion assertion = this.centralAuthenticationService.validateServiceTicket(serviceTicketId, service); if (!validateAssertion(request, serviceTicketId, assertion)) { return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_TICKET, CasProtocolConstants.ERROR_CODE_INVALID_TICKET, null, request, service); } String proxyIou = null; if (serviceCredential != null && this.proxyHandler.canHandle(serviceCredential)) { proxyIou = this.proxyHandler.handle(serviceCredential, proxyGrantingTicketId); if (StringUtils.isEmpty(proxyIou)) { return generateErrorView(CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK, CasProtocolConstants.ERROR_CODE_INVALID_PROXY_CALLBACK, new Object[] {serviceCredential.getId()}, request, service); } } onSuccessfulValidation(serviceTicketId, assertion); logger.debug("Successfully validated service ticket {} for service [{}]", serviceTicketId, service.getId()); return generateSuccessView(assertion, proxyIou, service, proxyGrantingTicketId); } catch (final AbstractTicketValidationException e) { final String code = e.getCode(); return generateErrorView(code, code, new Object[] {serviceTicketId, e.getOriginalService().getId(), service.getId()}, request, service); } catch (final AbstractTicketException te) { return generateErrorView(te.getCode(), te.getCode(), new Object[] {serviceTicketId}, request, service); } catch (final UnauthorizedProxyingException e) { return generateErrorView(e.getMessage(), e.getMessage(), new Object[] {service.getId()}, request, service); } catch (final UnauthorizedServiceException e) { return generateErrorView(e.getMessage(), e.getMessage(), null, request, service); } } /** * Validate assertion. * * @param request the request * @param serviceTicketId the service ticket id * @param assertion the assertion * @return the boolean */ private boolean validateAssertion(final HttpServletRequest request, final String serviceTicketId, final Assertion assertion) { final ValidationSpecification validationSpecification = this.getCommandClass(); final ServletRequestDataBinder binder = new ServletRequestDataBinder(validationSpecification, "validationSpecification"); initBinder(request, binder); binder.bind(request); if (!validationSpecification.isSatisfiedBy(assertion)) { logger.debug("Service ticket [{}] does not satisfy validation specification.", serviceTicketId); return false; } return true; } /** * Triggered on successful validation events. Extensions are to * use this as hook to plug in behvior. * * @param serviceTicketId the service ticket id * @param assertion the assertion */ protected void onSuccessfulValidation(final String serviceTicketId, final Assertion assertion) { // template method with nothing to do. } /** * Generate error view, set to {@link #setFailureView(String)}. * * @param code the code * @param description the description * @param args the args * @param request the request * @return the model and view */ private ModelAndView generateErrorView(final String code, final String description, final Object[] args, final HttpServletRequest request, final WebApplicationService service) { final ModelAndView modelAndView = getModelAndView(false, service); final String convertedDescription = this.context.getMessage(description, args, description, request.getLocale()); modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ERROR_CODE, code); modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ERROR_DESCRIPTION, convertedDescription); return modelAndView; } private ModelAndView getModelAndView(final boolean isSuccess, final WebApplicationService service) { if (service != null){ if (service.getFormat() == ValidationResponseType.JSON) { return new ModelAndView(DEFAULT_SERVICE_VIEW_NAME_JSON); } } return new ModelAndView(isSuccess ? this.successView : this.failureView); } /** * Generate the success view. The result will contain the assertion and the proxy iou. * * @param assertion the assertion * @param proxyIou the proxy iou * @param service the validated service * @param proxyGrantingTicket the proxy granting ticket * @return the model and view, pointed to the view name set by */ private ModelAndView generateSuccessView(final Assertion assertion, final String proxyIou, final WebApplicationService service, final TicketGrantingTicket proxyGrantingTicket) { final ModelAndView modelAndView = getModelAndView(true, service); modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_ASSERTION, assertion); modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_SERVICE, service); modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET_IOU, proxyIou); if (proxyGrantingTicket != null) { modelAndView.addObject(CasViewConstants.MODEL_ATTRIBUTE_NAME_PROXY_GRANTING_TICKET, proxyGrantingTicket.getId()); } final Map augmentedModelObjects = augmentSuccessViewModelObjects(assertion); if (augmentedModelObjects != null) { modelAndView.addAllObjects(augmentedModelObjects); } return modelAndView; } /** * Augment success view model objects. Provides * a way for extension of this controller to dynamically * populate the model object with attributes * that describe a custom nature of the validation protocol. * * @param assertion the assertion * @return map of objects each keyed to a name */ protected Map augmentSuccessViewModelObjects(final Assertion assertion) { return Collections.emptyMap(); } /** * Gets the command class based on {@link #setValidationSpecificationClass(Class)}. * * @return the command class */ private ValidationSpecification getCommandClass() { try { return (ValidationSpecification) this.validationSpecificationClass.newInstance(); } catch (final Exception e) { throw new RuntimeException(e); } } @Override public boolean canHandle(final HttpServletRequest request, final HttpServletResponse response) { return true; } /** * @param centralAuthenticationService The centralAuthenticationService to * set. */ public final void setCentralAuthenticationService(final CentralAuthenticationService centralAuthenticationService) { this.centralAuthenticationService = centralAuthenticationService; } public final void setArgumentExtractor(final ArgumentExtractor argumentExtractor) { this.argumentExtractor = argumentExtractor; } /** * @param validationSpecificationClass The authenticationSpecificationClass * to set. */ public void setValidationSpecificationClass(final Class validationSpecificationClass) { this.validationSpecificationClass = validationSpecificationClass; } /** * @param failureView The failureView to set. */ public void setFailureView(final String failureView) { this.failureView = failureView; } /** * @param successView The successView to set. */ public void setSuccessView(final String successView) { this.successView = successView; } /** * @param proxyHandler The proxyHandler to set. */ public void setProxyHandler(final ProxyHandler proxyHandler) { this.proxyHandler = proxyHandler; } /** * Sets the services manager. * * @param servicesManager the new services manager */ public final void setServicesManager(final ServicesManager servicesManager) { this.servicesManager = servicesManager; } /** * Ensure that the service is found and enabled in the service registry. * @param registeredService the located entry in the registry * @param service authenticating service * @throws UnauthorizedServiceException */ private void verifyRegisteredServiceProperties(final RegisteredService registeredService, final Service service) { if (registeredService == null) { final String msg = String.format("ServiceManagement: Unauthorized Service Access. " + "Service [%s] is not found in service registry.", service.getId()); logger.warn(msg); throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg); } if (!registeredService.getAccessStrategy().isServiceAccessAllowed()) { final String msg = String.format("ServiceManagement: Unauthorized Service Access. " + "Service [%s] is not enabled in service registry.", service.getId()); logger.warn(msg); throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, msg); } } public void setAuthenticationSystemSupport(final AuthenticationSystemSupport authenticationSystemSupport) { this.authenticationSystemSupport = authenticationSystemSupport; } public void setApplicationContext(final ApplicationContext context) { this.context = context; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy