From 7d4ad708e0f9775f950f98eea260595f2c0b9921 Mon Sep 17 00:00:00 2001 From: Lotfi GHERIBI Date: Thu, 21 Mar 2024 17:33:36 +0100 Subject: [PATCH] Feature/Vas-12560: Multi-domain authentication - External Idp support (experimental) --- .../authentication/UserPrincipalResolver.java | 60 +++++++++++-------- .../cas/webflow/actions/DispatcherAction.java | 39 +++++++----- 2 files changed, 58 insertions(+), 41 deletions(-) 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 4e5901fc041..dc916128682 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 @@ -69,6 +69,8 @@ import org.pac4j.core.context.session.SessionStore; import org.pac4j.core.util.CommonHelper; import org.pac4j.jee.context.JEEContext; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.webflow.execution.RequestContextHolder; import java.security.cert.CertificateParsingException; @@ -126,7 +128,8 @@ @RequiredArgsConstructor public class UserPrincipalResolver implements PrincipalResolver { - public static final String EMAIL_VALID_REGEXP = "^[_a-z0-9]+(((\\.|-)[_a-z0-9]+))*@[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,})$"; + public static final String EMAIL_VALID_REGEXP = + "^[_a-z0-9]+(((\\.|-)[_a-z0-9]+))*@[a-z0-9-]+(\\.[a-z0-9-]+)*(\\.[a-z]{2,})$"; public static final String SUPER_USER_ID_ATTRIBUTE = "superUserId"; public static final String COMPUTED_OTP = "computedOtp"; @@ -151,7 +154,8 @@ public class UserPrincipalResolver implements PrincipalResolver { private final String x509DefaultDomain; @Override - public Principal resolve(final Credential credential, final Optional optPrincipal, final Optional handler) { + public Principal resolve(final Credential credential, final Optional optPrincipal, + final Optional handler) { // OAuth 2 authorization code flow (client credentials authentication) if (optPrincipal.isEmpty()) { @@ -169,7 +173,6 @@ public Principal resolve(final Credential credential, final Optional String superUserCustomerId; String username; - final String superUsername; String userProviderId; final Optional technicalUserId; // x509 certificate @@ -182,7 +185,6 @@ public Principal resolve(final Credential credential, final Optional } catch (final CertificateParsingException e) { throw new RuntimeException(e.getMessage()); } - superUsername = null; userProviderId = null; surrogationCall = false; @@ -205,10 +207,6 @@ public Principal resolve(final Credential credential, final Optional userProviderId = userProvider.get().getId(); } } else if (credential instanceof SurrogateUsernamePasswordCredential) { - // login/password + surrogation - val surrogationCredential = (SurrogateUsernamePasswordCredential) credential; - username = surrogationCredential.getSurrogateUsername(); - superUsername = surrogationCredential.getUsername(); userProviderId = null; technicalUserId = Optional.empty(); @@ -220,8 +218,6 @@ public Principal resolve(final Credential credential, final Optional } else if (credential instanceof UsernamePasswordCredential) { // login/password - username = principalId; - superUsername = null; userProviderId = null; technicalUserId = Optional.empty(); @@ -233,12 +229,6 @@ public Principal resolve(final Credential credential, final Optional } else { - // FIXME: - loginEmail = null; - loginCustomerId = null; - superUserEmail = null; - superUserCustomerId = null; - // authentication delegation (+ surrogation) val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext); val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext); @@ -251,7 +241,7 @@ public Principal resolve(final Credential credential, final Optional String email = principalId; if (CommonHelper.isNotBlank(mailAttribute)) { val mails = principal.getAttributes().get(mailAttribute); - if (mails == null || mails.size() == 0 || CommonHelper.isBlank((String) mails.get(0))) { + if (CollectionUtils.isEmpty(mails) || CommonHelper.isBlank((String) mails.get(0))) { LOGGER.error( "Provider: '{}' requested specific mail attribute: '{}' for id, but attribute does not exist or has no value", providerName, mailAttribute); @@ -268,7 +258,7 @@ public Principal resolve(final Credential credential, final Optional String identifier = principalId; if (CommonHelper.isNotBlank(identifierAttribute)) { val identifiers = principal.getAttributes().get(identifierAttribute); - if (identifiers == null || identifiers.size() == 0 || + if (CollectionUtils.isEmpty(identifiers) || CommonHelper.isBlank((String) identifiers.get(0))) { LOGGER.error( "Provider: '{}' requested specific identifier attribute: '{}' for id, but attribute does not exist or has no value", @@ -282,20 +272,38 @@ public Principal resolve(final Credential credential, final Optional identifier = identifierAttr; } } - // FIXME - val surrogateInSession = sessionStore.get(webContext, Constants.SURROGATE).orElse(null); - if (surrogateInSession != null) { - username = (String) surrogateInSession; - superUsername = email; + + String surrogateEmailFromSession = + (String) sessionStore.get(webContext, Constants.FLOW_SURROGATE_EMAIL).orElse(null); + String surrogateCustomerIdFromSession = + (String) sessionStore.get(webContext, Constants.FLOW_SURROGATE_CUSTOMER_ID).orElse(null); + String loginEmailFromSession = + (String) sessionStore.get(webContext, Constants.FLOW_LOGIN_EMAIL).orElseThrow(); + String loginCustomerIdFromSession = + (String) sessionStore.get(webContext, Constants.FLOW_LOGIN_CUSTOMER_ID).orElseThrow(); + + Assert.isTrue(email.equals(loginEmailFromSession), + String.format("Invalid user from Idp : Expected: '%s', actual: '%s'", loginEmailFromSession, email)); + + if (surrogateEmailFromSession != null) { userProviderId = null; technicalUserId = Optional.empty(); surrogationCall = true; + + loginEmail = surrogateEmailFromSession; + loginCustomerId = surrogateCustomerIdFromSession; + superUserEmail = loginEmailFromSession; + superUserCustomerId = loginCustomerIdFromSession; + } else { - username = email; - superUsername = null; userProviderId = provider.getId(); technicalUserId = Optional.of(identifier); surrogationCall = false; + + loginEmail = loginEmailFromSession; + loginCustomerId = loginCustomerIdFromSession; + superUserEmail = null; + superUserCustomerId = null; } } @@ -382,7 +390,7 @@ public Principal resolve(final Credential credential, final Optional final Set roles = new HashSet<>(); final List profiles = authUser.getProfileGroup().getProfiles(); profiles.forEach(profile -> profile.getRoles().forEach(role -> roles.add(role.getName()))); - attributes.put(ROLES_ATTRIBUTE, new ArrayList(roles)); + attributes.put(ROLES_ATTRIBUTE, new ArrayList<>(roles)); } val createdPrincipal = principalFactory.createPrincipal(user.getId(), attributes); if (surrogationCall) { 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 2774c6676a2..554a9679065 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 @@ -45,10 +45,11 @@ 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.RequiredArgsConstructor; import lombok.val; +import org.apereo.cas.web.support.WebUtils; import org.pac4j.core.context.session.SessionStore; +import org.pac4j.jee.context.JEEContext; import org.springframework.webflow.action.AbstractAction; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.execution.Event; @@ -109,7 +110,7 @@ private Event processSubrogationRequest(RequestContext requestContext, MutableAt ParameterChecker.checkParameter("Missing subrogation params", surrogateEmail, surrogateCustomerId, superUserEmail, superUserCustomerId); - return dispatchUser(requestContext, superUserEmail, superUserCustomerId); + return dispatchUser(requestContext, superUserEmail, superUserCustomerId, surrogateEmail, surrogateCustomerId); } private Event processLoginRequest(RequestContext requestContext, MutableAttributeMap flowScope) @@ -122,35 +123,43 @@ private Event processLoginRequest(RequestContext requestContext, MutableAttribut ParameterChecker.checkParameter("Missing authn params", userEmail, customerId); - return dispatchUser(requestContext, userEmail, customerId); + return dispatchUser(requestContext, userEmail, customerId, null, null); } - private Event dispatchUser(RequestContext requestContext, String username, - String customerId) throws IOException { + private Event dispatchUser(RequestContext requestContext, String loginEmail, + String loginCustomerId, String surrogateEmail, String surrogateCustomerId) throws IOException { Optional providerOpt = - identityProviderHelper.findByCustomerId(providersService.getProviders(), customerId); + identityProviderHelper.findByCustomerId(providersService.getProviders(), loginCustomerId); if (providerOpt.isEmpty()) { - LOGGER.error("No provider found for superUserCustomerId: {}", customerId); + LOGGER.error("No provider found for superUserCustomerId: {}", loginCustomerId); return new Event(this, BAD_CONFIGURATION); } var identityProviderDto = providerOpt.get(); + val request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext); + val response = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext); + val webContext = new JEEContext(request, response); + if (identityProviderDto.getInternal()) { - // FIXME : - // sessionStore.set(webContext, Constants.SURROGATE, null); + sessionStore.set(webContext, Constants.FLOW_LOGIN_EMAIL, null); + sessionStore.set(webContext, Constants.FLOW_LOGIN_CUSTOMER_ID, null); + sessionStore.set(webContext, Constants.FLOW_SURROGATE_EMAIL, null); + sessionStore.set(webContext, Constants.FLOW_SURROGATE_CUSTOMER_ID, null); + LOGGER.debug("Redirect the user to the password page..."); return success(); } else { - // FIXME : - // // save the surrogate in the session to be retrieved by the UserPrincipalResolver and DelegatedSurrogateAuthenticationPostProcessor - // if (surrogate != null) { - // LOGGER.debug("Saving surrogate for after authentication delegation: {}", surrogate); - // sessionStore.set(webContext, Constants.SURROGATE, surrogate); - // } + LOGGER.debug("Saving surrogate for after authentication delegation: loginEmail : {}, " + + "loginCustomerId : {}, surrogateEmail : {}, surrogateCustomerId : {}", + loginEmail, loginCustomerId, surrogateEmail, surrogateCustomerId); + sessionStore.set(webContext, Constants.FLOW_LOGIN_EMAIL, loginEmail); + sessionStore.set(webContext, Constants.FLOW_LOGIN_CUSTOMER_ID, loginCustomerId); + sessionStore.set(webContext, Constants.FLOW_SURROGATE_EMAIL, surrogateEmail); + sessionStore.set(webContext, Constants.FLOW_SURROGATE_CUSTOMER_ID, surrogateCustomerId); return utils.performClientRedirection(this, ((Pac4jClientIdentityProviderDto) identityProviderDto).getClient(), requestContext);