From 55ac2b8010781bac1e5a9971949f71c74204ca59 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LELEU?= <jerome.leleu@teamdlab.com>
Date: Thu, 26 Mar 2020 12:03:00 +0100
Subject: [PATCH] fix login/pwd authentication

---
 .../config/cas-server-application-dev.yml     |  2 +-
 .../config/cas-server-application-recette.yml |  2 +-
 .../SurrogatedUserPrincipalFactory.java       | 14 ++---
 .../UserAuthenticationHandler.java            | 21 +++----
 .../authentication/UserPrincipalResolver.java | 19 +++---
 .../fr/gouv/vitamui/cas/config/AppConfig.java | 23 ++++---
 .../vitamui/cas/config/WebflowConfig.java     | 21 +++++--
 .../webflow/CustomLoginWebflowConfigurer.java | 62 ++++++++++++++++++-
 .../actions/CustomInitialFlowSetupAction.java | 25 ++++----
 .../cas/webflow/actions/DispatcherAction.java | 34 +++++-----
 .../resources/webflow/login/login-webflow.xml | 51 ---------------
 .../webflow/mfa-sms/mfa-sms-webflow.xml       | 53 ----------------
 .../UserAuthenticationHandlerTest.java        |  6 +-
 .../UserPrincipalResolverTest.java            |  5 +-
 .../CustomInitialFlowSetupActionTest.java     |  3 +-
 .../actions/DispatcherActionTests.java        |  6 +-
 16 files changed, 145 insertions(+), 202 deletions(-)
 delete mode 100644 cas/cas-server/src/main/resources/webflow/login/login-webflow.xml
 delete mode 100644 cas/cas-server/src/main/resources/webflow/mfa-sms/mfa-sms-webflow.xml

diff --git a/cas/cas-server/src/main/config/cas-server-application-dev.yml b/cas/cas-server/src/main/config/cas-server-application-dev.yml
index 49c2b270..574a7bc4 100644
--- a/cas/cas-server/src/main/config/cas-server-application-dev.yml
+++ b/cas/cas-server/src/main/config/cas-server-application-dev.yml
@@ -94,7 +94,7 @@ spring.mail.properties.mail.smtp.auth: true
 spring.mail.properties.mail.smtp.starttls.enable: true
 
 
-cas.authn.mfa.globalProviderId: mfa-simple
+#cas.authn.mfa.globalProviderId: mfa-simple
 
 
 cas.authn.throttle.failure.threshold: 2
diff --git a/cas/cas-server/src/main/config/cas-server-application-recette.yml b/cas/cas-server/src/main/config/cas-server-application-recette.yml
index b7148fc7..1783a57f 100644
--- a/cas/cas-server/src/main/config/cas-server-application-recette.yml
+++ b/cas/cas-server/src/main/config/cas-server-application-recette.yml
@@ -95,7 +95,7 @@ spring.mail.properties.mail.smtp.auth: true
 spring.mail.properties.mail.smtp.starttls.enable: true
 
 
-cas.authn.mfa.globalProviderId: mfa-simple
+#cas.authn.mfa.globalProviderId: mfa-simple
 
 
 cas.authn.throttle.failure.threshold: 2
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/SurrogatedUserPrincipalFactory.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/SurrogatedUserPrincipalFactory.java
index bd7f4d23..d5921efe 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/SurrogatedUserPrincipalFactory.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/SurrogatedUserPrincipalFactory.java
@@ -39,13 +39,10 @@ package fr.gouv.vitamui.cas.authentication;
 import fr.gouv.vitamui.commons.api.exception.VitamUIException;
 import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
 import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
+import lombok.RequiredArgsConstructor;
 import org.apereo.cas.authentication.principal.Principal;
 import org.apereo.cas.authentication.principal.PrincipalFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 
-import static fr.gouv.vitamui.commons.api.CommonConstants.SUPER_USER_ATTRIBUTE;
-
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -55,26 +52,23 @@ import java.util.Map;
  *
  *
  */
