Test Failed
Push — master ( cfc629...7c8fa2 )
by Misagh
14:48
created

determineDuoUserAccountAction()   A

Complexity

Conditions 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
1
package org.apereo.cas.adaptors.duo.config;
2
3
import org.apache.commons.lang3.StringUtils;
4
import org.apereo.cas.adaptors.duo.authn.BasicDuoSecurityAuthenticationService;
5
import org.apereo.cas.adaptors.duo.authn.DefaultDuoMultifactorAuthenticationProvider;
6
import org.apereo.cas.adaptors.duo.authn.DuoAuthenticationHandler;
7
import org.apereo.cas.adaptors.duo.web.flow.action.DetermineDuoUserAccountAction;
8
import org.apereo.cas.adaptors.duo.web.flow.action.PrepareDuoWebLoginFormAction;
9
import org.apereo.cas.adaptors.duo.web.flow.config.DuoMultifactorWebflowConfigurer;
10
import org.apereo.cas.authentication.DefaultVariegatedMultifactorAuthenticationProvider;
11
import org.apereo.cas.authentication.MultifactorAuthenticationUtils;
12
import org.apereo.cas.authentication.metadata.AuthenticationContextAttributeMetaDataPopulator;
13
import org.apereo.cas.authentication.AuthenticationHandler;
14
import org.apereo.cas.authentication.AuthenticationMetaDataPopulator;
15
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
16
import org.apereo.cas.authentication.principal.PrincipalFactory;
17
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
18
import org.apereo.cas.configuration.CasConfigurationProperties;
19
import org.apereo.cas.configuration.model.support.mfa.DuoSecurityMultifactorProperties;
20
import org.apereo.cas.services.ServicesManager;
21
import org.apereo.cas.services.VariegatedMultifactorAuthenticationProvider;
22
import org.apereo.cas.util.http.HttpClient;
23
import org.apereo.cas.web.flow.CasWebflowConfigurer;
24
import org.slf4j.Logger;
25
import org.slf4j.LoggerFactory;
26
import org.springframework.beans.factory.BeanCreationException;
27
import org.springframework.beans.factory.annotation.Autowired;
28
import org.springframework.beans.factory.annotation.Qualifier;
29
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
30
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
31
import org.springframework.boot.context.properties.EnableConfigurationProperties;
32
import org.springframework.cloud.context.config.annotation.RefreshScope;
33
import org.springframework.context.ApplicationContext;
34
import org.springframework.context.annotation.Bean;
35
import org.springframework.context.annotation.Configuration;
36
import org.springframework.context.annotation.DependsOn;
37
import org.springframework.core.Ordered;
38
import org.springframework.core.annotation.Order;
39
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
40
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
41
import org.springframework.webflow.execution.Action;
42
43
import java.util.List;
44
45
/**
46
 * This is {@link DuoSecurityAuthenticationEventExecutionPlanConfiguration}.
47
 *
48
 * @author Misagh Moayyed
49
 * @author Dmitriy Kopylenko
50
 * @since 5.1.0
51
 */
