We would like to allow users in a specific ldap group the ability to 
optionally bypass Duo for a given service if the user is not signed up for 
a 2fa account. Essentially there would be these two cases for a user: 

- 2fa always required
- 2fa optionally required (but always required if the user has a Duo 
account)

I have two duo instances defined in the cas.properties file: mfa-duo, 
mfa-duo-force. The first is in bypass mode while the latter doesn't allow 
any bypass.

Then my other classes are thus:
*spring.factories*
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.apereo.
cas.custom.config.SelectiveDuoWebflowEventResolverConfiguration


I then put together a custom trigger that will determine if a user is 
required to use DUO or not:

*SelectiveDuoWebflowEventResolver.java*
package org.apereo.cas.custom.mfa;

import com.google.common.collect.ImmutableSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
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.ServicesManager;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import 
org.apereo.cas.web.flow.resolver.impl.AbstractCasWebflowEventResolver;
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;

public class SelectiveDuoWebflowEventResolver extends 
AbstractCasWebflowEventResolver {

    private static final Logger LOGGER = 
LoggerFactory.getLogger(SelectiveDuoWebflowEventResolver.class);

    public SelectiveDuoWebflowEventResolver(AuthenticationSystemSupport 
authenticationSystemSupport, CentralAuthenticationService 
centralAuthenticationService, ServicesManager servicesManager, 
TicketRegistrySupport ticketRegistrySupport, CookieGenerator 
warnCookieGenerator, AuthenticationServiceSelectionPlan 
authenticationSelectionStrategies, 
MultifactorAuthenticationProviderSelector selector) {
        super(authenticationSystemSupport, centralAuthenticationService, 
servicesManager, ticketRegistrySupport, warnCookieGenerator, 
authenticationSelectionStrategies, selector);
    }

    @Override
    public Set<Event> resolveInternal(RequestContext context) {
        final RegisteredService service = 
WebUtils.getRegisteredService(context);
        final Authentication authentication = 
WebUtils.getAuthentication(context);
        Set<String> attributeKeys = authentication.getAttributes().keySet();
        for (String s : attributeKeys) {
            System.out.println("s: " + s + " " + 
authentication.getAttributes().get(s));
        }
        Principal principal = authentication.getPrincipal();
        attributeKeys = principal.getAttributes().keySet();
        for (String s : attributeKeys) {
            System.out.println("p: " + s + " " + 
principal.getAttributes().get(s));
        }
        if (userRequiresDUO()) {
            LOGGER.warn("Forcing MFA");
            Optional<MultifactorAuthenticationProvider> mfaDuoForced = 
this.getMultifactorAuthenticationProviderFromApplicationContext("mfa-duo-force");
            MultifactorAuthenticationProvider forcedProvider = 
mfaDuoForced.get();
            final Map eventAttributes
                    = buildEventAttributeMap(authentication.getPrincipal(),
                            service,
                            forcedProvider);
            final Event event
                    = 
validateEventIdForMatchingTransitionInContext(forcedProvider.getId(),
                            context, eventAttributes);
            return ImmutableSet.of(event);
        } else {
        LOGGER.warn("Not forcing MFA");
        Optional<MultifactorAuthenticationProvider> mfaDuo = 
this.getMultifactorAuthenticationProviderFromApplicationContext("mfa-duo");
        MultifactorAuthenticationProvider bypassableProvider = mfaDuo.get();
        final Map eventAttributes
                = buildEventAttributeMap(authentication.getPrincipal(),
                        service,
                        bypassableProvider);
        final Event event
                = 
validateEventIdForMatchingTransitionInContext(bypassableProvider.getId(),
                        context, eventAttributes);
        return ImmutableSet.of(event);
        }
    }
}



*SelectiveDuoWebflowEventResolverConfiguration.java*
package org.apereo.cas.custom.config;

import javax.annotation.PostConstruct;
import org.apereo.cas.CentralAuthenticationService;
import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan;
import org.apereo.cas.authentication.AuthenticationSystemSupport;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.custom.mfa.SelectiveDuoWebflowEventResolver;
import org.apereo.cas.services.MultifactorAuthenticationProviderSelector;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.ticket.registry.TicketRegistrySupport;
import 
org.apereo.cas.web.flow.authentication.RankedMultifactorAuthenticationProviderSelector;
import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver;
import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import 
org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.util.CookieGenerator;

@Configuration("selectiveDuoWebflowEventResolverConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class SelectiveDuoWebflowEventResolverConfiguration {

    @Autowired
    @Qualifier("initialAuthenticationAttemptWebflowEventResolver")
    private CasDelegatingWebflowEventResolver initialEventResolver;

    @Autowired
    @Qualifier("centralAuthenticationService")
    private CentralAuthenticationService centralAuthenticationService;

    @Autowired
    @Qualifier("defaultAuthenticationSystemSupport")
    private AuthenticationSystemSupport authenticationSystemSupport;

    @Autowired
    @Qualifier("defaultTicketRegistrySupport")
    private TicketRegistrySupport ticketRegistrySupport;

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

    @Autowired(required = false)
    @Qualifier("multifactorAuthenticationProviderSelector")
    private final MultifactorAuthenticationProviderSelector 
multifactorAuthenticationProviderSelector = new 
RankedMultifactorAuthenticationProviderSelector();

    @Autowired
    @Qualifier("warnCookieGenerator")
    private CookieGenerator warnCookieGenerator;

    @Autowired
    @Qualifier("authenticationServiceSelectionPlan")
    private AuthenticationServiceSelectionPlan 
authenticationRequestServiceSelectionStrategies;

    @RefreshScope
    @Bean
    public CasWebflowEventResolver selectiveDuoWebflowEventResolver() {
        return new 
SelectiveDuoWebflowEventResolver(authenticationSystemSupport,
                centralAuthenticationService,
                servicesManager, ticketRegistrySupport, warnCookieGenerator,
                authenticationRequestServiceSelectionStrategies,
                multifactorAuthenticationProviderSelector);
    }

    @PostConstruct
    public void initialize() {
        
initialEventResolver.addDelegate(selectiveDuoWebflowEventResolver());
    }

}


This is driving me nuts because in the documentation it just states that 
you are allowed to use multiple DUO instances. But I'm getting an error 
that transitions aren't defined for the mfa-duo-force instance:

2018-02-01 10:25:29,433 WARN 
[org.apereo.cas.web.flow.resolver.impl.AbstractCasWebflowEventResolver] - 
<Transition definition cannot be found for event [mfa-duo-force|mfa-duo]>


If anyone has any information on how I can get this working or if I'm 
approaching this all wrong, please let me know. Thanks in advance!

-- 
- Website: https://apereo.github.io/cas
- Gitter Chatroom: https://gitter.im/apereo/cas
- List Guidelines: https://goo.gl/1VRrw7
- Contributions: https://goo.gl/mh7qDG
--- 
You received this message because you are subscribed to the Google Groups "CAS 
Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/a/apereo.org/d/msgid/cas-user/263f6a6c-9f2b-446f-9707-3c23b96a3f65%40apereo.org.

Reply via email to