+@RequiredArgsConstructor
 public class SurrogatedUserPrincipalFactory implements PrincipalFactory {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(SurrogatedUserPrincipalFactory.class);
 
-    @Autowired
-    private UserPrincipalResolver resolver;
+    private final UserPrincipalResolver resolver;
 
     @Override
     public Principal createPrincipal(final String username) {
         throw new UnsupportedOperationException("This method cannot be used");
-
     }
 
     @Override
     public Principal createPrincipal(final String id, final Map<String, List<Object>> attributes) {
         LOGGER.debug("Creating username: {}", id);
         try {
-            Map<String, List<Object>> surrogateAttribute = new HashMap<>();
-            surrogateAttribute.put(SUPER_USER_ATTRIBUTE, Collections.emptyList());
-            final Principal principal = resolver.resolve(id, surrogateAttribute);
+            final Principal principal = resolver.resolve(id, new HashMap<>());
             LOGGER.debug("principal: {}", principal);
             return principal;
 
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandler.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandler.java
index 771f24b9..18cf370d 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandler.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandler.java
@@ -58,7 +58,6 @@ import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAut
 import org.apereo.cas.authentication.principal.Principal;
 import org.apereo.cas.authentication.principal.PrincipalFactory;
 import org.apereo.cas.services.ServicesManager;
-import org.springframework.beans.factory.annotation.Autowired;
 
 import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.commons.api.exception.VitamUIException;
@@ -68,9 +67,6 @@ import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
 import fr.gouv.vitamui.commons.api.domain.UserDto;
 import fr.gouv.vitamui.commons.api.enums.UserStatusEnum;
-import lombok.Getter;
-import lombok.Setter;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.webflow.context.ExternalContext;
 import org.springframework.webflow.core.collection.MutableAttributeMap;
 import org.springframework.webflow.execution.RequestContext;
@@ -81,23 +77,22 @@ import org.springframework.webflow.execution.RequestContextHolder;
  *
  *
  */
-@Getter
-@Setter
 public class UserAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(UserAuthenticationHandler.class);
 
-    @Autowired
-    private CasExternalRestClient casExternalRestClient;
+    private final CasExternalRestClient casExternalRestClient;
 
-    @Autowired
-    private Utils utils;
+    private final Utils utils;
 
-    @Value("${ip.header}")
-    private String ipHeaderName;
+    private final String ipHeaderName;
 
-    public UserAuthenticationHandler(final ServicesManager servicesManager, final PrincipalFactory principalFactory) {
+    public UserAuthenticationHandler(final ServicesManager servicesManager, final PrincipalFactory principalFactory,
+                                     final CasExternalRestClient casExternalRestClient, final Utils utils, final String ipHeaderName) {
         super(UserAuthenticationHandler.class.getSimpleName(), servicesManager, principalFactory, 1);
+        this.casExternalRestClient = casExternalRestClient;
+        this.utils = utils;
+        this.ipHeaderName = ipHeaderName;
     }
 
     @Override
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java
index 2fa2c1fa..3dc60b13 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolver.java
@@ -73,6 +73,7 @@ import java.util.*;
 
 import javax.servlet.http.HttpServletRequest;
 
+import lombok.RequiredArgsConstructor;
 import org.apereo.cas.authentication.AuthenticationHandler;
 import org.apereo.cas.authentication.Credential;
 import org.apereo.cas.authentication.SurrogateUsernamePasswordCredential;
@@ -83,7 +84,6 @@ import org.apereo.cas.authentication.principal.PrincipalFactory;
 import org.apereo.cas.authentication.principal.PrincipalResolver;
 import org.apereo.cas.web.support.WebUtils;
 import org.apereo.services.persondir.IPersonAttributeDao;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.webflow.core.collection.MutableAttributeMap;
 import org.springframework.webflow.execution.RequestContext;
 import org.springframework.webflow.execution.RequestContextHolder;
@@ -98,28 +98,24 @@ import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
 import fr.gouv.vitamui.commons.api.utils.CasJsonWrapper;
 import fr.gouv.vitamui.commons.security.client.dto.AuthUserDto;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
-import lombok.Getter;
-import lombok.Setter;
 
 /**
  * Resolver to retrieve the user.
  *
  *
  */
-@Getter
-@Setter
+@RequiredArgsConstructor
 public class UserPrincipalResolver implements PrincipalResolver {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(UserPrincipalResolver.class);
 
-    @Autowired
-    private PrincipalFactory principalFactory;
+    private final boolean finalSurrogationCall;
 
-    @Autowired
-    private CasExternalRestClient casExternalRestClient;
+    private final PrincipalFactory principalFactory;
 
-    @Autowired
-    private Utils utils;
+    private final CasExternalRestClient casExternalRestClient;
+
+    private final Utils utils;
 
     @Override
     public Principal resolve(final Credential credential, final Optional<Principal> principal, final Optional<AuthenticationHandler> handler) {
@@ -148,7 +144,6 @@ public class UserPrincipalResolver implements PrincipalResolver {
         }
 
         boolean authToken = true;
-        final boolean finalSurrogationCall = oldAttributes.containsKey(SUPER_USER_ATTRIBUTE);
         // this is not called by the surrogation principal factory, but the surrogation is in progress, it will be called again
         // spare the extra info on this call by not requesting the auth token
         if (!finalSurrogationCall && superUsername != null) {
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/AppConfig.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/AppConfig.java
index 65e581b2..da2a71cf 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/AppConfig.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/AppConfig.java
@@ -159,26 +159,33 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
     @Value("${api.token.ttl}")
     private Integer apiTokenTtl;
 
+    @Value("${ip.header}")
+    private String ipHeaderName;
+
     @Bean
     public UserAuthenticationHandler userAuthenticationHandler() {
-        return new UserAuthenticationHandler(servicesManager, principalFactory);
+        return new UserAuthenticationHandler(servicesManager, principalFactory, casRestClient(), utils(), ipHeaderName);
     }
 
     @Bean
     public UserPrincipalResolver userResolver() {
-        return new UserPrincipalResolver();
+        return new UserPrincipalResolver(false, principalFactory, casRestClient(), utils());
+    }
+
+    @Bean
+    public UserPrincipalResolver userResolverForSurrogation() {
+        return new UserPrincipalResolver(true, principalFactory, casRestClient(), utils());
     }
 
     @Bean
-    public AuthenticationEventExecutionPlanConfigurer registerInternalHandler(final UserAuthenticationHandler userAuthenticationHandler,
-            final UserPrincipalResolver userResolver) {
-        return plan -> plan.registerAuthenticationHandlerWithPrincipalResolver(userAuthenticationHandler, userResolver);
+    public AuthenticationEventExecutionPlanConfigurer registerInternalHandler() {
+        return plan -> plan.registerAuthenticationHandlerWithPrincipalResolver(userAuthenticationHandler(), userResolver());
     }
 
     @Bean
-    public AuthenticationEventExecutionPlanConfigurer pac4jAuthenticationEventExecutionPlanConfigurer(final UserPrincipalResolver userResolver) {
+    public AuthenticationEventExecutionPlanConfigurer pac4jAuthenticationEventExecutionPlanConfigurer() {
         return plan -> {
-            plan.registerAuthenticationHandlerWithPrincipalResolver(clientAuthenticationHandler, userResolver);
+            plan.registerAuthenticationHandlerWithPrincipalResolver(clientAuthenticationHandler, userResolver());
             plan.registerAuthenticationMetadataPopulator(clientAuthenticationMetaDataPopulator);
         };
     }
@@ -211,7 +218,7 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
 
     @Bean
     public PrincipalFactory surrogatePrincipalFactory() {
-        return new SurrogatedUserPrincipalFactory();
+        return new SurrogatedUserPrincipalFactory(userResolverForSurrogation());
     }
 
     @Bean
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java
index 6cfab56e..647e615f 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebflowConfig.java
@@ -36,6 +36,7 @@
  */
 package fr.gouv.vitamui.cas.config;
 
+import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.cas.webflow.actions.GeneralTerminateSessionAction;
 import fr.gouv.vitamui.cas.provider.ProvidersService;
 import fr.gouv.vitamui.cas.webflow.*;
@@ -59,7 +60,6 @@ import org.apereo.cas.util.io.CommunicationsManager;
 import org.apereo.cas.web.DelegatedClientWebflowManager;
 import org.apereo.cas.web.cookie.CasCookieBuilder;
 import org.apereo.cas.web.flow.CasWebflowConfigurer;
-import org.apereo.cas.web.flow.DelegatedClientAuthenticationAction;
 import org.apereo.cas.web.flow.SingleSignOnParticipationStrategy;
 import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver;
 import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver;
@@ -69,6 +69,7 @@ import org.pac4j.core.context.session.SessionStore;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.annotation.Bean;
@@ -193,10 +194,20 @@ public class WebflowConfig {
     @Qualifier("delegatedClientDistributedSessionStore")
     private ObjectProvider<SessionStore> delegatedClientDistributedSessionStore;
 
+    @Value("${vitamui.portal.url}")
+    private String vitamuiPortalUrl;
+
+    @Value("${cas.authn.surrogate.separator}")
+    private String surrogationSeperator;
+
+    @Autowired
+    private Utils utils;
+
     @Bean
     public DispatcherAction dispatcherAction() {
-        return new DispatcherAction(providersService, identityProviderHelper, casRestClient);
+        return new DispatcherAction(providersService, identityProviderHelper, casRestClient, surrogationSeperator, utils);
     }
+
     @Bean
     @RefreshScope
     public Action sendPasswordResetInstructionsAction() {
@@ -215,7 +226,9 @@ public class WebflowConfig {
             casProperties,
             authenticationEventExecutionPlan.getObject(),
             webflowSingleSignOnParticipationStrategy.getObject(),
-            ticketRegistrySupport.getObject());
+            ticketRegistrySupport.getObject(),
+            vitamuiPortalUrl,
+            surrogationSeperator);
     }
 
     @Bean
@@ -242,7 +255,7 @@ public class WebflowConfig {
     @Bean
     @Lazy
     public Action delegatedAuthenticationAction() {
-        return new DelegatedClientAuthenticationAction(
+        return new AutomaticDelegatedClientAuthenticationAction(
             initialAuthenticationAttemptWebflowEventResolver.getObject(),
             serviceTicketRequestWebflowEventResolver.getObject(),
             adaptiveAuthenticationPolicy.getObject(),
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomLoginWebflowConfigurer.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomLoginWebflowConfigurer.java
index 48ae7640..77b5f661 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomLoginWebflowConfigurer.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomLoginWebflowConfigurer.java
@@ -41,9 +41,11 @@ import javax.security.auth.login.AccountNotFoundException;
 import javax.security.auth.login.CredentialExpiredException;
 import javax.security.auth.login.FailedLoginException;
 
+import fr.gouv.vitamui.cas.webflow.actions.DispatcherAction;
 import lombok.val;
 import org.apereo.cas.authentication.PrincipalException;
 import org.apereo.cas.authentication.adaptive.UnauthorizedAuthenticationException;
+import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
 import org.apereo.cas.authentication.exceptions.AccountDisabledException;
 import org.apereo.cas.authentication.exceptions.AccountPasswordMustChangeException;
 import org.apereo.cas.authentication.exceptions.InvalidLoginLocationException;
@@ -51,12 +53,15 @@ import org.apereo.cas.authentication.exceptions.InvalidLoginTimeException;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.services.UnauthorizedServiceForPrincipalException;
 import org.apereo.cas.ticket.UnsatisfiedAuthenticationPolicyException;
+import org.apereo.cas.util.CollectionUtils;
 import org.apereo.cas.web.flow.CasWebflowConstants;
 import org.apereo.cas.web.flow.configurer.DefaultLoginWebflowConfigurer;
 import org.springframework.context.ApplicationContext;
 import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
 import org.springframework.webflow.engine.ActionState;
 import org.springframework.webflow.engine.Flow;
+import org.springframework.webflow.engine.History;
+import org.springframework.webflow.engine.builder.BinderConfiguration;
 import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
 
 import fr.gouv.vitamui.cas.webflow.actions.TriggerChangePasswordAction;
@@ -64,7 +69,9 @@ import fr.gouv.vitamui.cas.webflow.actions.TriggerChangePasswordAction;
 /**
  * A webflow configurer:
  * - with a specific processing for the redirections ("secured connection" intermediate page)
- * - to handle the change password action even if the user is already authenticated.
+ * - to handle the change password action even if the user is already authenticated
+ * - with a only-username page
+ * - with a (username+)password page.
  *
  *
  */
@@ -72,6 +79,12 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer
 
     private static final String STATE_ID_TRIGGER_CHANGE_PASSWORD = "triggerChangePassword"; // NOSONAR
 
+    private static final String INTERMEDIATE_SUBMIT = "intermediateSubmit";
+
+    private static final String VIEW_PWD_FORM = "viewPwfForm";
+
+    private static final String BAD_CONFIGURATION_VIEW = "casAccountBadConfigurationView";
+
     public static final String DIRECT_RESULT = "direct";
 
     private static final String DIRECT_ACTION = "directRedirection";
@@ -121,7 +134,7 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer
             CasWebflowConstants.ACTION_ID_AUTHENTICATION_EXCEPTION_HANDLER);
         createTransitionForState(handler, AccountDisabledException.class.getSimpleName(), CasWebflowConstants.VIEW_ID_ACCOUNT_DISABLED);
         createTransitionForState(handler, AccountLockedException.class.getSimpleName(), CasWebflowConstants.VIEW_ID_ACCOUNT_LOCKED);
-        // JLE custo:
+        // custo:
         createTransitionForState(handler, AccountPasswordMustChangeException.class.getSimpleName(), "sendInstructions");
         createTransitionForState(handler, CredentialExpiredException.class.getSimpleName(), CasWebflowConstants.VIEW_ID_EXPIRED_PASSWORD);
         createTransitionForState(handler, InvalidLoginLocationException.class.getSimpleName(), CasWebflowConstants.VIEW_ID_INVALID_WORKSTATION);
@@ -134,6 +147,51 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer
         createTransitionForState(handler, UnauthorizedAuthenticationException.class.getSimpleName(), CasWebflowConstants.VIEW_ID_AUTHENTICATION_BLOCKED);
         createTransitionForState(handler, CasWebflowConstants.STATE_ID_SERVICE_UNAUTHZ_CHECK, CasWebflowConstants.STATE_ID_SERVICE_UNAUTHZ_CHECK);
         createStateDefaultTransition(handler, CasWebflowConstants.STATE_ID_INIT_LOGIN_FORM);
+    }
+
+    @Override
+    protected void createLoginFormView(final Flow flow) {
+        val propertiesToBind = CollectionUtils.wrapList("username");
+        val binder = createStateBinderConfiguration(propertiesToBind);
+
+        val state = createViewState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, "casLoginView", binder);
+        state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));
+        createStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordCredential.class);
+
+        val transition = createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, INTERMEDIATE_SUBMIT);
+        val attributes = transition.getAttributes();
+        attributes.put("bind", Boolean.TRUE);
+        attributes.put("validate", Boolean.TRUE);
+        attributes.put("history", History.INVALIDATE);
+
+        createIntermediateSubmitAction(flow);
+        createPwdFormView(flow);
+    }
+
+    protected void createIntermediateSubmitAction(final Flow flow) {
+        val action = createActionState(flow, INTERMEDIATE_SUBMIT, "dispatcherAction");
+        createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_SUCCESS, VIEW_PWD_FORM);
+        createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_STOP, CasWebflowConstants.STATE_ID_STOP_WEBFLOW);
+        createTransitionForState(action, DispatcherAction.DISABLED, CasWebflowConstants.VIEW_ID_ACCOUNT_DISABLED);
+        createTransitionForState(action, DispatcherAction.BAD_CONFIGURATION, BAD_CONFIGURATION_VIEW);
+
+        createEndState(flow, BAD_CONFIGURATION_VIEW, BAD_CONFIGURATION_VIEW);
+    }
+
+    protected void createPwdFormView(final Flow flow) {
+        val propertiesToBind = CollectionUtils.wrapList("username", "password");
+        val binder = createStateBinderConfiguration(propertiesToBind);
+
+        val state = createViewState(flow, VIEW_PWD_FORM, "casPwdView", binder);
+        state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));
+        createStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordCredential.class);
+
+        val transition = createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, CasWebflowConstants.STATE_ID_REAL_SUBMIT);
+        val attributes = transition.getAttributes();
+        attributes.put("bind", Boolean.TRUE);
+        attributes.put("validate", Boolean.TRUE);
+        attributes.put("history", History.INVALIDATE);
 
+        createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_RESET_PASSWORD, CasWebflowConstants.VIEW_ID_SEND_RESET_PASSWORD_ACCT_INFO);
     }
 }
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupAction.java
index 9d658b5e..cb481940 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupAction.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupAction.java
@@ -51,7 +51,6 @@ import org.apereo.cas.web.flow.SingleSignOnParticipationStrategy;
 import org.apereo.cas.web.flow.login.InitialFlowSetupAction;
 import org.apereo.cas.web.support.ArgumentExtractor;
 import org.apereo.cas.web.support.WebUtils;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.webflow.core.collection.MutableAttributeMap;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
