package org.apereo.cas.config;

import org.apereo.cas.authentication.DefaultMultifactorAuthenticationContextValidator;
import org.apereo.cas.authentication.MultifactorAuthenticationContextValidator;
import org.apereo.cas.authentication.MultifactorAuthenticationProvider;
import org.apereo.cas.authentication.MultifactorAuthenticationUtils;
import org.apereo.cas.authentication.MultifactorTriggerSelectionStrategy;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.RegisteredServiceAccessStrategyUtils;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.validation.RequestedContextValidator;

import lombok.extern.slf4j.Slf4j;
import lombok.val;

import org.apache.commons.lang3.tuple.Pair;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.Optional;

/**
 * This is {@link CasCoreMultifactorAuthenticationConfiguration}.
 *
 * @author Travis Schmidt
 * @since 6.0.0
 */
@Configuration("casCoreMultifactorAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@Slf4j
public class CasCoreMultifactorAuthenticationConfiguration {

    @Autowired
    private CasConfigurationProperties casProperties;

    @Autowired
    private ConfigurableApplicationContext applicationContext;

    @Autowired
    @Qualifier("servicesManager")
    private ServicesManager servicesManager;

    @Autowired
    private MultifactorTriggerSelectionStrategy multifactorTriggerSelectionStrategy;

    @RefreshScope
    @Bean
    @ConditionalOnMissingBean(name = "authenticationContextValidator")
    public MultifactorAuthenticationContextValidator authenticationContextValidator() {
        val mfa = casProperties.getAuthn().getMfa();
        val contextAttribute = mfa.getAuthenticationContextAttribute();
        val failureMode = mfa.getGlobalFailureMode();
        val authnAttributeName = mfa.getTrusted().getAuthenticationContextAttribute();
        return new DefaultMultifactorAuthenticationContextValidator(contextAttribute, failureMode, authnAttributeName, applicationContext);
    }

    @Bean
    public RequestedContextValidator<MultifactorAuthenticationProvider> requestedContextValidator() {
        return (assertion, request) -> {
            LOGGER.debug("Locating the primary authentication associated with this service request [{}]", assertion.getService());
            val service = servicesManager.findServiceBy(assertion.getService());
            RegisteredServiceAccessStrategyUtils.ensureServiceAccessIsAllowed(assertion.getService(), service);
            val providers = MultifactorAuthenticationUtils.getAvailableMultifactorAuthenticationProviders(applicationContext);
            val authentication = assertion.getPrimaryAuthentication();
            val requestedContext = multifactorTriggerSelectionStrategy.resolve(providers.values(), request, service, authentication);

            if (!requestedContext.isPresent()) {
                LOGGER.debug("No particular authentication context is required for this request");
                return Pair.of(Boolean.TRUE, Optional.empty());
            }

            return authenticationContextValidator().validate(authentication, requestedContext.get(), service);
        };
    }

}

@@ -744,24 +744,81 @@
 uple.Pair;%0A%0A
+import org.springframework.beans.factory.ObjectProvider;%0A
 import org.s
@@ -1904,16 +1904,31 @@
 private 
+ObjectProvider%3C
 Services
@@ -1934,16 +1934,17 @@
 sManager
+%3E
  service
@@ -1973,24 +1973,100 @@
 red%0A    
-private 
+@Qualifier(%22defaultMultifactorTriggerSelectionStrategy%22)%0A    private ObjectProvider%3C
 Multifac
@@ -2092,16 +2092,17 @@
 Strategy
+%3E
  multifa
@@ -3058,16 +3058,33 @@
 Manager.
+getIfAvailable().
 findServ
@@ -3495,16 +3495,33 @@
 trategy.
+getIfAvailable().
 resolve(

fix checkstyle issues