From 28eabd765d7215d097ffabd300134f8fa2a8371e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9r=C3=B4me=20LELEU?= <jerome.leleu@teamdlab.com>
Date: Thu, 2 Apr 2020 18:08:41 +0200
Subject: [PATCH] password management

---
 cas/cas-server/pom.xml                        |  22 +--
 .../config/cas-server-application-dev.yml     |   1 +
 .../config/cas-server-application-recette.yml |   1 +
 .../fr/gouv/vitamui/cas/config/AppConfig.java |  33 +++-
 .../fr/gouv/vitamui/cas/config/PmConfig.java  |  67 --------
 .../fr/gouv/vitamui/cas/config/WebConfig.java |  79 ----------
 .../vitamui/cas/config/WebflowConfig.java     |  52 ++++++-
 ...java => IamPasswordManagementService.java} | 102 +++++-------
 .../fr/gouv/vitamui/cas/pm/PmTokenTicket.java | 145 ------------------
 .../vitamui/cas/pm/PmTokenTicketFactory.java  |  55 -------
 ...tSessionTicketExpirationPolicyBuilder.java |  41 +++++
 .../cas/pm/ResetPasswordController.java       |  81 +++++-----
 .../java/fr/gouv/vitamui/cas/util/Utils.java  |  26 +---
 ...8NSendPasswordResetInstructionsAction.java |  79 +++++-----
 .../RestPasswordManagementConfiguration.java  |  42 -----
 .../main/resources/META-INF/spring.factories  |   4 +-
 .../actions => }/BaseWebflowActionTest.java   |   2 +-
 ...IamSurrogateAuthenticationServiceTest.java |   2 +-
 .../UserAuthenticationHandlerTest.java        |   2 +-
 .../UserPrincipalResolverTest.java            |   4 +-
 ... => IamPasswordManagementServiceTest.java} |  42 ++---
 .../cas/provider/ProvidersServiceTest.java    |  13 +-
 ...legatedClientAuthenticationActionTest.java |   1 +
 .../webflow/actions/DispatcherActionTest.java |   3 +-
 24 files changed, 275 insertions(+), 624 deletions(-)
 delete mode 100644 cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/PmConfig.java
 delete mode 100644 cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebConfig.java
 rename cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/{IamRestPasswordManagementService.java => IamPasswordManagementService.java} (64%)
 delete mode 100644 cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicket.java
 delete mode 100644 cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicketFactory.java
 create mode 100644 cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTransientSessionTicketExpirationPolicyBuilder.java
 delete mode 100644 cas/cas-server/src/main/java/org/apereo/cas/config/pm/RestPasswordManagementConfiguration.java
 rename cas/cas-server/src/test/java/fr/gouv/vitamui/cas/{webflow/actions => }/BaseWebflowActionTest.java (98%)
 rename cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/{IamRestPasswordManagementServiceTest.java => IamPasswordManagementServiceTest.java} (81%)

diff --git a/cas/cas-server/pom.xml b/cas/cas-server/pom.xml
index 282462ba..fdec4a71 100644
--- a/cas/cas-server/pom.xml
+++ b/cas/cas-server/pom.xml
@@ -150,16 +150,6 @@
         </dependency>
 
         <!-- password management -->