@@ -72,11 +71,9 @@ public class CustomInitialFlowSetupAction extends InitialFlowSetupAction {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(CustomInitialFlowSetupAction.class);
 
-    @Value("${vitamui.portal.url}")
-    private String vitamuiPortalUrl;
+    private final String vitamuiPortalUrl;
 
-    @Value("${cas.authn.surrogate.separator}")
-    private String separator;
+    private final String surrogationSeperator;
 
     @Value("${theme.vitam-logo:#{null}}")
     private String vitamLogoPath;
@@ -92,9 +89,13 @@ public class CustomInitialFlowSetupAction extends InitialFlowSetupAction {
                                         final CasConfigurationProperties casProperties,
                                         final AuthenticationEventExecutionPlan authenticationEventExecutionPlan,
                                         final SingleSignOnParticipationStrategy renewalStrategy,
-                                        final TicketRegistrySupport ticketRegistrySupport) {
+                                        final TicketRegistrySupport ticketRegistrySupport,
+                                        final String vitamuiPortalUrl,
+                                        final String surrogationSeperator) {
         super(argumentExtractors, servicesManager, authenticationRequestServiceSelectionStrategies, ticketGrantingTicketCookieGenerator,
             warnCookieGenerator, casProperties, authenticationEventExecutionPlan, renewalStrategy, ticketRegistrySupport);
+        this.vitamuiPortalUrl = vitamuiPortalUrl;
+        this.surrogationSeperator = surrogationSeperator;
     }
 
     @Override
@@ -108,14 +109,14 @@ public class CustomInitialFlowSetupAction extends InitialFlowSetupAction {
 
             username = username.toLowerCase();
             LOGGER.debug("Provided username: {}", username);
-            if (username.startsWith(separator)) {
-                username = StringUtils.substringAfter(username, separator);
+            if (username.startsWith(surrogationSeperator)) {
+                username = StringUtils.substringAfter(username, surrogationSeperator);
             }
 
             WebUtils.putCredential(context, new UsernamePasswordCredential(username, null));
 
-            if (username.contains(separator)) {
-                final String[] parts = username.split("\\" + separator);
+            if (username.contains(surrogationSeperator)) {
+                final String[] parts = username.split("\\" + surrogationSeperator);
                 flowScope.put(Constants.SURROGATE, parts[0]);
                 flowScope.put(Constants.SUPER_USER, parts[1]);
             }
@@ -144,8 +145,4 @@ public class CustomInitialFlowSetupAction extends InitialFlowSetupAction {
 
         return super.doExecute(context);
     }
-
-    public void setSeparator(final String separator) {
-        this.separator = separator;
-    }
 }
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherAction.java
index d1ee36d4..7ff4bacf 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherAction.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherAction.java
@@ -48,15 +48,11 @@ import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
 import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
 import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
-import lombok.Getter;
-import lombok.Setter;
 import org.apache.commons.lang.StringUtils;
 import org.apereo.cas.CasProtocolConstants;
 import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
 import org.apereo.cas.authentication.principal.Service;
 import org.apereo.cas.web.support.WebUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.webflow.action.AbstractAction;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
@@ -73,10 +69,11 @@ import java.util.Optional;
  *
  *
  */
-@Getter
-@Setter
 public class DispatcherAction extends AbstractAction {
 
+    public static final String DISABLED = "disabled";
+    public static final String BAD_CONFIGURATION = "badConfiguration";
+
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(DispatcherAction.class);
 
     private final ProvidersService providersService;
@@ -85,19 +82,20 @@ public class DispatcherAction extends AbstractAction {
 
     private final CasExternalRestClient casExternalRestClient;
 
-    @Value("${cas.authn.surrogate.separator}")
-    private String separator;
+    private final String surrogationSeperator;
 
-    @Autowired
-    private Utils utils;
+    private final Utils utils;
 
-    @Autowired
     public DispatcherAction(final ProvidersService providersService,
                             final IdentityProviderHelper identityProviderHelper,
-                            final CasExternalRestClient casExternalRestClient) {
+                            final CasExternalRestClient casExternalRestClient,
+                            final String surrogationSeperator,
+                            final Utils utils) {
         this.providersService = providersService;
         this.identityProviderHelper = identityProviderHelper;
         this.casExternalRestClient = casExternalRestClient;
+        this.surrogationSeperator = surrogationSeperator;
+        this.utils = utils;
     }
 
     @Override
@@ -107,12 +105,12 @@ public class DispatcherAction extends AbstractAction {
         final String username = credential.getUsername().toLowerCase();
         String dispatchedUser = username;
         String surrogate = null;
-        if (username.contains(separator)) {
-            dispatchedUser = StringUtils.substringAfter(username, separator);
-            if (username.startsWith(separator)) {
+        if (username.contains(surrogationSeperator)) {
+            dispatchedUser = StringUtils.substringAfter(username, surrogationSeperator);
+            if (username.startsWith(surrogationSeperator)) {
                 WebUtils.putCredential(requestContext, new UsernamePasswordCredential(dispatchedUser, null));
             } else {
-                surrogate = StringUtils.substringBefore(username, separator);
+                surrogate = StringUtils.substringBefore(username, surrogationSeperator);
             }
         }
         LOGGER.debug("Dispatching user: {} / surrogate: {}", dispatchedUser, surrogate);
@@ -146,7 +144,7 @@ public class DispatcherAction extends AbstractAction {
         if (provider != null) {
             isInternal = provider.getInternal();
         } else {
-            return new Event(this, "badConfiguration");
+            return new Event(this, BAD_CONFIGURATION);
         }
         if (isInternal) {
             request.removeAttribute(Constants.SURROGATE);
@@ -169,6 +167,6 @@ public class DispatcherAction extends AbstractAction {
 
     private Event userDisabled(final String emailUser) {
         LOGGER.error("Bad status for user: {}", emailUser);
-        return new Event(this, "disabled");
+        return new Event(this, DISABLED);
     }
 }
diff --git a/cas/cas-server/src/main/resources/webflow/login/login-webflow.xml b/cas/cas-server/src/main/resources/webflow/login/login-webflow.xml
deleted file mode 100644
index f215afa6..00000000
--- a/cas/cas-server/src/main/resources/webflow/login/login-webflow.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-      xmlns="http://www.springframework.org/schema/webflow"
-      xsi:schemaLocation="http://www.springframework.org/schema/webflow
-                          http://www.springframework.org/schema/webflow/spring-webflow.xsd">
-
-    <action-state id="initializeLoginForm">
-        <evaluate expression="initializeLoginAction" />
-        <transition on="success" to="viewLoginForm"/>
-    </action-state>
-
-    <!-- start custo -->
-    <!-- viewLoginForm is updated from the original webflow -->
-    <view-state id="viewLoginForm" view="casLoginView" model="credential">
-        <binder>
-            <binding property="username" required="true"/>
-        </binder>
-        <transition on="submit" bind="true" validate="true" to="intermediateSubmit" history="invalidate"/>
-    </view-state>
-
-    <action-state id="intermediateSubmit">
-        <evaluate expression="dispatcherAction"/>
-        <transition on="success" to="viewPwdForm"/>
-        <transition on="stop" to="stopWebflow"/>
-        <transition on="disabled" to="casAccountDisabledView"/>
-        <transition on="badConfiguration" to="casAccountBadConfigurationView"/>
-    </action-state>
-
-    <view-state id="casAccountBadConfigurationView" view="casAccountBadConfigurationView" />
-
-    <view-state id="viewPwdForm" view="casPwdView" model="credential">
-        <binder>
-            <binding property="username" required="true"/>
-            <binding property="password" required="true"/>
-        </binder>
-        <!-- transition added as we use the password management module -->
-        <transition on="resetPassword" to="casResetPasswordSendInstructionsView" />
-        <transition on="submit" bind="true" validate="true" to="realSubmit" history="invalidate"/>
-    </view-state>
-    <!-- end custo -->
-
-    <action-state id="realSubmit">
-        <evaluate expression="authenticationViaFormAction"/>
-        <transition on="warn" to="warn"/>
-        <transition on="success" to="createTicketGrantingTicket"/>
-        <transition on="successWithWarnings" to="showAuthenticationWarningMessages"/>
-        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
-        <transition on="error" to="initializeLoginForm"/>
-    </action-state>
-
-</flow>
diff --git a/cas/cas-server/src/main/resources/webflow/mfa-sms/mfa-sms-webflow.xml b/cas/cas-server/src/main/resources/webflow/mfa-sms/mfa-sms-webflow.xml
deleted file mode 100644
index 3d4ddfb4..00000000
--- a/cas/cas-server/src/main/resources/webflow/mfa-sms/mfa-sms-webflow.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<flow xmlns="http://www.springframework.org/schema/webflow"
-      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-      xsi:schemaLocation="http://www.springframework.org/schema/webflow
-                          http://www.springframework.org/schema/webflow/spring-webflow.xsd">
-
-    <var name="credential" class="org.apereo.cas.authentication.OneTimeTokenCredential" />
-    <on-start>
-        <evaluate expression="initialFlowSetupAction" />
-    </on-start>
-
-    <action-state id="initializeLoginForm">
-        <evaluate expression="initializeLoginAction" />
-        <transition on="success" to="sendOTPAction"/>
-    </action-state>
-
-    <action-state id="initializeLoginFormWithoutOTP">
-        <evaluate expression="initializeLoginAction" />
-        <transition on="success" to="viewLoginForm"/>
-    </action-state>
-
-    <action-state id="sendOTPAction">
-        <evaluate expression="sendOTPAction" />
-        <transition on="success" to="viewLoginForm"/>
-        <transition on="error" to="missingPhone"/>
-    </action-state>
-
-    <view-state id="missingPhone" view="casSmsMissingPhoneView" />
-
-    <view-state id="viewLoginForm" view="casSmsLoginView" model="credential">
-        <binder>
-            <binding property="token" required="true"/>
-        </binder>
-        <on-entry>
-            <set name="viewScope.principal" value="conversationScope.authentication.principal" />
-        </on-entry>
-        <transition on="submit" bind="true" validate="true" to="realSubmit"/>
-        <transition on="resend" to="initializeLoginForm"/>
-    </view-state>
-
-    <action-state id="realSubmit">
-        <evaluate expression="smsAuthenticationWebflowAction" />
-        <transition on="success" to="success" />
-        <transition on="expired" to="codeExpired"/>
-        <transition on="error" to="initializeLoginFormWithoutOTP" />
-    </action-state>
-
-    <view-state id="codeExpired" view="casSmsCodeExpiredView">
-        <transition on="resend" to="initializeLoginForm"/>
-    </view-state>
-
-    <end-state id="success" />
-</flow>
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandlerTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandlerTest.java
index 50a97d17..bd685858 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandlerTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserAuthenticationHandlerTest.java
@@ -60,12 +60,10 @@ public final class UserAuthenticationHandlerTest {
 
     @Before
     public void setUp() {
-        handler = new UserAuthenticationHandler(null, new DefaultPrincipalFactory());
         casExternalRestClient = mock(CasExternalRestClient.class);
-        handler.setCasExternalRestClient(casExternalRestClient);
-        credential = new UsernamePasswordCredential(USERNAME, PASSWORD);
         final Utils utils = new Utils(casExternalRestClient, null);
-        handler.setUtils(utils);
+        handler = new UserAuthenticationHandler(null, new DefaultPrincipalFactory(), casExternalRestClient, utils, null);
+        credential = new UsernamePasswordCredential(USERNAME, PASSWORD);
     }
 
     @Test
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolverTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolverTest.java
index f9c15477..b130c464 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolverTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/UserPrincipalResolverTest.java
@@ -56,12 +56,9 @@ public final class UserPrincipalResolverTest {
 
     @Before
     public void setUp() {
-        resolver = new UserPrincipalResolver();
         casExternalRestClient = mock(CasExternalRestClient.class);
-        resolver.setCasExternalRestClient(casExternalRestClient);
-        resolver.setPrincipalFactory(new DefaultPrincipalFactory());
         final Utils utils = new Utils(casExternalRestClient, null);
-        resolver.setUtils(utils);
+        resolver = new UserPrincipalResolver(false, new DefaultPrincipalFactory(), casExternalRestClient, utils);
         final RequestContext context = mock(RequestContext.class);
         RequestContextHolder.setRequestContext(context);
     }
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupActionTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupActionTest.java
index 4acfb606..174314ad 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupActionTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomInitialFlowSetupActionTest.java
@@ -46,8 +46,7 @@ public final class CustomInitialFlowSetupActionTest extends BaseWebflowActionTes
         action = new CustomInitialFlowSetupAction(new ArrayList<>(), mock(ServicesManager.class),
                 mock(AuthenticationServiceSelectionPlan.class), mock(CasCookieBuilder.class),
                 mock(CasCookieBuilder.class), new CasConfigurationProperties(), mock(AuthenticationEventExecutionPlan.class),
-                mock(SingleSignOnParticipationStrategy.class), mock(TicketRegistrySupport.class));
-        action.setSeparator(",");
+                mock(SingleSignOnParticipationStrategy.class), mock(TicketRegistrySupport.class), "", ",");
     }
     @Test
     public void testUsernameNoSubrogation() {
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTests.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTests.java
index 52988866..b96f5856 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTests.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTests.java
@@ -17,7 +17,6 @@ import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.pac4j.core.exception.http.FoundAction;
 import org.pac4j.saml.client.SAML2Client;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
@@ -65,12 +64,9 @@ public final class DispatcherActionTests extends BaseWebflowActionTest {
         identityProviderHelper = mock(IdentityProviderHelper.class);
         casExternalRestClient = mock(CasExternalRestClient.class);
 
-        action = new DispatcherAction(providersService, identityProviderHelper, casExternalRestClient);
-        action.setSeparator(",");
         final Utils utils = new Utils(casExternalRestClient, null);
-        action.setUtils(utils);
+        action = new DispatcherAction(providersService, identityProviderHelper, casExternalRestClient, ",", utils);
 
-        final FoundAction foundAction = new FoundAction("http://server");
         final SAML2Client client = new SAML2Client();
         provider = new SamlIdentityProviderDto(new IdentityProviderDto(), client);
         provider.setInternal(true);
-- 
GitLab