52
@Configuration("duoSecurityAuthenticationEventExecutionPlanConfiguration")
53
@EnableConfigurationProperties(CasConfigurationProperties.class)
54
public class DuoSecurityAuthenticationEventExecutionPlanConfiguration {
55
56
    private static final Logger LOGGER = LoggerFactory.getLogger(DuoSecurityAuthenticationEventExecutionPlanConfiguration.class);
57
58
    @Autowired
59
    private ApplicationContext applicationContext;
60
61
    @Autowired
62
    private CasConfigurationProperties casProperties;
63
    
64
    @Autowired
65
    @Qualifier("loginFlowRegistry")
66
    private FlowDefinitionRegistry loginFlowDefinitionRegistry;
67
68
    @Autowired
69
    private FlowBuilderServices flowBuilderServices;
70
71
    @Autowired
72
    @Qualifier("noRedirectHttpClient")
73
    private HttpClient httpClient;
74
    
75
    @Autowired
76
    @Qualifier("servicesManager")
77
    private ServicesManager servicesManager;
78
79
    @ConditionalOnMissingBean(name = "duoPrincipalFactory")
80
    @Bean
81
    public PrincipalFactory duoPrincipalFactory() {
82
        return new DefaultPrincipalFactory();
83
    }
84
85
    @Bean
86
    @RefreshScope
87
    public VariegatedMultifactorAuthenticationProvider duoMultifactorAuthenticationProvider() {
88
        final DefaultVariegatedMultifactorAuthenticationProvider provider = new DefaultVariegatedMultifactorAuthenticationProvider();
89
90
        casProperties.getAuthn().getMfa().getDuo()
91
                .stream()
92
                .filter(duo -> StringUtils.isNotBlank(duo.getDuoApiHost())
93
                        && StringUtils.isNotBlank(duo.getDuoIntegrationKey())
94
                        && StringUtils.isNotBlank(duo.getDuoSecretKey())
95
                        && StringUtils.isNotBlank(duo.getDuoApplicationKey()))
96
                .forEach(duo -> {
97
                    final BasicDuoSecurityAuthenticationService s = new BasicDuoSecurityAuthenticationService(duo, httpClient);
98
                    final DefaultDuoMultifactorAuthenticationProvider duoP = new DefaultDuoMultifactorAuthenticationProvider(s);
99
                    duoP.setGlobalFailureMode(casProperties.getAuthn().getMfa().getGlobalFailureMode());
100
                    duoP.setBypassEvaluator(MultifactorAuthenticationUtils.newMultifactorAuthenticationProviderBypass(duo.getBypass()));
101
                    duoP.setOrder(duo.getRank());
102
                    duoP.setId(duo.getId());
103
                    duoP.setRegistrationUrl(duo.getRegistrationUrl());
104
                    provider.addProvider(duoP);
105
                });
106
107
        if (provider.getProviders().isEmpty()) {
108
            throw new IllegalArgumentException("At least one Duo instance must be defined");
109
        }
110
        return provider;
111
    }
112
113
    @Bean
114
    public Action prepareDuoWebLoginFormAction() {
115
        return new PrepareDuoWebLoginFormAction(duoMultifactorAuthenticationProvider());
116
    }
117
118
    @Bean
119
    public Action determineDuoUserAccountAction() {
120
        return new DetermineDuoUserAccountAction(duoMultifactorAuthenticationProvider());
121
    }
122
    
123
    @Bean
124
    @RefreshScope
125
    public AuthenticationMetaDataPopulator duoAuthenticationMetaDataPopulator() {
126
        final String authenticationContextAttribute = casProperties.getAuthn().getMfa().getAuthenticationContextAttribute();
127
        return new AuthenticationContextAttributeMetaDataPopulator(authenticationContextAttribute, duoAuthenticationHandler(),
128
                duoMultifactorAuthenticationProvider());
129
    }
130
131
    @RefreshScope
132
    @Bean
133
    public AuthenticationHandler duoAuthenticationHandler() {
134
        final DuoAuthenticationHandler h;
135
        final List<DuoSecurityMultifactorProperties> duos = casProperties.getAuthn().getMfa().getDuo();
136
        if (!duos.isEmpty()) {
137
            final String name = duos.get(0).getName();
138
            if (duos.size() > 1) {
139
                LOGGER.debug("Multiple Duo Security providers are available; Duo authentication handler is named after [{}]", name);
140
            }
141
            h = new DuoAuthenticationHandler(name, servicesManager, duoPrincipalFactory(), duoMultifactorAuthenticationProvider());
142
        } else {
143
            throw new BeanCreationException("No configuration/settings could be found for Duo Security. Review settings and ensure the correct syntax is used");
144
        }
145
        return h;
146
    }
147
148
    @ConditionalOnMissingBean(name = "duoMultifactorWebflowConfigurer")
149
    @Bean
150
    @DependsOn("defaultWebflowConfigurer")
151
    @Order(Ordered.HIGHEST_PRECEDENCE)
152
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
153
    public CasWebflowConfigurer duoMultifactorWebflowConfigurer() {
154
        final boolean deviceRegistrationEnabled = casProperties.getAuthn().getMfa().getTrusted().isDeviceRegistrationEnabled();
155
        final CasWebflowConfigurer w = new DuoMultifactorWebflowConfigurer(flowBuilderServices, 
156
                loginFlowDefinitionRegistry, deviceRegistrationEnabled,
157
                duoMultifactorAuthenticationProvider(), applicationContext, casProperties);
158
        w.initialize();
159
        return w;
160
    }
161
162
    @ConditionalOnMissingBean(name = "duoSecurityAuthenticationEventExecutionPlanConfigurer")
163
    @Bean
164
    public AuthenticationEventExecutionPlanConfigurer duoSecurityAuthenticationEventExecutionPlanConfigurer() {
165
        return plan -> {
166
            plan.registerAuthenticationHandler(duoAuthenticationHandler());
167
            plan.registerMetadataPopulator(duoAuthenticationMetaDataPopulator());
168
        };
169
    }
170
}
171