-        <dependency>
-            <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-support-pm-rest</artifactId>
-            <version>${cas.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-support-pm</artifactId>
-            <version>${cas.version}</version>
-        </dependency>
         <dependency>
             <groupId>org.apereo.cas</groupId>
             <artifactId>cas-server-support-pm-webflow</artifactId>
@@ -195,29 +185,27 @@
             <version>${cas.version}</version>
         </dependency>
 
-        <!-- CustomInitialFlowSetupAction -->
+        <!-- others -->
         <dependency>
             <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-support-actions</artifactId>
+            <artifactId>cas-server-core-cookie-api</artifactId>
             <version>${cas.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-core-cookie-api</artifactId>
+            <artifactId>cas-server-core-web-api</artifactId>
             <version>${cas.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-core-web-api</artifactId>
+            <artifactId>cas-server-core-authentication-api</artifactId>
             <version>${cas.version}</version>
         </dependency>
         <dependency>
             <groupId>org.apereo.cas</groupId>
-            <artifactId>cas-server-core-authentication-api</artifactId>
+            <artifactId>cas-server-support-actions</artifactId>
             <version>${cas.version}</version>
         </dependency>
-
-        <!-- others -->
         <dependency>
             <groupId>org.apereo.cas</groupId>
             <artifactId>cas-server-webapp-init</artifactId>
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 821a84fe..74891cae 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
@@ -85,6 +85,7 @@ cas.authn.pm.reset.mail.from: serveur-cas@noreply.com
 cas.authn.pm.reset.expirationMinutes: 10
 cas.authn.pm.reset.mail.attributeName: xxx
 cas.authn.pm.reset.securityQuestionsEnabled: false
+cas.authn.pm.reset.includeServerIpAddress: false
 cas.authn.pm.autoLogin: true
 
 # Used to sign/encrypt the password-reset link
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 31f454d4..4a9ea04f 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
@@ -86,6 +86,7 @@ cas.authn.pm.reset.mail.from: serveur-cas@noreply.com
 cas.authn.pm.reset.expirationMinutes: 10
 cas.authn.pm.reset.mail.attributeName: xxx
 cas.authn.pm.reset.securityQuestionsEnabled: false
+cas.authn.pm.reset.includeServerIpAddress: false
 cas.authn.pm.autoLogin: true
 
 # Used to sign/encrypt the password-reset link
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 2c074020..ee63213d 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
@@ -38,17 +38,22 @@ package fr.gouv.vitamui.cas.config;
 
 import fr.gouv.vitamui.cas.authentication.DelegatedSurrogateAuthenticationPostProcessor;
 import fr.gouv.vitamui.cas.authentication.IamSurrogateAuthenticationService;
+import fr.gouv.vitamui.cas.pm.IamPasswordManagementService;
 import lombok.SneakyThrows;
+import org.apereo.cas.CentralAuthenticationService;
 import org.apereo.cas.audit.AuditableExecution;
 import org.apereo.cas.authentication.*;
 import org.apereo.cas.authentication.principal.PrincipalFactory;
 import org.apereo.cas.authentication.principal.PrincipalResolver;
 import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
 import org.apereo.cas.configuration.CasConfigurationProperties;
+import org.apereo.cas.pm.PasswordHistoryService;
+import org.apereo.cas.pm.PasswordManagementService;
 import org.apereo.cas.services.ServicesManager;
 import org.apereo.cas.ticket.*;
 import org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenFactory;
 import org.apereo.cas.ticket.accesstoken.OAuth20DefaultAccessToken;
+import org.apereo.cas.ticket.registry.TicketRegistry;
 import org.apereo.cas.token.JwtBuilder;
 import org.apereo.cas.util.crypto.CipherExecutor;
 import org.pac4j.core.context.session.SessionStore;
@@ -94,6 +99,9 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(AppConfig.class);
 
+    @Autowired
+    private CasConfigurationProperties casProperties;
+
     @Autowired
     @Qualifier("servicesManager")
     private ServicesManager servicesManager;
@@ -161,6 +169,21 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
     @Qualifier("delegatedClientDistributedSessionStore")
     private SessionStore delegatedClientDistributedSessionStore;
 
+    @Autowired
+    private TicketRegistry ticketRegistry;
+
+    @Autowired
+    @Qualifier("centralAuthenticationService")
+    private ObjectProvider<CentralAuthenticationService> centralAuthenticationService;
+
+    @Autowired
+    @Qualifier("passwordManagementCipherExecutor")
+    private CipherExecutor passwordManagementCipherExecutor;
+
+    @Autowired
+    @Qualifier("passwordHistoryService")
+    private PasswordHistoryService passwordHistoryService;
+
     @Value("${token.api.cas}")
     private String tokenApiCas;
 
@@ -239,7 +262,7 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
 
     @Bean
     public Utils utils() {
-        return new Utils(casRestClient(), tokenApiCas, casTenantIdentifier, casIdentity, mailSender);
+        return new Utils(tokenApiCas, casTenantIdentifier, casIdentity, mailSender);
     }
 
     @Bean
@@ -269,4 +292,12 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
     public SurrogateAuthenticationService surrogateAuthenticationService() {
         return new IamSurrogateAuthenticationService(casRestClient(), servicesManager, utils());
     }
+
+    @RefreshScope
+    @Bean
+    public PasswordManagementService passwordChangeService() {
+        return new IamPasswordManagementService(casProperties.getAuthn().getPm(), passwordManagementCipherExecutor,
+            casProperties.getServer().getPrefix(), passwordHistoryService, casRestClient(), providersService(),
+            identityProviderHelper(), centralAuthenticationService.getObject(), utils(), ticketRegistry);
+    }
 }
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/PmConfig.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/PmConfig.java
deleted file mode 100644
index 7de12802..00000000
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/PmConfig.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020)
- * and the signatories of the "VITAM - Accord du Contributeur" agreement.
- *
- * contact@programmevitam.fr
- *
- * This software is a computer program whose purpose is to implement
- * implement a digital archiving front-office system for the secure and
- * efficient high volumetry VITAM solution.
- *
- * This software is governed by the CeCILL-C license under French law and
- * abiding by the rules of distribution of free software.  You can  use,
- * modify and/ or redistribute the software under the terms of the CeCILL-C
- * license as circulated by CEA, CNRS and INRIA at the following URL
- * "http://www.cecill.info".
- *
- * As a counterpart to the access to the source code and  rights to copy,
- * modify and redistribute granted by the license, users are provided only
- * with a limited warranty  and the software's author,  the holder of the
- * economic rights,  and the successive licensors  have only  limited
- * liability.
- *
- * In this respect, the user's attention is drawn to the risks associated
- * with loading,  using,  modifying and/or developing or reproducing the
- * software by the user in light of its specific status of free software,
- * that may mean  that it is complicated to manipulate,  and  that  also
- * therefore means  that it is reserved for developers  and  experienced
- * professionals having in-depth computer knowledge. Users are therefore
- * encouraged to load and test the software's suitability as regards their
- * requirements in conditions enabling the security of their systems and/or
- * data to be ensured and,  more generally, to use and operate it in the
- * same conditions as regards security.
- *
- * The fact that you are presently reading this means that you have had
- * knowledge of the CeCILL-C license and that you accept its terms.
- */
-package fr.gouv.vitamui.cas.config;
-
-import fr.gouv.vitamui.cas.pm.PmTokenTicket;
-import fr.gouv.vitamui.cas.pm.PmTokenTicketFactory;
-import org.apereo.cas.ticket.BaseTicketCatalogConfigurer;
-import org.apereo.cas.ticket.TicketCatalog;
-import org.apereo.cas.ticket.TicketDefinition;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Password management configuration.
- *
- *
- */
-@Configuration
-public class PmConfig extends BaseTicketCatalogConfigurer {
-
-    @Bean
-    public PmTokenTicketFactory pmTokenTicketFactory() {
-        return new PmTokenTicketFactory();
-    }
-
-    @Override
-    public final void configureTicketCatalog(final TicketCatalog plan) {
-        final TicketDefinition ticketDefinition = buildTicketDefinition(plan, PmTokenTicket.PREFIX, PmTokenTicket.class);
-        ticketDefinition.getProperties().setStorageName("pmTokenTicketsCache");
-        ticketDefinition.getProperties().setStorageTimeout(24L * 3600L);
-        plan.register(ticketDefinition);
-    }
-}
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebConfig.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebConfig.java
deleted file mode 100644
index c393d9bc..00000000
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/config/WebConfig.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020)
- * and the signatories of the "VITAM - Accord du Contributeur" agreement.
- *
- * contact@programmevitam.fr
- *
- * This software is a computer program whose purpose is to implement
- * implement a digital archiving front-office system for the secure and
- * efficient high volumetry VITAM solution.
- *
- * This software is governed by the CeCILL-C license under French law and
- * abiding by the rules of distribution of free software.  You can  use,
- * modify and/ or redistribute the software under the terms of the CeCILL-C
- * license as circulated by CEA, CNRS and INRIA at the following URL
- * "http://www.cecill.info".
- *
- * As a counterpart to the access to the source code and  rights to copy,
- * modify and redistribute granted by the license, users are provided only
- * with a limited warranty  and the software's author,  the holder of the
- * economic rights,  and the successive licensors  have only  limited
- * liability.
- *
- * In this respect, the user's attention is drawn to the risks associated
- * with loading,  using,  modifying and/or developing or reproducing the
- * software by the user in light of its specific status of free software,
- * that may mean  that it is complicated to manipulate,  and  that  also
- * therefore means  that it is reserved for developers  and  experienced
- * professionals having in-depth computer knowledge. Users are therefore
- * encouraged to load and test the software's suitability as regards their
- * requirements in conditions enabling the security of their systems and/or
- * data to be ensured and,  more generally, to use and operate it in the
- * same conditions as regards security.
- *
- * The fact that you are presently reading this means that you have had
- * knowledge of the CeCILL-C license and that you accept its terms.
- */
-package fr.gouv.vitamui.cas.config;
-
-import fr.gouv.vitamui.cas.pm.ResetPasswordController;
-import org.apereo.cas.web.view.CasProtocolView;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Scope;
-import org.thymeleaf.spring5.SpringTemplateEngine;
-
-
-/**
- * Web customizations.
- *
- *
- */
-@Configuration
-public class WebConfig {
-
-    @Autowired
-    private ApplicationContext applicationContext;
-
-    @Autowired
-    private SpringTemplateEngine springTemplateEngine;
-
-    @Autowired
-    private ThymeleafProperties thymeleafProperties;
-
-    @Bean
-    public ResetPasswordController resetPasswordController() {
-        return new ResetPasswordController();
-    }
-
-    @Bean
-    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-    public CasProtocolView casGetResponseView() {
-        return new CasProtocolView("protocol/casGetResponseView",
-            applicationContext, springTemplateEngine, thymeleafProperties);
-    }
-}
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 36d298cf..c34f6e61 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,8 @@
  */
 package fr.gouv.vitamui.cas.config;
 
+import fr.gouv.vitamui.cas.pm.PmTransientSessionTicketExpirationPolicyBuilder;
+import fr.gouv.vitamui.cas.pm.ResetPasswordController;
 import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.cas.webflow.actions.GeneralTerminateSessionAction;
 import fr.gouv.vitamui.cas.provider.ProvidersService;
@@ -43,6 +45,7 @@ import fr.gouv.vitamui.cas.webflow.*;
 import fr.gouv.vitamui.cas.webflow.actions.*;
 import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
+import lombok.val;
 import org.apereo.cas.CentralAuthenticationService;
 import org.apereo.cas.audit.AuditableExecution;
 import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan;
@@ -51,7 +54,9 @@ import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.pm.PasswordManagementService;
 import org.apereo.cas.services.ServicesManager;
-import org.apereo.cas.ticket.TicketFactory;
+import org.apereo.cas.ticket.TransientSessionTicket;
+import org.apereo.cas.ticket.factory.DefaultTicketFactory;
+import org.apereo.cas.ticket.factory.DefaultTransientSessionTicketFactory;
 import org.apereo.cas.ticket.registry.TicketRegistry;
 import org.apereo.cas.ticket.registry.TicketRegistrySupport;
 import org.apereo.cas.util.CollectionUtils;
@@ -63,25 +68,31 @@ import org.apereo.cas.web.flow.SingleSignOnParticipationStrategy;
 import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver;
 import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver;
 import org.apereo.cas.web.support.ArgumentExtractor;
+import org.apereo.cas.web.view.CasProtocolView;
 import org.pac4j.core.client.Clients;
 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.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
 import org.springframework.cloud.context.config.annotation.RefreshScope;
 import org.springframework.context.ApplicationContext;
+import org.springframework.context.HierarchicalMessageSource;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Lazy;
+import org.springframework.context.annotation.Scope;
 import org.springframework.core.Ordered;
 import org.springframework.core.annotation.Order;
 import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
 import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
 import org.springframework.webflow.execution.Action;
+import org.thymeleaf.spring5.SpringTemplateEngine;
 
 /**
- * Webflow customizations.
+ * Web(flow) customizations.
  *
  *
  */
