package org.apereo.cas.web.flow.resolver.impl.mfa; import com.google.common.base.Throwables; import groovy.lang.GroovyClassLoader; import org.apache.commons.io.IOUtils; import org.apereo.cas.CentralAuthenticationService; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; import org.apereo.cas.authentication.AuthenticationSystemSupport; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.configuration.CasConfigurationProperties; 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.util.ResourceUtils; import org.codehaus.groovy.control.CompilerConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.io.Resource; import org.springframework.web.util.CookieGenerator; import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; import java.lang.reflect.Constructor; import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.Set; import java.util.function.Predicate; /** * This is {@link PredicatedPrincipalAttributeMultifactorAuthenticationPolicyEventResolver}. * * @author Misagh Moayyed * @since 5.1.0 */ public class PredicatedPrincipalAttributeMultifactorAuthenticationPolicyEventResolver extends PrincipalAttributeMultifactorAuthenticationPolicyEventResolver { private static final Logger LOGGER = LoggerFactory.getLogger(PredicatedPrincipalAttributeMultifactorAuthenticationPolicyEventResolver.class); private final Resource predicateResource; public PredicatedPrincipalAttributeMultifactorAuthenticationPolicyEventResolver(final AuthenticationSystemSupport authenticationSystemSupport, final CentralAuthenticationService centralAuthenticationService, final ServicesManager servicesManager, final TicketRegistrySupport ticketRegistrySupport, final CookieGenerator warnCookieGenerator, final AuthenticationServiceSelectionPlan authSelectionStrategies, final MultifactorAuthenticationProviderSelector selector, final CasConfigurationProperties casProperties) { super(authenticationSystemSupport, centralAuthenticationService, servicesManager, ticketRegistrySupport, warnCookieGenerator, authSelectionStrategies, selector, casProperties); predicateResource = casProperties.getAuthn().getMfa().getGlobalPrincipalAttributePredicate(); } @Override protected Set<Event> resolveMultifactorProviderViaPredicate(final RequestContext context, final RegisteredService service, final Principal principal, final Collection<MultifactorAuthenticationProvider> providers) { try { if (predicateResource == null || !ResourceUtils.doesResourceExist(predicateResource)) { LOGGER.debug("No groovy script predicate is defined to decide which multifactor authentication provider should be chosen"); return null; } final String script = IOUtils.toString(predicateResource.getInputStream(), StandardCharsets.UTF_8); final GroovyClassLoader classLoader = new GroovyClassLoader(getClass().getClassLoader(), new CompilerConfiguration(), true); final Class<Predicate> predicateClass = classLoader.parseClass(script); final Object[] args = {service, principal, providers, LOGGER}; final Class[] types = {Object.class, Object.class, Object.class, Object.class}; final Constructor<Predicate> ctor = predicateClass.getDeclaredConstructor(types); final Predicate<MultifactorAuthenticationProvider> predicate = ctor.newInstance(args); return resolveEventViaPrincipalAttribute(principal, attributeNames, service, context, providers, input -> providers.stream() .filter(predicate) .findFirst() .isPresent()); } catch (final Exception e) { throw Throwables.propagate(e); } } }