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 49c2b2709086ba331b5908d0084c9467be56b2c5..574a7bc4427339951750e9a14b396fa2b0104ce3 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 b7148fc778c9566503e21230535d6ae5581a75cf..1783a57faa7da117b37db35f09937443d465b34c 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 bd7f4d2374b92eb535336c1c466a2cedfa841ed9..d5921efe6dea388b94a0864d4e3a4ad63e0ddcf1 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 771f24b9fdf0454baa7bd4d53cc915ae1d5a7c2e..18cf370d8c7a602b64f7c61aa31924d3e28915e2 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 2fa2c1fa6b3b3c949474a69d9e87c77d80ab51e6..3dc60b13b08a524c9e4de82a6b6f136dd7249d66 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 65e581b29dee3dd427eb2448d33e6154ff36a8e4..da2a71cfdb7a171af4b198edea5c48e351cd9d2a 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 6cfab56e99005244e2d11daf3e992c6cd496f25f..647e615fb8e525d9cb62e37800bd253c29347619 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 48ae7640a6285bcb27795a0b446cbb97dfca9480..77b5f66152642146007fa2300430ba20327c7603 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 9d658b5eabc3941d2ddd6e69bd537d136db64c81..cb481940860c6856b81dd29d4e2f136fe7370781 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 d1ee36d49f51ff2b2dbae3bdaceb12e63a101f85..7ff4bacf6f885b7cd137cfe143e8cea234b0b830 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 f215afa620165993369de0e92cbb6c616cb2e6a5..0000000000000000000000000000000000000000 --- 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 3d4ddfb4153e111d3f928a97d0e8464bbd188da0..0000000000000000000000000000000000000000 --- 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 50a97d17627077e7ed02211606a4d4b64ff07f04..bd685858128d59b89749ee6841855e21a9d34b7b 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 f9c154775ec75c900a8c4da0755a99c84dec852a..b130c464bb0fb706697ce35b49e85ded8bb42bb6 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 4acfb606fb7c77db75557fdd50292efaa7824ac8..174314ad6be8c676651bbb12e3fa4ffec00e3209 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 529888669dbbb098b79ebfe3fca0072e59bccfb2..b96f58564b97bb2f1e62f04e04440eea9729887a 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);