package org.apereo.cas.web.flow.resolver.impl;
import org.apache.commons.lang3.tuple.Pair;
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.services.MultifactorAuthenticationProvider;
import org.apereo.cas.services.MultifactorAuthenticationProviderSelector;
import org.apereo.cas.services.RegisteredService;
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.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 javax.servlet.http.HttpServletRequest;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
/**
* This is {@link SelectiveAuthenticationProviderWebflowEventEventResolver}
* that acts as a stub resolver, specifically designed for extensions.
* Deployers can extend this class to perform additional processes on the final set
* of resolved events, to select one vs another based on the nature of the event attributes.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class SelectiveAuthenticationProviderWebflowEventEventResolver extends BaseMultifactorAuthenticationProviderEventResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(SelectiveAuthenticationProviderWebflowEventEventResolver.class);
public SelectiveAuthenticationProviderWebflowEventEventResolver(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) {
final Set<Event> resolvedEvents = getResolvedEventsAsAttribute(context);
final Authentication authentication = WebUtils.getAuthentication(context);
final RegisteredService registeredService = resolveRegisteredServiceInRequestContext(context);
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
return resolveEventsInternal(resolvedEvents, authentication, registeredService, request, context);
}
/**
* Resolve events internal set. Implementation may filter events from the collection
* to only return the one that is appropriate for this request. The default
* implementation returns the entire collection.
*
* @param resolveEvents the resolve events
* @param authentication the authentication
* @param registeredService the registered service
* @param request the request
* @param context the request context
* @return the set of resolved events
*/
protected Set<Event> resolveEventsInternal(final Set<Event> resolveEvents, final Authentication authentication, final RegisteredService registeredService,
final HttpServletRequest request, final RequestContext context) {
LOGGER.debug("Collection of resolved events for this authentication sequence are:");
resolveEvents.forEach(e -> LOGGER.debug("Event id [{}] resolved from [{}]", e.getId(), e.getSource().getClass().getName()));
final Pair<Set<Event>, Collection<MultifactorAuthenticationProvider>> pair =
filterEventsByMultifactorAuthenticationProvider(resolveEvents, authentication, registeredService);
WebUtils.putResolvedMultifactorAuthenticationProviders(context, pair.getValue());
return pair.getKey();
}
/**
* Filter events by multifactor authentication providers.
*
* @param resolveEvents the resolve events
* @param authentication the authentication
* @param registeredService the registered service
* @return the set of events
*/
protected Pair<Set<Event>, Collection<MultifactorAuthenticationProvider>> filterEventsByMultifactorAuthenticationProvider(
final Set<Event> resolveEvents, final Authentication authentication, final RegisteredService registeredService) {
LOGGER.debug("Locating multifactor providers to determine support for this authentication sequence");
final Map<String, MultifactorAuthenticationProvider> providers =
WebUtils.getAvailableMultifactorAuthenticationProviders(applicationContext);
if (providers == null || providers.isEmpty()) {
LOGGER.debug("No providers are available to honor this request. Moving on...");
return Pair.of(resolveEvents, Collections.emptySet());
}
final Collection<MultifactorAuthenticationProvider> flattenedProviders = flattenProviders(providers.values());
// remove providers that don't support the event
flattenedProviders.removeIf(p -> resolveEvents.stream().filter(e -> p.supports(e, authentication, registeredService)).count() == 0);
// remove events that are not supported by providers.
resolveEvents.removeIf(e -> flattenedProviders.stream().filter(p -> p.supports(e, authentication, registeredService)).count() == 0);
LOGGER.debug("Finalized set of resolved events are [{}]", resolveEvents);
return Pair.of(resolveEvents, flattenedProviders);
}
}