package org.apereo.cas.support.saml.mdui.web.flow;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.authentication.principal.ServiceFactory;
import org.apereo.cas.authentication.principal.WebApplicationService;
import org.apereo.cas.services.RegisteredService;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.services.UnauthorizedServiceException;
import org.apereo.cas.support.saml.mdui.MetadataResolverAdapter;
import org.apereo.cas.support.saml.mdui.MetadataUIUtils;
import org.apereo.cas.support.saml.mdui.SamlMetadataUIInfo;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import javax.servlet.http.HttpServletRequest;
/**
* This is {@link SamlMetadataUIParserAction} that attempts to parse
* the MDUI extension block for a SAML SP from the provided metadata locations.
* The result is put into the flow request context. The entity id parameter is
* specified by default at {@link org.apereo.cas.support.saml.SamlProtocolConstants#PARAMETER_ENTITY_ID}.
* <p>This action is best suited to be invoked when the CAS login page
* is about to render so that the page, once the MDUI info is obtained,
* has a chance to populate the UI with relevant info about the SP.</p>
*
* @author Misagh Moayyed
* @since 4.1.0
*/
public class SamlMetadataUIParserAction extends AbstractAction {
private static final Logger LOGGER = LoggerFactory.getLogger(SamlMetadataUIParserAction.class);
private final String entityIdParameterName;
private final MetadataResolverAdapter metadataAdapter;
private ServicesManager servicesManager;
private ServiceFactory<WebApplicationService> serviceFactory;
/**
* Instantiates a new SAML MDUI parser action.
*
* @param entityIdParameterName the entity id parameter name
* @param metadataAdapter the metadata adapter
* @param serviceFactory the service factory
* @param servicesManager the service manager
*/
public SamlMetadataUIParserAction(final String entityIdParameterName, final MetadataResolverAdapter metadataAdapter,
final ServiceFactory<WebApplicationService> serviceFactory, final ServicesManager servicesManager) {
this.entityIdParameterName = entityIdParameterName;
this.metadataAdapter = metadataAdapter;
this.serviceFactory = serviceFactory;
this.servicesManager = servicesManager;
}
@Override
public Event doExecute(final RequestContext requestContext) throws Exception {
final String entityId = getEntityIdFromRequest(requestContext);
if (StringUtils.isBlank(entityId)) {
LOGGER.debug("No entity id found for parameter [{}]", this.entityIdParameterName);
return success();
}
final RegisteredService registeredService = getRegisteredServiceFromRequest(requestContext, entityId);
verifyRegisteredService(requestContext, registeredService);
loadSamlMetadataIntoRequestContext(requestContext, entityId, registeredService);
return success();
}
/**
* Load saml metadata into request context.
*
* @param requestContext the request context
* @param entityId the entity id
* @param registeredService the registered service
*/
protected void loadSamlMetadataIntoRequestContext(final RequestContext requestContext, final String entityId, final RegisteredService registeredService) {
final SamlMetadataUIInfo mdui = MetadataUIUtils.locateMetadataUserInterfaceForEntityId(this.metadataAdapter, entityId, registeredService);
WebUtils.putServiceUserInterfaceMetadata(requestContext, mdui);
}
/**
* Verify registered service.
*
* @param requestContext the request context
* @param registeredService the registered service
*/
protected void verifyRegisteredService(final RequestContext requestContext, final RegisteredService registeredService) {
if (registeredService == null || !registeredService.getAccessStrategy().isServiceAccessAllowed()) {
LOGGER.debug("Service [{}] is not recognized/allowed by the CAS service registry", registeredService);
if (registeredService != null) {
WebUtils.putUnauthorizedRedirectUrlIntoFlowScope(requestContext, registeredService.getAccessStrategy().getUnauthorizedRedirectUrl());
}
throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, StringUtils.EMPTY);
}
}
/**
* Gets registered service from request.
*
* @param requestContext the request context
* @param entityId the entity id
* @return the registered service from request
*/
protected RegisteredService getRegisteredServiceFromRequest(final RequestContext requestContext, final String entityId) {
final Service currentService = WebUtils.getService(requestContext);
final WebApplicationService service = this.serviceFactory.createService(entityId);
RegisteredService registeredService = this.servicesManager.findServiceBy(service);
if (registeredService == null) {
LOGGER.debug("Entity id [{}] not found in the registry. Fallback onto [{}]", entityId, currentService);
registeredService = this.servicesManager.findServiceBy(currentService);
}
LOGGER.debug("Located service definition [{}]", registeredService);
return registeredService;
}
/**
* Gets entity id from request.
*
* @param requestContext the request context
* @return the entity id from request
*/
protected String getEntityIdFromRequest(final RequestContext requestContext) {
final HttpServletRequest request = WebUtils.getHttpServletRequest(requestContext);
return request.getParameter(this.entityIdParameterName);
}
}