package org.apereo.cas.web.flow.resolver.impl.mfa; import com.google.common.base.Throwables; import org.apache.commons.lang3.StringUtils; import org.apereo.cas.CentralAuthenticationService; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; import org.apereo.cas.authentication.AuthenticationSystemSupport; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.services.MultifactorAuthenticationProvider; import org.apereo.cas.services.MultifactorAuthenticationProviderSelector; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.RegisteredServiceMultifactorPolicy; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.ticket.registry.TicketRegistrySupport; import org.apereo.cas.web.flow.authentication.BaseMultifactorAuthenticationProviderEventResolver; import org.apereo.cas.web.support.WebUtils; import org.apereo.inspektr.audit.annotation.Audit; 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.Collection; import java.util.Collections; import java.util.Set; /** * This is {@link RegisteredServiceMultifactorAuthenticationPolicyEventResolver} * that attempts to resolve the next event based on the authentication providers of this service. * * @author Misagh Moayyed * @since 5.0.0 */ public class RegisteredServiceMultifactorAuthenticationPolicyEventResolver extends BaseMultifactorAuthenticationProviderEventResolver { private static final Logger LOGGER = LoggerFactory.getLogger(RegisteredServiceMultifactorAuthenticationPolicyEventResolver.class); public RegisteredServiceMultifactorAuthenticationPolicyEventResolver(final AuthenticationSystemSupport authenticationSystemSupport, final CentralAuthenticationService centralAuthenticationService, final ServicesManager servicesManager, final TicketRegistrySupport ticketRegistrySupport, final CookieGenerator warnCookieGenerator, final AuthenticationServiceSelectionPlan authSelectionStrategies, final MultifactorAuthenticationProviderSelector selector) { super(authenticationSystemSupport, centralAuthenticationService, servicesManager, ticketRegistrySupport, warnCookieGenerator, authSelectionStrategies, selector); } @Override public Set<Event> resolveInternal(final RequestContext context) { final RegisteredService service = resolveRegisteredServiceInRequestContext(context); final Authentication authentication = WebUtils.getAuthentication(context); if (service == null || authentication == null) { LOGGER.debug("No service or authentication is available to determine event for principal"); return null; } final RegisteredServiceMultifactorPolicy policy = service.getMultifactorPolicy(); if (policy == null || policy.getMultifactorAuthenticationProviders().isEmpty()) { LOGGER.debug("Authentication policy does not contain any multifactor authentication providers"); return null; } if (StringUtils.isNotBlank(policy.getPrincipalAttributeNameTrigger()) || StringUtils.isNotBlank(policy.getPrincipalAttributeValueToMatch())) { LOGGER.debug("Authentication policy for [{}] has defined principal attribute triggers. Skipping...", service.getServiceId()); return null; } return resolveEventPerAuthenticationProvider(authentication.getPrincipal(), context, service); } /** * Resolve event per authentication provider event. * * @param principal the principal * @param context the context * @param service the service * @return the event */ protected Set<Event> resolveEventPerAuthenticationProvider(final Principal principal, final RequestContext context, final RegisteredService service) { try { final Collection<MultifactorAuthenticationProvider> providers = flattenProviders(getAuthenticationProviderForService(service)); if (providers != null && !providers.isEmpty()) { final MultifactorAuthenticationProvider provider = this.multifactorAuthenticationProviderSelector.resolve(providers, service, principal); LOGGER.debug("Selected multifactor authentication provider for this transaction is [{}]", provider); if (!provider.isAvailable(service)) { LOGGER.warn("Multifactor authentication provider [{}] could not be verified/reached.", provider); return null; } final String identifier = provider.getId(); LOGGER.debug("Attempting to build an event based on the authentication provider [{}] and service [{}]", provider, service.getName()); final Event event = validateEventIdForMatchingTransitionInContext(identifier, context, buildEventAttributeMap(principal, service, provider)); return Collections.singleton(event); } LOGGER.debug("No multifactor authentication providers could be located for [{}]", service); return null; } catch (final Exception e) { throw Throwables.propagate(e); } } @Audit(action = "AUTHENTICATION_EVENT", actionResolverName = "AUTHENTICATION_EVENT_ACTION_RESOLVER", resourceResolverName = "AUTHENTICATION_EVENT_RESOURCE_RESOLVER") @Override public Event resolveSingle(final RequestContext context) { return super.resolveSingle(context); } }