@@ -169,10 +180,6 @@ public class WebflowConfig {
     @Qualifier("argumentExtractor")
     private ObjectProvider<ArgumentExtractor> argumentExtractor;
 
-    @Autowired
-    @Qualifier("defaultTicketFactory")
-    private TicketFactory ticketFactory;
-
     @Autowired
     @Qualifier("centralAuthenticationService")
     private ObjectProvider<CentralAuthenticationService> centralAuthenticationService;
@@ -194,6 +201,16 @@ public class WebflowConfig {
     @Autowired
     private TicketRegistrySupport ticketRegistrySupport;
 
+    @Autowired
+    @Qualifier("messageSource")
+    private HierarchicalMessageSource messageSource;
+
+    @Autowired
+    private SpringTemplateEngine springTemplateEngine;
+
+    @Autowired
+    private ThymeleafProperties thymeleafProperties;
+
     @Value("${vitamui.portal.url}")
     private String vitamuiPortalUrl;
 
@@ -206,11 +223,19 @@ public class WebflowConfig {
             surrogationSeparator, utils, delegatedClientDistributedSessionStore.getObject());
     }
 
+    @Bean
+    public DefaultTransientSessionTicketFactory pmTicketFactory() {
+        return new DefaultTransientSessionTicketFactory(new PmTransientSessionTicketExpirationPolicyBuilder(casProperties));
+    }
+
     @Bean
     @RefreshScope
     public Action sendPasswordResetInstructionsAction() {
+        val pmTicketFactory = new DefaultTicketFactory();
+        pmTicketFactory.addTicketFactory(TransientSessionTicket.class, pmTicketFactory());
+
         return new I18NSendPasswordResetInstructionsAction(casProperties, communicationsManager, passwordManagementService,
-            ticketRegistry, ticketFactory);
+            ticketRegistry, pmTicketFactory, messageSource, providersService, identityProviderHelper, utils);
     }
 
     @Bean
@@ -268,4 +293,17 @@ public class WebflowConfig {
             warnCookieGenerator.getObject(),
             casProperties.getLogout());
     }
+
+    @Bean
+    @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+    public CasProtocolView casGetResponseView() {
+        return new CasProtocolView("protocol/casGetResponseView",
+            applicationContext, springTemplateEngine, thymeleafProperties);
+    }
+
+    @Bean
+    public ResetPasswordController resetPasswordController() {
+        return new ResetPasswordController(casProperties, passwordManagementService, communicationsManager, ticketRegistry,
+            messageSource, utils, pmTicketFactory());
+    }
 }
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementService.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementService.java
similarity index 64%
rename from cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementService.java
rename to cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementService.java
index e204936c..5636e5e0 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementService.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementService.java
@@ -36,10 +36,13 @@
  */
 package fr.gouv.vitamui.cas.pm;
 
+import java.io.Serializable;
 import java.util.Map;
 import java.util.Optional;
 
-import org.apereo.cas.DefaultCentralAuthenticationService;
+import lombok.val;
+import org.apache.commons.lang.StringUtils;
+import org.apereo.cas.CentralAuthenticationService;
 import org.apereo.cas.authentication.Authentication;
 import org.apereo.cas.authentication.Credential;
 import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
@@ -47,24 +50,22 @@ import org.apereo.cas.configuration.model.support.pm.PasswordManagementPropertie
 import org.apereo.cas.pm.BasePasswordManagementService;
 import org.apereo.cas.pm.InvalidPasswordException;
 import org.apereo.cas.pm.PasswordChangeRequest;
+import org.apereo.cas.pm.PasswordHistoryService;
 import org.apereo.cas.ticket.TicketGrantingTicket;
 import org.apereo.cas.ticket.registry.TicketRegistry;
+import org.apereo.cas.util.crypto.CipherExecutor;
 import org.apereo.cas.web.support.WebUtils;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.util.Assert;
-import org.springframework.webflow.core.collection.MutableAttributeMap;
 import org.springframework.webflow.execution.RequestContext;
 import org.springframework.webflow.execution.RequestContextHolder;
 
 import fr.gouv.vitamui.cas.provider.ProvidersService;
 import fr.gouv.vitamui.cas.util.Utils;
-import fr.gouv.vitamui.commons.api.domain.UserDto;
 import fr.gouv.vitamui.commons.api.enums.UserStatusEnum;
 import fr.gouv.vitamui.commons.api.exception.ConflictException;
 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 fr.gouv.vitamui.iam.common.dto.IdentityProviderDto;
 import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
 import lombok.Getter;
@@ -77,11 +78,9 @@ import lombok.Setter;
  */
 @Getter
 @Setter
