package org.apereo.cas.oidc.web.flow;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.Authentication;
import org.apereo.cas.authentication.AuthenticationException;
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.support.oauth.OAuth20Constants;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import org.apereo.cas.web.flow.authentication.BaseMultifactorAuthenticationProviderEventResolver;
import org.apereo.cas.web.support.WebUtils;
import org.jasig.cas.client.util.URIBuilder;
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.Optional;
import java.util.Set;
/**
* This is {@link OidcAuthenticationContextWebflowEventEventResolver}.
*
* @author Misagh Moayyed
* @since 5.0.0
*/
public class OidcAuthenticationContextWebflowEventEventResolver extends BaseMultifactorAuthenticationProviderEventResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(OidcAuthenticationContextWebflowEventEventResolver.class);
public OidcAuthenticationContextWebflowEventEventResolver(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 RegisteredService service = resolveRegisteredServiceInRequestContext(context);
final Authentication authentication = WebUtils.getAuthentication(context);
final HttpServletRequest request = WebUtils.getHttpServletRequest(context);
if (service == null || authentication == null) {
LOGGER.debug("No service or authentication is available to determine event for principal");
return null;
}
String acr = request.getParameter(OAuth20Constants.ACR_VALUES);
if (StringUtils.isBlank(acr)) {
final URIBuilder builderContext = new URIBuilder(StringUtils.trimToEmpty(context.getFlowExecutionUrl()));
final Optional<URIBuilder.BasicNameValuePair> parameter = builderContext.getQueryParams()
.stream().filter(p -> p.getName().equals(OAuth20Constants.ACR_VALUES))
.findFirst();
if (parameter.isPresent()) {
acr = parameter.get().getValue();
}
}
if (StringUtils.isBlank(acr)) {
LOGGER.debug("No ACR provided in the authentication request");
return null;
}
final Set<String> values = org.springframework.util.StringUtils.commaDelimitedListToSet(acr);
if (values.isEmpty()) {
LOGGER.debug("No ACR provided in the authentication request");
return null;
}
final Map<String, MultifactorAuthenticationProvider> providerMap =
WebUtils.getAvailableMultifactorAuthenticationProviders(this.applicationContext);
if (providerMap == null || providerMap.isEmpty()) {
LOGGER.error("No multifactor authentication providers are available in the application context to handle [{}]", values);
throw new AuthenticationException();
}
final Collection<MultifactorAuthenticationProvider> flattenedProviders = flattenProviders(providerMap.values());
final Optional<MultifactorAuthenticationProvider> provider = flattenedProviders
.stream()
.filter(v -> values.contains(v.getId())).findAny();
if (provider.isPresent()) {
return Collections.singleton(new Event(this, provider.get().getId()));
}
LOGGER.warn("The requested authentication class [{}] cannot be satisfied by any of the MFA providers available", values);
throw new AuthenticationException();
}
}