-public class IamRestPasswordManagementService extends BasePasswordManagementService {
+public class IamPasswordManagementService extends BasePasswordManagementService {
 
-    private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(IamRestPasswordManagementService.class);
-
-    private static final String PM_TICKET_ID = "pmTicketId";
+    private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(IamPasswordManagementService.class);
 
     private final CasExternalRestClient casExternalRestClient;
 
@@ -89,40 +88,43 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ
 
     private final IdentityProviderHelper identityProviderHelper;
 
-    @Autowired
-    private DefaultCentralAuthenticationService centralAuthenticationService;
-
-    @Autowired
-    private Utils utils;
+    private final CentralAuthenticationService centralAuthenticationService;
 
-    @Autowired
-    private PmTokenTicketFactory pmTokenTicketFactory;
+    private final Utils utils;
 
-    @Autowired
-    private TicketRegistry ticketRegistry;
+    private final TicketRegistry ticketRegistry;
 
-    public IamRestPasswordManagementService(final CasExternalRestClient casExternalRestClient,
-                                            final PasswordManagementProperties passwordManagementProperties,
-                                            final ProvidersService providersService,
-                                            final IdentityProviderHelper identityProviderHelper) {
-        super(passwordManagementProperties, null, null, null);
+    public IamPasswordManagementService(final PasswordManagementProperties passwordManagementProperties,
+                                        final CipherExecutor<Serializable, String> cipherExecutor,
+                                        final String issuer,
+                                        final PasswordHistoryService passwordHistoryService,
+                                        final CasExternalRestClient casExternalRestClient,
+                                        final ProvidersService providersService,
+                                        final IdentityProviderHelper identityProviderHelper,
+                                        final CentralAuthenticationService centralAuthenticationService,
+                                        final Utils utils,
+                                        final TicketRegistry ticketRegistry) {
+        super(passwordManagementProperties, cipherExecutor, issuer, passwordHistoryService);
         this.casExternalRestClient = casExternalRestClient;
         this.providersService = providersService;
         this.identityProviderHelper = identityProviderHelper;
+        this.centralAuthenticationService = centralAuthenticationService;
+        this.utils = utils;
+        this.ticketRegistry = ticketRegistry;
     }
 
     protected RequestContext blockIfSubrogation() {
-        final RequestContext requestContext = RequestContextHolder.getRequestContext();
+        val requestContext = RequestContextHolder.getRequestContext();
         Authentication authentication = WebUtils.getAuthentication(requestContext);
         if (authentication == null) {
-            final String tgtId = WebUtils.getTicketGrantingTicketId(requestContext);
-            if (tgtId != null) {
-                final TicketGrantingTicket tgt = centralAuthenticationService.getTicket(tgtId, TicketGrantingTicket.class);
+            val tgtId = WebUtils.getTicketGrantingTicketId(requestContext);
+            if (StringUtils.isNotBlank(tgtId)) {
+                val tgt = centralAuthenticationService.getTicket(tgtId, TicketGrantingTicket.class);
                 authentication = tgt.getAuthentication();
             }
         }
         if (authentication != null) {
-            final String superUsername = utils.getSuperUsername(authentication);
+            val superUsername = utils.getSuperUsername(authentication);
             Assert.isNull(superUsername, "cannot use password management with subrogation");
         }
 
@@ -131,32 +133,25 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ
 
     @Override
     public boolean changeInternal(final Credential c, final PasswordChangeRequest bean) throws InvalidPasswordException {
-        final RequestContext requestContext = blockIfSubrogation();
-        final MutableAttributeMap flowScope = requestContext.getFlowScope();
+        val requestContext = blockIfSubrogation();
+        val flowScope = requestContext.getFlowScope();
         if (flowScope != null) {
             flowScope.put("passwordHasBeenChanged", true);
         }
 
-        final UsernamePasswordCredential upc = (UsernamePasswordCredential) c;
-        final String username = upc.getUsername();
+        val upc = (UsernamePasswordCredential) c;
+        val username = upc.getUsername();
         Assert.notNull(username, "username can not be null");
         // username to lowercase
-        final String usernameLowercase = username.toLowerCase();
+        val usernameLowercase = username.toLowerCase();
         LOGGER.debug("username: {}", usernameLowercase);
-        final Optional<IdentityProviderDto> identityProvider = identityProviderHelper.findByUserIdentifier(providersService.getProviders(), usernameLowercase);
+        val identityProvider = identityProviderHelper.findByUserIdentifier(providersService.getProviders(), usernameLowercase);
         Assert.isTrue(identityProvider.isPresent(), "only a user [" + usernameLowercase + "] linked to an identity provider can change his password");
         Assert.isTrue(identityProvider.get().getInternal() != null && identityProvider.get().getInternal(),
                 "only an internal user [" + usernameLowercase + "] can change his password");
 
-        // we don't care about the fact that the oldPassword is the same as upc.getPassword();
         try {
             casExternalRestClient.changePassword(utils.buildContext(usernameLowercase), usernameLowercase, bean.getPassword());
-
-            final String ticket = (String) requestContext.getFlowScope().get(PM_TICKET_ID);
-            if (ticket != null) {
-                ticketRegistry.deleteTicket(ticket);
-            }
-
             return true;
         }
         catch (final ConflictException e) {
@@ -171,9 +166,9 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ
     @Override
     public String findEmail(final String username) {
         String email = null;
-        final String usernameWithLowercase = username.toLowerCase();
+        val usernameWithLowercase = username.toLowerCase();
         try {
-            final UserDto user = casExternalRestClient.getUserByEmail(utils.buildContext(usernameWithLowercase), usernameWithLowercase, Optional.empty());
+            val user = casExternalRestClient.getUserByEmail(utils.buildContext(usernameWithLowercase), usernameWithLowercase, Optional.empty());
             if (user != null && UserStatusEnum.ENABLED.equals(user.getStatus())) {
                 email = user.getEmail();
             }
@@ -189,27 +184,6 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ
         throw new UnsupportedOperationException("security questions/answers are not available");
     }
 
-    @Override
-    public String createToken(final String to) {
-        final PmTokenTicket ticket = pmTokenTicketFactory.create(to, (int) properties.getReset().getExpirationMinutes());
-        ticketRegistry.addTicket(ticket);
-        return ticket.getId();
-    }
-
-    @Override
-    public String parseToken(final String token) {
-        final PmTokenTicket ticket = ticketRegistry.getTicket(token, PmTokenTicket.class);
-        if (ticket == null || ticket.isExpired()) {
-            LOGGER.warn("PM token ticket expired: {}", token);
-            return null;
-        }
-        final RequestContext requestContext = RequestContextHolder.getRequestContext();
-        if (requestContext != null) {
-            requestContext.getFlowScope().put(PM_TICKET_ID, ticket.getId());
-        }
-        return ticket.getUser();
-    }
-
     private static class PasswordAlreadyUsedException extends InvalidPasswordException {
 
         /**
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicket.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicket.java
deleted file mode 100644
index 5d0a943f..00000000
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicket.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020)
- * and the signatories of the "VITAM - Accord du Contributeur" agreement.
- *
- * contact@programmevitam.fr
- *
- * This software is a computer program whose purpose is to implement
- * implement a digital archiving front-office system for the secure and
- * efficient high volumetry VITAM solution.
- *
- * This software is governed by the CeCILL-C license under French law and
- * abiding by the rules of distribution of free software.  You can  use,
- * modify and/ or redistribute the software under the terms of the CeCILL-C
- * license as circulated by CEA, CNRS and INRIA at the following URL
- * "http://www.cecill.info".
- *
- * As a counterpart to the access to the source code and  rights to copy,
- * modify and redistribute granted by the license, users are provided only
- * with a limited warranty  and the software's author,  the holder of the
- * economic rights,  and the successive licensors  have only  limited
- * liability.
- *
- * In this respect, the user's attention is drawn to the risks associated
- * with loading,  using,  modifying and/or developing or reproducing the
- * software by the user in light of its specific status of free software,
- * that may mean  that it is complicated to manipulate,  and  that  also
- * therefore means  that it is reserved for developers  and  experienced
- * professionals having in-depth computer knowledge. Users are therefore
- * encouraged to load and test the software's suitability as regards their
- * requirements in conditions enabling the security of their systems and/or
- * data to be ensured and,  more generally, to use and operate it in the
- * same conditions as regards security.
- *
- * The fact that you are presently reading this means that you have had
- * knowledge of the CeCILL-C license and that you accept its terms.
- */
-package fr.gouv.vitamui.cas.pm;
-
-import java.time.ZonedDateTime;
-
-import org.apereo.cas.authentication.Authentication;
-import org.apereo.cas.ticket.ExpirationPolicy;
-import org.apereo.cas.ticket.Ticket;
-import org.apereo.cas.ticket.TicketGrantingTicket;
-import org.apereo.cas.ticket.TicketState;
-import org.apereo.cas.ticket.expiration.HardTimeoutExpirationPolicy;
-
-/**
- * Specific ticket for the password management token.
- *
- *
- */
-public class PmTokenTicket implements Ticket, TicketState {
-
-    /**
-     *
-     */
-    private static final long serialVersionUID = 4524446119459217215L;
-
-    public static final String PREFIX = "PM";
-
-    private final String id;
-
-    private final ZonedDateTime creationTime;
-
-    private final String user;
-
-    private final ExpirationPolicy expirationPolicy;
-
-    public PmTokenTicket(final String id, final String user, final int ttlInMinutes) {
-        this.id = id;
-        this.user = user;
-        creationTime = ZonedDateTime.now();
-        expirationPolicy = new HardTimeoutExpirationPolicy(ttlInMinutes * 60l);
-    }
-
-    @Override
-    public String getId() {
-        return id;
-    }
-
-    @Override
-    public boolean isExpired() {
-        return expirationPolicy.isExpired(this);
-    }
-
-    @Override
-    public TicketGrantingTicket getTicketGrantingTicket() {
-        return null;
-    }
-
-    @Override
-    public ZonedDateTime getCreationTime() {
-        return creationTime;
-    }
-
-    @Override
-    public int getCountOfUses() {
-        return 0;
-    }
-
-    @Override
-    public ExpirationPolicy getExpirationPolicy() {
-        return expirationPolicy;
-    }
-
-    @Override
-    public String getPrefix() {
-        return PREFIX;
-    }
-
-    @Override
-    public void markTicketExpired() {
-        //do nothing
-    }
-
-    @Override
-    public int compareTo(final Ticket t) {
-        return creationTime.compareTo(t.getCreationTime());
-    }
-
-    public String getUser() {
-        return user;
-    }
-
-    @Override
-    public ZonedDateTime getLastTimeUsed() {
-        return getCreationTime();
-    }
-
-    @Override
-    public ZonedDateTime getPreviousTimeUsed() {
-        return getCreationTime();
-    }
-
-    @Override
-    public Authentication getAuthentication() {
-        return null;
-    }
-
-    @Override
-    public void update() {
-        //do nothing
-    }
-}
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicketFactory.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicketFactory.java
deleted file mode 100644
index 10703bf7..00000000
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTokenTicketFactory.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2019-2020)
- * and the signatories of the "VITAM - Accord du Contributeur" agreement.
- *
- * contact@programmevitam.fr
- *
- * This software is a computer program whose purpose is to implement
- * implement a digital archiving front-office system for the secure and
- * efficient high volumetry VITAM solution.
- *
- * This software is governed by the CeCILL-C license under French law and
- * abiding by the rules of distribution of free software.  You can  use,
- * modify and/ or redistribute the software under the terms of the CeCILL-C
- * license as circulated by CEA, CNRS and INRIA at the following URL
- * "http://www.cecill.info".
- *
- * As a counterpart to the access to the source code and  rights to copy,
- * modify and redistribute granted by the license, users are provided only
- * with a limited warranty  and the software's author,  the holder of the
- * economic rights,  and the successive licensors  have only  limited
- * liability.
- *
- * In this respect, the user's attention is drawn to the risks associated
- * with loading,  using,  modifying and/or developing or reproducing the
- * software by the user in light of its specific status of free software,
- * that may mean  that it is complicated to manipulate,  and  that  also
- * therefore means  that it is reserved for developers  and  experienced
- * professionals having in-depth computer knowledge. Users are therefore
- * encouraged to load and test the software's suitability as regards their
- * requirements in conditions enabling the security of their systems and/or
- * data to be ensured and,  more generally, to use and operate it in the
- * same conditions as regards security.
- *
- * The fact that you are presently reading this means that you have had
- * knowledge of the CeCILL-C license and that you accept its terms.
- */
-package fr.gouv.vitamui.cas.pm;
-
-import org.apereo.cas.ticket.UniqueTicketIdGenerator;
-import org.apereo.cas.util.DefaultUniqueTicketIdGenerator;
-
-/**
- * Factory for the management password token tickets.
- *
- *
- */
-public class PmTokenTicketFactory {
-
-    private final UniqueTicketIdGenerator defaultTicketIdGenerator = new DefaultUniqueTicketIdGenerator(40);
-
-    public PmTokenTicket create(final String user, final int ttlInMinutes) {
-        final String id = defaultTicketIdGenerator.getNewTicketId(PmTokenTicket.PREFIX);
-        return new PmTokenTicket(id, user, ttlInMinutes);
-    }
-}
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTransientSessionTicketExpirationPolicyBuilder.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTransientSessionTicketExpirationPolicyBuilder.java
new file mode 100644
index 00000000..7b9e9107
--- /dev/null
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/PmTransientSessionTicketExpirationPolicyBuilder.java
@@ -0,0 +1,41 @@
+package fr.gouv.vitamui.cas.pm;
+
+import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
+import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
+import lombok.val;
+import org.apereo.cas.configuration.CasConfigurationProperties;
+import org.apereo.cas.ticket.ExpirationPolicy;
+import org.apereo.cas.ticket.expiration.HardTimeoutExpirationPolicy;
+import org.apereo.cas.ticket.expiration.builder.TransientSessionTicketExpirationPolicyBuilder;
+import org.apereo.cas.web.support.WebUtils;
+
+/**
+ * Specific expiration policy builder for password management.
+ */
+public class PmTransientSessionTicketExpirationPolicyBuilder extends TransientSessionTicketExpirationPolicyBuilder {
+
+    private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(PmTransientSessionTicketExpirationPolicyBuilder.class);
+
+    public static final String PM_EXPIRATION_IN_MINUTES_ATTRIBUTE = "pmExpirationInMinutes";
+
+    public PmTransientSessionTicketExpirationPolicyBuilder(final CasConfigurationProperties casProperties) {
+        super(casProperties);
+    }
+
+    @Override
+    public ExpirationPolicy toTransientSessionTicketExpirationPolicy() {
+        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext();
+        if (request != null) {
+            val expInMinutesAttribute = request.getAttribute(PM_EXPIRATION_IN_MINUTES_ATTRIBUTE);
+            if (expInMinutesAttribute != null) {
+                try {
+                    val expInMinutes = Integer.parseInt((String) expInMinutesAttribute);
+                    return new HardTimeoutExpirationPolicy(expInMinutes * 60);
+                } catch (final NumberFormatException e) {
+                    LOGGER.error("Cannot get expiration in minutes", e);
+                }
+            }
+        }
+        return new HardTimeoutExpirationPolicy(casProperties.getAuthn().getPm().getReset().getExpirationMinutes() * 60);
+    }
+}
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/ResetPasswordController.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/ResetPasswordController.java
index ab6e3872..80acc38d 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/ResetPasswordController.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/pm/ResetPasswordController.java
@@ -36,18 +36,21 @@
  */
 package fr.gouv.vitamui.cas.pm;
 
-import static org.apereo.cas.web.flow.CasWebflowConfigurer.FLOW_ID_LOGIN;
-
+import java.io.Serializable;
 import java.util.Locale;
 
+import lombok.RequiredArgsConstructor;
+import lombok.val;
 import org.apache.commons.lang3.StringUtils;
+import org.apereo.cas.authentication.principal.Service;
 import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.pm.PasswordManagementService;
 import org.apereo.cas.pm.web.flow.PasswordManagementWebflowUtils;
+import org.apereo.cas.ticket.factory.DefaultTransientSessionTicketFactory;
 import org.apereo.cas.ticket.registry.TicketRegistry;
+import org.apereo.cas.util.CollectionUtils;
 import org.apereo.cas.util.io.CommunicationsManager;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
+import org.apereo.cas.web.flow.CasWebflowConfigurer;
 import org.springframework.context.HierarchicalMessageSource;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -58,6 +61,8 @@ import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
 import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
 
+import javax.servlet.http.HttpServletRequest;
+
 /**
  * Rest controller for CAS extra features.
  *
@@ -65,37 +70,31 @@ import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
  */
 @RestController
 @RequestMapping("/extras")
+@RequiredArgsConstructor
 public class ResetPasswordController {
 
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(ResetPasswordController.class);
 
-    @Autowired
-    private CasConfigurationProperties casProperties;
+    private final CasConfigurationProperties casProperties;
 
-    @Autowired
-    private PasswordManagementService passwordManagementService;
+    private final PasswordManagementService passwordManagementService;
 
-    @Autowired
-    private CommunicationsManager communicationsManager;
+    private final CommunicationsManager communicationsManager;
 
-    @Autowired
-    private PmTokenTicketFactory pmTokenTicketFactory;
+    private final TicketRegistry ticketRegistry;
 
-    @Autowired
-    private TicketRegistry ticketRegistry;
+    private final HierarchicalMessageSource messageSource;
 
-    @Autowired
-    @Qualifier("messageSource")
-    private HierarchicalMessageSource messageSource;
+    private final Utils utils;
 
-    @Autowired
-    private Utils utils;
+    private final DefaultTransientSessionTicketFactory pmTicketFactory;
 
     @GetMapping("/resetPassword")
     public boolean resetPassword(@RequestParam(value = "username", defaultValue = "") final String username,
-            @RequestParam(value = "firstname", defaultValue = "") final String firstname,
-            @RequestParam(value = "lastname", defaultValue = "") final String lastname, @RequestParam(value = "ttl", defaultValue = "") final String ttl,
-            @RequestParam(value = "language", defaultValue = "en") final String language) {
+                                 @RequestParam(value = "firstname", defaultValue = "") final String firstname,
+                                 @RequestParam(value = "lastname", defaultValue = "") final String lastname, @RequestParam(value = "ttl", defaultValue = "") final String ttl,
+                                 @RequestParam(value = "language", defaultValue = "en") final String language,
+                                 final HttpServletRequest request) {
 
         if (StringUtils.isBlank(username)) {
             LOGGER.warn("No username is provided");
@@ -107,9 +106,9 @@ public class ResetPasswordController {
             LOGGER.warn("CAS is unable to send password-reset emails given no settings are defined to account for email servers");
             return false;
         }
-        final String usernameLower = username.toLowerCase();
-        final String to = passwordManagementService.findEmail(usernameLower);
-        if (StringUtils.isBlank(to)) {
+        val usernameLower = username.toLowerCase();
+        val email = passwordManagementService.findEmail(usernameLower);
+        if (StringUtils.isBlank(email)) {
             LOGGER.warn("No recipient is provided");
             return false;
         }
@@ -117,33 +116,35 @@ public class ResetPasswordController {
         final int expMinutes;
         if (PmMessageToSend.ONE_DAY.equals(ttl)) {
             expMinutes = 24 * 60;
-        }
-        else {
+        } else {
             expMinutes = (int) casProperties.getAuthn().getPm().getReset().getExpirationMinutes();
         }
+        request.setAttribute(PmTransientSessionTicketExpirationPolicyBuilder.PM_EXPIRATION_IN_MINUTES_ATTRIBUTE, expMinutes);
 
-        final String url = buildPasswordResetUrl(usernameLower, casProperties, expMinutes);
+        final String url = buildPasswordResetUrl(usernameLower, casProperties);
         final PmMessageToSend messageToSend = PmMessageToSend.buildMessage(messageSource, firstname, lastname, ttl, url, new Locale(language));
 
-        LOGGER.debug("Generated password reset URL [{}] for: {} ({}); Link is only active for the next [{}] minute(s)", utils.sanitizePasswordResetUrl(url), to,
-                messageToSend.getSubject(), expMinutes);
-        if (!sendPasswordResetEmailToAccount(to, messageToSend.getSubject(), messageToSend.getText())) {
+        LOGGER.debug("Generated password reset URL [{}] for: {} ({}); Link is only active for the next [{}] minute(s)", utils.sanitizePasswordResetUrl(url),
+            email, messageToSend.getSubject(), expMinutes);
+        if (!sendPasswordResetEmailToAccount(email, messageToSend.getSubject(), messageToSend.getText())) {
             return false;
         }
 
         return true;
     }
 
-    protected String buildPasswordResetUrl(final String username, final CasConfigurationProperties casProperties, final int expMinutes) {
-        final String token = createToken(username, expMinutes);
-        return casProperties.getServer().getPrefix().concat('/' + FLOW_ID_LOGIN + '?'
-            + PasswordManagementWebflowUtils.REQUEST_PARAMETER_NAME_PASSWORD_RESET_TOKEN + '=').concat(token);
-    }
+    protected String buildPasswordResetUrl(final String username, final CasConfigurationProperties casProperties) {
+        val token = passwordManagementService.createToken(username);
+
+        val properties = CollectionUtils.<String, Serializable>wrap(PasswordManagementWebflowUtils.FLOWSCOPE_PARAMETER_NAME_TOKEN, token);
+        val ticket = pmTicketFactory.create((Service) null, properties);
+        this.ticketRegistry.addTicket(ticket);
+
+        val resetUrl = new StringBuilder(casProperties.getServer().getPrefix())
+            .append('/').append(CasWebflowConfigurer.FLOW_ID_LOGIN).append('?')
+            .append(PasswordManagementWebflowUtils.REQUEST_PARAMETER_NAME_PASSWORD_RESET_TOKEN).append('=').append(ticket.getId());
 
-    protected String createToken(final String to, final int expMinutes) {
-        final PmTokenTicket ticket = pmTokenTicketFactory.create(to, expMinutes);
-        ticketRegistry.addTicket(ticket);
-        return ticket.getId();
+        return resetUrl.toString();
     }
 
     protected boolean sendPasswordResetEmailToAccount(final String to, final String subject, final String msg) {
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Utils.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Utils.java
index b3928976..3ac4fb37 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Utils.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Utils.java
@@ -39,17 +39,15 @@ package fr.gouv.vitamui.cas.util;
 import java.io.IOException;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 
 import javax.mail.internet.MimeMessage;
 import javax.servlet.http.Cookie;
 import javax.servlet.http.HttpServletResponse;
 
 import lombok.RequiredArgsConstructor;
-import org.apache.commons.lang.StringUtils;
+import lombok.val;
 import org.apereo.cas.CasProtocolConstants;
 import org.apereo.cas.authentication.Authentication;
-import org.apereo.cas.authentication.principal.Principal;
 import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
 import org.apereo.cas.configuration.model.support.cookie.TicketGrantingCookieProperties;
 import org.apereo.cas.web.flow.CasWebflowConstants;
@@ -65,13 +63,9 @@ import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
 
 import fr.gouv.vitamui.commons.api.CommonConstants;
-import fr.gouv.vitamui.commons.api.domain.UserDto;
 import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
 import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory;
 import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext;
-import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
-
-import lombok.val;
 
 /**
  * Helper class.
@@ -85,8 +79,6 @@ public class Utils {
 
     private static final int BROWSER_SESSION_LIFETIME = -1;
 
-    private final CasExternalRestClient casExternalRestClient;
-
     private final String casToken;
 
     private final Integer casTenantIdentifier;
@@ -120,22 +112,6 @@ public class Utils {
         return username;
     }
 
-    public UserDto getRealUser(final Authentication authentication) {
-        final String username = getSuperUsername(authentication);
-        final UserDto user;
-        // it's a surrogation, we retrieve him by his email
-        if (StringUtils.isNotBlank(username)) {
-            user = casExternalRestClient.getUserByEmail(buildContext(username), username, Optional.empty());
-        }
-        else {
-            // otherwise, we retrieve him by his identifier
-            final Principal principal = authentication.getPrincipal();
-            final String userId = principal.getId();
-            user = casExternalRestClient.getUserById(buildContext(userId), userId);
-        }
-        return user;
-    }
-
     public Cookie buildIdpCookie(final String value, final TicketGrantingCookieProperties tgc) {
         final Cookie cookie = new Cookie(CommonConstants.IDP_PARAMETER, value);
         cookie.setPath(tgc.getPath());
diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/I18NSendPasswordResetInstructionsAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/I18NSendPasswordResetInstructionsAction.java
index e40a4c64..89b321bb 100644
--- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/I18NSendPasswordResetInstructionsAction.java
+++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/I18NSendPasswordResetInstructionsAction.java
@@ -37,6 +37,7 @@
 package fr.gouv.vitamui.cas.webflow.actions;
 
 import fr.gouv.vitamui.cas.pm.PmMessageToSend;
+import fr.gouv.vitamui.cas.pm.PmTransientSessionTicketExpirationPolicyBuilder;
 import fr.gouv.vitamui.cas.provider.ProvidersService;
 import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.commons.api.logger.VitamUILogger;
@@ -46,23 +47,18 @@ import lombok.val;
 import org.apache.commons.lang3.StringUtils;
 import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
 import org.apereo.cas.configuration.CasConfigurationProperties;
-import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties;
 import org.apereo.cas.pm.PasswordManagementService;
 import org.apereo.cas.pm.web.flow.actions.SendPasswordResetInstructionsAction;
 import org.apereo.cas.ticket.TicketFactory;
 import org.apereo.cas.ticket.registry.TicketRegistry;
 import org.apereo.cas.util.io.CommunicationsManager;
 import org.apereo.cas.web.support.WebUtils;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.context.HierarchicalMessageSource;
 import org.springframework.context.i18n.LocaleContextHolder;
 import org.springframework.webflow.core.collection.MutableAttributeMap;
 import org.springframework.webflow.execution.Event;
 import org.springframework.webflow.execution.RequestContext;
 
-import javax.servlet.http.HttpServletRequest;
-
 /**
  * Send reset password emails with i18n messages.
  *
@@ -73,48 +69,50 @@ public class I18NSendPasswordResetInstructionsAction extends SendPasswordResetIn
     private static final VitamUILogger LOGGER = VitamUILoggerFactory
             .getInstance(I18NSendPasswordResetInstructionsAction.class);
 
-    @Autowired
-    @Qualifier("messageSource")
-    private HierarchicalMessageSource messageSource;
+    private final CasConfigurationProperties casProperties;
 
-    @Autowired
-    private ProvidersService providersService;
+    private final CommunicationsManager communicationsManager;
 
-    @Autowired
-    private IdentityProviderHelper identityProviderHelper;
+    private final PasswordManagementService passwordManagementService;
 
-    @Autowired
-    private Utils utils;
+    private final HierarchicalMessageSource messageSource;
 
-    private final CasConfigurationProperties casProperties;
+    private final ProvidersService providersService;
 
-    private final CommunicationsManager communicationsManager;
+    private final IdentityProviderHelper identityProviderHelper;
 
-    private final PasswordManagementService passwordManagementService;
+    private final Utils utils;
 
     public I18NSendPasswordResetInstructionsAction(final CasConfigurationProperties casProperties,
                                                    final CommunicationsManager communicationsManager,
                                                    final PasswordManagementService passwordManagementService,
                                                    final TicketRegistry ticketRegistry,
-                                                   final TicketFactory ticketFactory) {
+                                                   final TicketFactory ticketFactory,
+                                                   final HierarchicalMessageSource messageSource,
+                                                   final ProvidersService providersService,
+                                                   final IdentityProviderHelper identityProviderHelper,
+                                                   final Utils utils) {
         super(casProperties, communicationsManager, passwordManagementService, ticketRegistry, ticketFactory);
         this.casProperties = casProperties;
         this.communicationsManager = communicationsManager;
         this.passwordManagementService = passwordManagementService;
+        this.messageSource = messageSource;
+        this.providersService = providersService;
+        this.identityProviderHelper = identityProviderHelper;
+        this.utils = utils;
     }
 
     @Override
     protected Event doExecute(final RequestContext requestContext) {
         communicationsManager.validate();
         if (!communicationsManager.isMailSenderDefined()) {
-            LOGGER.warn(
-                    "CAS is unable to send password-reset emails given no settings are defined to account for email servers");
-            return error();
+            return getErrorEvent("contact.failed", "Unable to send email as no mail sender is defined", requestContext);
         }
-        final PasswordManagementProperties pm = casProperties.getAuthn().getPm();
-        final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
-        // JLE: changed from CAS
+
+        val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
+        request.removeAttribute(PmTransientSessionTicketExpirationPolicyBuilder.PM_EXPIRATION_IN_MINUTES_ATTRIBUTE);
         String username = request.getParameter("username");
+        // added from CAS:
         if (StringUtils.isBlank(username)) {
             // try to get the username from the credentials also (after a password expiration)
             final MutableAttributeMap flowScope = requestContext.getFlowScope();
@@ -124,32 +122,35 @@ public class I18NSendPasswordResetInstructionsAction extends SendPasswordResetIn
                 username = usernamePasswordCredential.getUsername();
             }
         }
-
         if (StringUtils.isBlank(username)) {
-            LOGGER.warn("No username is provided");
-            return error();
+            LOGGER.warn("No username parameter is provided");
+            return getErrorEvent("username.required", "No username is provided", requestContext);
         }
 
-        // JLE: changed from CAS
-        final String to = passwordManagementService.findEmail(username);
-        if (StringUtils.isBlank(to)) {
+        // changed from CAS:
+        final String email = passwordManagementService.findEmail(username);
+        if (StringUtils.isBlank(email)) {
             LOGGER.warn("No recipient is provided; nonetheless, we return to the success page");
             return success();
-        } else if (!identityProviderHelper.identifierMatchProviderPattern(providersService.getProviders(), to)) {
-            LOGGER.warn("Recipient: {} is not internal; ignoring and returning to the success page", to);
+        } else if (!identityProviderHelper.identifierMatchProviderPattern(providersService.getProviders(), email)) {
+            LOGGER.warn("Recipient: {} is not internal; ignoring and returning to the success page", email);
             return success();
         }
 
         val service = WebUtils.getService(requestContext);
-        final String url = buildPasswordResetUrl(username, passwordManagementService, casProperties, service);
-
-        LOGGER.debug("Generated password reset URL [{}]; Link is only active for the next [{}] minute(s)",
+        val url = buildPasswordResetUrl(username, passwordManagementService, casProperties, service);
+        if (StringUtils.isNotBlank(url)) {
+            val pm = casProperties.getAuthn().getPm();
+            LOGGER.debug("Generated password reset URL [{}]; Link is only active for the next [{}] minute(s)",
                 utils.sanitizePasswordResetUrl(url), pm.getReset().getExpirationMinutes());
-        if (sendPasswordResetEmailToAccount(to, url)) {
-            return success();
+            if (sendPasswordResetEmailToAccount(email, url)) {
+                return success();
+            }
+        } else {
+            LOGGER.error("No password reset URL could be built and sent to [{}]", email);
         }
-        LOGGER.error("Failed to notify account [{}]", to);
-        return error();
+        LOGGER.error("Failed to notify account [{}]", email);
+        return getErrorEvent("contact.failed", "Failed to send the password reset link to the given email address or phone number", requestContext);
     }
 
     @Override
diff --git a/cas/cas-server/src/main/java/org/apereo/cas/config/pm/RestPasswordManagementConfiguration.java b/cas/cas-server/src/main/java/org/apereo/cas/config/pm/RestPasswordManagementConfiguration.java
deleted file mode 100644
index 1cdfea9d..00000000
--- a/cas/cas-server/src/main/java/org/apereo/cas/config/pm/RestPasswordManagementConfiguration.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package org.apereo.cas.config.pm;
-
-import fr.gouv.vitamui.cas.pm.IamRestPasswordManagementService;
-import fr.gouv.vitamui.cas.provider.ProvidersService;
-import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
-import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
-import org.apereo.cas.configuration.CasConfigurationProperties;
-import org.apereo.cas.pm.PasswordManagementService;
-
-import org.springframework.beans.factory.annotation.Autowired;
-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;
-
-/**
- * Overrides the configuration class from the CAS server, using the CasExternalRestClient.
- *
- *
- */
-@Configuration(value = "restPasswordManagementConfiguration", proxyBeanMethods = false)
-@EnableConfigurationProperties(CasConfigurationProperties.class)
-public class RestPasswordManagementConfiguration {
-    @Autowired
-    private CasConfigurationProperties casProperties;
-
-    // customisation:
-    @Autowired
-    private CasExternalRestClient casExternalRestClient;
-
-    @Autowired
-    private ProvidersService providersService;
-
-    @Autowired
-    private IdentityProviderHelper identityProviderHelper;
-
-    @RefreshScope
-    @Bean
-    public PasswordManagementService passwordChangeService() {
-        return new IamRestPasswordManagementService(casExternalRestClient, casProperties.getAuthn().getPm(), providersService, identityProviderHelper);
-    }
-}
diff --git a/cas/cas-server/src/main/resources/META-INF/spring.factories b/cas/cas-server/src/main/resources/META-INF/spring.factories
index 9edafad4..cfdf9b2a 100644
--- a/cas/cas-server/src/main/resources/META-INF/spring.factories
+++ b/cas/cas-server/src/main/resources/META-INF/spring.factories
@@ -1,5 +1,3 @@
 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
 fr.gouv.vitamui.cas.config.AppConfig,\
-fr.gouv.vitamui.cas.config.WebConfig,\
-fr.gouv.vitamui.cas.config.WebflowConfig,\
-fr.gouv.vitamui.cas.config.PmConfig
+fr.gouv.vitamui.cas.config.WebflowConfig
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/BaseWebflowActionTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/BaseWebflowActionTest.java
similarity index 98%
rename from cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/BaseWebflowActionTest.java
rename to cas/cas-server/src/test/java/fr/gouv/vitamui/cas/BaseWebflowActionTest.java
index 9d414610..c4088a49 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/BaseWebflowActionTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/BaseWebflowActionTest.java
@@ -1,4 +1,4 @@
-package fr.gouv.vitamui.cas.webflow.actions;
+package fr.gouv.vitamui.cas;
 
 import fr.gouv.vitamui.commons.api.identity.ServerIdentityAutoConfiguration;
 import org.apereo.cas.authentication.principal.WebApplicationService;
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateAuthenticationServiceTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateAuthenticationServiceTest.java
index b94dd0ee..30266ba0 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateAuthenticationServiceTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateAuthenticationServiceTest.java
@@ -47,7 +47,7 @@ public final class IamSurrogateAuthenticationServiceTest {
     public void setUp() {
         casExternalRestClient = mock(CasExternalRestClient.class);
 
-        val utils = new Utils(casExternalRestClient, null, 0, null, null);
+        val utils = new Utils(null, 0, null, null);
         service = new IamSurrogateAuthenticationService(casExternalRestClient, mock(ServicesManager.class), utils);
     }
 
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 eb1b350b..40559d59 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
@@ -61,7 +61,7 @@ public final class UserAuthenticationHandlerTest {
     @Before
     public void setUp() {
         casExternalRestClient = mock(CasExternalRestClient.class);
-        val utils = new Utils(casExternalRestClient, null, 0, null, null);
+        val utils = new Utils(null, 0, null, null);
         handler = new UserAuthenticationHandler(null, new DefaultPrincipalFactory(), casExternalRestClient, utils, null);
         credential = new UsernamePasswordCredential(USERNAME, PASSWORD);
     }
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 d006f8e8..e6ac7a15 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
@@ -2,7 +2,7 @@ package fr.gouv.vitamui.cas.authentication;
 
 import fr.gouv.vitamui.cas.util.Constants;
 import fr.gouv.vitamui.cas.util.Utils;
-import fr.gouv.vitamui.cas.webflow.actions.BaseWebflowActionTest;
+import fr.gouv.vitamui.cas.BaseWebflowActionTest;
 import fr.gouv.vitamui.commons.api.CommonConstants;
 import fr.gouv.vitamui.commons.api.domain.AddressDto;
 import fr.gouv.vitamui.commons.api.domain.GroupDto;
@@ -75,7 +75,7 @@ public final class UserPrincipalResolverTest extends BaseWebflowActionTest {
         super.setUp();
 
         casExternalRestClient = mock(CasExternalRestClient.class);
-        val utils = new Utils(casExternalRestClient, null, 0, null, null);
+        val utils = new Utils(null, 0, null, null);
         principalFactory = new DefaultPrincipalFactory();
         sessionStore = mock(SessionStore.class);
         resolver = new UserPrincipalResolver(principalFactory, casExternalRestClient, utils, sessionStore);
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementServiceTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementServiceTest.java
similarity index 81%
rename from cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementServiceTest.java
rename to cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementServiceTest.java
index f609c624..04887a3c 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamRestPasswordManagementServiceTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/pm/IamPasswordManagementServiceTest.java
@@ -1,25 +1,16 @@
 package fr.gouv.vitamui.cas.pm;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import java.time.ZonedDateTime;
 import java.util.*;
 
 import fr.gouv.vitamui.cas.provider.ProvidersService;
 import fr.gouv.vitamui.cas.util.Utils;
+import fr.gouv.vitamui.cas.BaseWebflowActionTest;
 import fr.gouv.vitamui.commons.api.domain.UserDto;
 import fr.gouv.vitamui.commons.api.identity.ServerIdentityAutoConfiguration;
 import fr.gouv.vitamui.iam.common.dto.IdentityProviderDto;
 import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
+import lombok.val;
 import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
 import org.apereo.cas.authentication.DefaultAuthentication;
 import org.apereo.cas.authentication.credential.UsernamePasswordCredential;
@@ -38,24 +29,25 @@ import org.junit.runner.RunWith;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
-import org.springframework.webflow.core.collection.LocalAttributeMap;
-import org.springframework.webflow.core.collection.MutableAttributeMap;
-import org.springframework.webflow.execution.RequestContext;
-import org.springframework.webflow.execution.RequestContextHolder;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.*;
+
 
 /**
- * Tests {@link IamRestPasswordManagementService}.
+ * Tests {@link IamPasswordManagementService}.
  *
  *
  */
 @RunWith(SpringRunner.class)
 @ContextConfiguration(classes = ServerIdentityAutoConfiguration.class)
 @TestPropertySource(locations = "classpath:/application-test.properties")
-public final class IamRestPasswordManagementServiceTest {
+public final class IamPasswordManagementServiceTest extends BaseWebflowActionTest {
 
     private static final String EMAIL = "jerome@test.com";
 
-    private IamRestPasswordManagementService service;
+    private IamPasswordManagementService service;
 
     private CasExternalRestClient casExternalRestClient;
 
@@ -69,20 +61,16 @@ public final class IamRestPasswordManagementServiceTest {
 
     @Before
     public void setUp() {
+        super.setUp();
+
         casExternalRestClient = mock(CasExternalRestClient.class);
         providersService = mock(ProvidersService.class);
         identityProviderHelper = mock(IdentityProviderHelper.class);
         identityProviderDto = new IdentityProviderDto();
         identityProviderDto.setInternal(true);
         when(identityProviderHelper.findByUserIdentifier(any(List.class), eq(EMAIL))).thenReturn(Optional.of(identityProviderDto));
-        service = new IamRestPasswordManagementService(casExternalRestClient, null, providersService, identityProviderHelper);
-        final Utils utils = new Utils(casExternalRestClient, null, 0, null, null);
-        service.setUtils(utils);
-        final RequestContext context = mock(RequestContext.class);
-        RequestContextHolder.setRequestContext(context);
-        final MutableAttributeMap<Object> flowParameters = new LocalAttributeMap<>();
-        when(context.getConversationScope()).thenReturn(flowParameters);
-        when(context.getFlowScope()).thenReturn(flowParameters);
+        val utils = new Utils(null, 0, null, null);
+        service = new IamPasswordManagementService(null, null, null, null, casExternalRestClient, providersService, identityProviderHelper, null, utils, null);
         final Map<String, AuthenticationHandlerExecutionResult> successes = new HashMap<>();
         successes.put("fake", null);
         authAttributes = new HashMap<>();
@@ -188,7 +176,7 @@ public final class IamRestPasswordManagementServiceTest {
     }
 
     private UserDto user(final UserStatusEnum status) {
-        final UserDto user = new UserDto();
+        val user = new UserDto();
         user.setStatus(status);
         user.setEmail(EMAIL);
         return user;
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/provider/ProvidersServiceTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/provider/ProvidersServiceTest.java
index c9762528..558d8381 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/provider/ProvidersServiceTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/provider/ProvidersServiceTest.java
@@ -4,11 +4,11 @@ import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.commons.api.identity.ServerIdentityAutoConfiguration;
 import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext;
 import fr.gouv.vitamui.iam.common.dto.common.ProviderEmbeddedOptions;
-import fr.gouv.vitamui.iam.external.client.CasExternalRestClient;
 import fr.gouv.vitamui.iam.external.client.IdentityProviderExternalRestClient;
 import fr.gouv.vitamui.iam.common.dto.IdentityProviderDto;
 import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
 import fr.gouv.vitamui.iam.common.utils.Saml2ClientBuilder;
+import lombok.val;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -50,14 +50,13 @@ public final class ProvidersServiceTest {
     @Before
     public void setUp() {
         service = new ProvidersService();
-        final Clients clients = new Clients();
+        val clients = new Clients();
         service.setClients(clients);
-        final Saml2ClientBuilder builder = mock(Saml2ClientBuilder.class);
+        val builder = mock(Saml2ClientBuilder.class);
         service.setSaml2ClientBuilder(builder);
         restClient = mock(IdentityProviderExternalRestClient.class);
         service.setIdentityProviderExternalRestClient(restClient);
-        final CasExternalRestClient casExternalRestClient = mock(CasExternalRestClient.class);
-        final Utils utils = new Utils(casExternalRestClient, null, 0, null, null);
+        val utils = new Utils(null, 0, null, null);
         service.setUtils(utils);
 
         provider = new IdentityProviderDto();
@@ -79,10 +78,10 @@ public final class ProvidersServiceTest {
 
         service.loadData();
 
-        final Optional<IdentityProviderDto> missingProvider = identityProviderHelper.findByUserIdentifier(service.getProviders(), "jerome@vitamui.com");
+        val missingProvider = identityProviderHelper.findByUserIdentifier(service.getProviders(), "jerome@vitamui.com");
         assertFalse(missingProvider.isPresent());
 
-        final Optional<IdentityProviderDto> userProvider = identityProviderHelper.findByUserIdentifier(service.getProviders(), "jerome@company.com");
+        val userProvider = identityProviderHelper.findByUserIdentifier(service.getProviders(), "jerome@company.com");
         assertTrue(userProvider.isPresent());
         assertEquals(PROVIDER_ID, userProvider.get().getId());
         assertEquals(saml2Client, ((SamlIdentityProviderDto) userProvider.get()).getSaml2Client());
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomDelegatedClientAuthenticationActionTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomDelegatedClientAuthenticationActionTest.java
index e79ad534..2c515caf 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomDelegatedClientAuthenticationActionTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/CustomDelegatedClientAuthenticationActionTest.java
@@ -5,6 +5,7 @@ import static org.mockito.Mockito.mock;
 
 import java.util.ArrayList;
 
+import fr.gouv.vitamui.cas.BaseWebflowActionTest;
 import fr.gouv.vitamui.cas.provider.ProvidersService;
 import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper;
diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTest.java
index fa96aedb..0e35e391 100644
--- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTest.java
+++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/DispatcherActionTest.java
@@ -9,6 +9,7 @@ import java.io.IOException;
 import java.util.LinkedList;
 import java.util.Optional;
 
+import fr.gouv.vitamui.cas.BaseWebflowActionTest;
 import fr.gouv.vitamui.cas.util.Utils;
 import fr.gouv.vitamui.commons.api.exception.InvalidFormatException;
 import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext;
@@ -65,7 +66,7 @@ public final class DispatcherActionTest extends BaseWebflowActionTest {
         identityProviderHelper = mock(IdentityProviderHelper.class);
         casExternalRestClient = mock(CasExternalRestClient.class);
 
-        final Utils utils = new Utils(casExternalRestClient, null, 0, null, null);
+        final Utils utils = new Utils(null, 0, null, null);
         action = new DispatcherAction(providersService, identityProviderHelper, casExternalRestClient, ",", utils, mock(SessionStore.class));
 
         final SAML2Client client = new SAML2Client();
-- 
GitLab