diff --git a/api/api-iam/iam-commons/src/main/java/fr/gouv/vitamui/iam/common/utils/Saml2ClientBuilder.java b/api/api-iam/iam-commons/src/main/java/fr/gouv/vitamui/iam/common/utils/Saml2ClientBuilder.java index 5bf95768426961b0f73e69490ba867d12535f2ee..1bd8e94f1449caec01709b763b2bd57fa32a1cfb 100644 --- a/api/api-iam/iam-commons/src/main/java/fr/gouv/vitamui/iam/common/utils/Saml2ClientBuilder.java +++ b/api/api-iam/iam-commons/src/main/java/fr/gouv/vitamui/iam/common/utils/Saml2ClientBuilder.java @@ -42,9 +42,9 @@ import java.util.Optional; import javax.validation.constraints.NotNull; import org.apache.commons.lang3.StringUtils; -import org.pac4j.core.context.Pac4jConstants; import org.pac4j.core.exception.TechnicalException; import org.pac4j.core.util.CommonHelper; +import org.pac4j.core.util.Pac4jConstants; import org.pac4j.saml.client.SAML2Client; import org.pac4j.saml.config.SAML2Configuration; import org.springframework.beans.factory.annotation.Value; diff --git a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ApplicationExternalServiceTest.java b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ApplicationExternalServiceTest.java index 9026f56cdadb9859b3cfaec0ba567b3d44b7e46a..63fe393afbae306ddb7123d75223ba54ed36d54c 100644 --- a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ApplicationExternalServiceTest.java +++ b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ApplicationExternalServiceTest.java @@ -4,7 +4,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import org.apache.commons.collections.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -44,7 +43,7 @@ public class ApplicationExternalServiceTest { user.setLevel(""); user.setCustomerId(userCustomerId); final List<String> roles = Arrays.asList(userRoles); - if (CollectionUtils.isNotEmpty(roles)) { + if (roles != null && roles.size() > 0) { roles.forEach(r -> Mockito.when(externalSecurityService.hasRole(r)).thenReturn(true)); } Mockito.when(externalSecurityService.userIsRootLevel()).thenReturn(true); diff --git a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ProfileExternalServiceTest.java b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ProfileExternalServiceTest.java index 961905de9588e2003426054f15296b7748bed9d5..ec3f330bbf80377fa96b08d3d19e5b9bbef478b9 100644 --- a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ProfileExternalServiceTest.java +++ b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/ProfileExternalServiceTest.java @@ -7,7 +7,6 @@ import java.util.Collection; import java.util.List; import java.util.Optional; -import org.apache.commons.collections.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -69,7 +68,7 @@ public class ProfileExternalServiceTest { user.setLevel(""); user.setCustomerId(userCustomerId); final List<String> roles = Arrays.asList(userRoles); - if (CollectionUtils.isNotEmpty(roles)) { + if (roles != null && roles.size() > 0) { roles.forEach(r -> Mockito.when(externalSecurityService.hasRole(r)).thenReturn(true)); } Mockito.when(externalSecurityService.userIsRootLevel()).thenReturn(true); diff --git a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/TenantExternalServiceTest.java b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/TenantExternalServiceTest.java index 369d16a153bea2c2a32e022fd8ff03c1b6f1d8f6..2d6b0ba6d6baf0a33938e19330c1860e3d6daafa 100644 --- a/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/TenantExternalServiceTest.java +++ b/api/api-iam/iam-external/src/test/java/fr/gouv/vitamui/iam/external/server/service/TenantExternalServiceTest.java @@ -6,7 +6,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; -import org.apache.commons.collections.CollectionUtils; import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; @@ -68,7 +67,7 @@ public class TenantExternalServiceTest { user.setLevel(""); user.setCustomerId(userCustomerId); final List<String> roles = Arrays.asList(userRoles); - if (CollectionUtils.isNotEmpty(roles)) { + if (roles != null && roles.size() > 0) { roles.forEach(r -> Mockito.when(externalSecurityService.hasRole(r)).thenReturn(true)); } Mockito.when(externalSecurityService.getCustomerId()).thenReturn(userCustomerId); diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java index 41a794be3903e66b905144358b2ea62d466df3bb..5b08157b1ae68fb90b76050a1b48c39f413234ca 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/customer/service/CustomerInternalService.java @@ -46,7 +46,6 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -409,7 +408,7 @@ public class CustomerInternalService extends VitamUICrudService<CustomerDto, Cus } private void checkEmailDomains(final List<String> emailDomains, final String message) { - Assert.isTrue(CollectionUtils.isNotEmpty(emailDomains), message + ": a customer must have emails domains."); + Assert.isTrue(emailDomains != null && emailDomains.size() > 0, message + ": a customer must have emails domains."); for (final String domain : emailDomains) { Assert.isTrue(StringUtils.isNoneBlank(domain), message + ": an email domain is empty"); @@ -419,7 +418,7 @@ public class CustomerInternalService extends VitamUICrudService<CustomerDto, Cus } private void checkEmailDomains(final List<String> emailDomains, final String customerId, final String message) { - Assert.isTrue(CollectionUtils.isNotEmpty(emailDomains), message + ": a customer must have emails domains."); + Assert.isTrue(emailDomains != null && emailDomains.size() > 0, message + ": a customer must have emails domains."); for (final String domain : emailDomains) { Assert.isTrue(StringUtils.isNoneBlank(domain), message + ": an email domain is empty"); @@ -431,7 +430,7 @@ public class CustomerInternalService extends VitamUICrudService<CustomerDto, Cus } private void checkOwners(final List<OwnerDto> owners, final String message) { - Assert.isTrue(CollectionUtils.isNotEmpty(owners), message + ": a customer must have owners."); + Assert.isTrue(owners != null && owners.size() > 0, message + ": a customer must have owners."); } public JsonNode findHistoryById(final String id) throws VitamClientException { diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/converter/IdentityProviderConverter.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/converter/IdentityProviderConverter.java index 68086ebfc1cef5fb7f013352b6d7c88906f5cd8f..782f7d936a8336adfa49276a24e656594dd1f83d 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/converter/IdentityProviderConverter.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/converter/IdentityProviderConverter.java @@ -40,7 +40,6 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.stream.Collectors; -import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import fr.gouv.vitamui.commons.api.converter.Converter; @@ -119,7 +118,7 @@ public class IdentityProviderConverter implements Converter<IdentityProviderDto, } private void convertPatterns(final IdentityProviderDto dto, final IdentityProvider provider) { - if (CollectionUtils.isNotEmpty(dto.getPatterns())) { + if (dto.getPatterns() != null && dto.getPatterns().size() > 0) { dto.setPatterns(dto.getPatterns().stream().map(s -> s.startsWith(".*@") ? s : ".*@" + s).collect(Collectors.toList())); } provider.setPatterns(dto.getPatterns()); diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/service/IdentityProviderInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/service/IdentityProviderInternalService.java index c186f62f6a3b5604444fba0c7220b415b9e61679..93bf7e041603856573920b36abcb45469700b8cb 100644 --- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/service/IdentityProviderInternalService.java +++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/idp/service/IdentityProviderInternalService.java @@ -36,16 +36,10 @@ */ package fr.gouv.vitamui.iam.internal.server.idp.service; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Optional; import java.util.stream.Collectors; -import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Example; @@ -364,7 +358,7 @@ public class IdentityProviderInternalService extends VitamUICrudService<Identity public List<String> getDomainsNotAssigned(final String customerId) { final List<String> filterDomains = new ArrayList<>(); final List<IdentityProvider> idp = identityProviderRepository.findAll(Criteria.where("customerId").is(customerId)); - if (CollectionUtils.isNotEmpty(idp)) { + if (idp != null && idp.size() > 0) { for (final IdentityProvider i : idp) { filterDomains.addAll(i.getPatterns().stream().map(s -> s.replace(".*@", "")).collect(Collectors.toList())); } @@ -373,7 +367,7 @@ public class IdentityProviderInternalService extends VitamUICrudService<Identity final Customer customer = customerRepository.findById(customerId) .orElseThrow(() -> new IllegalArgumentException("no customer found for " + customerId)); List<String> availablesDomains = customer.getEmailDomains(); - if (CollectionUtils.isNotEmpty(customer.getEmailDomains())) { + if (idp != null && idp.size() > 0) { availablesDomains = availablesDomains.stream().filter(s -> !filterDomains.contains(s)).collect(Collectors.toList()); } return availablesDomains; diff --git a/cas/cas-server/pom.xml b/cas/cas-server/pom.xml index d2907410b744009123542ee44c4c76656b847816..4d23a2eeddc11ebd15e05c2afa15a5912cf8c066 100644 --- a/cas/cas-server/pom.xml +++ b/cas/cas-server/pom.xml @@ -11,18 +11,19 @@ <name>VITAMUI CAS Server</name> <properties> - <groovy.version>2.4.15</groovy.version> - <cas.hibernate.validator.version>5.4.1.Final</cas.hibernate.validator.version> + <!--groovy.version>2.4.15</groovy.version> + <spring.aop.version>4.3.20.RELEASE</spring.aop.version> <spring.boot.version>1.5.18.RELEASE</spring.boot.version> <spring.cloud.consul.version>1.3.0.RELEASE</spring.cloud.consul.version> <spring.oxm.version>4.3.20.RELEASE</spring.oxm.version> <spring.security.version>4.2.8.RELEASE</spring.security.version> - <spring.test.version>${spring.oxm.version}</spring.test.version> - <spring.version>4.3.20.RELEASE</spring.version> + <spring.version>4.3.20.RELEASE</spring.version--> + <cas.hibernate.validator.version>6.1.0.Final</cas.hibernate.validator.version> + <spring.test.version>5.2.0.RELEASE</spring.test.version> <swagger.version>1.5.18</swagger.version> - <thymeleaf-spring4.version>3.0.9.RELEASE</thymeleaf-spring4.version> - <micrometer.version>1.0.1</micrometer.version> + <thymeleaf-spring5.version>3.0.11.RELEASE</thymeleaf-spring5.version> + <!--micrometer.version>1.0.1</micrometer.version--> <assertj-core.version>3.11.1</assertj-core.version> <rpm.skip>false</rpm.skip> @@ -309,6 +310,17 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apereo.cas</groupId> + <artifactId>cas-server-support-pm-core</artifactId> + <version>${cas.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + </exclusion> + </exclusions> + </dependency> <!-- multi-factor authentication --> <dependency> @@ -495,6 +507,39 @@ </exclusion> </exclusions> </dependency> + <dependency> + <groupId>org.apereo.cas</groupId> + <artifactId>cas-server-support-token-core-api</artifactId> + <version>${cas.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apereo.cas</groupId> + <artifactId>cas-server-support-oauth-core-api</artifactId> + <version>${cas.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>org.apereo.cas</groupId> + <artifactId>cas-server-support-oauth-services</artifactId> + <version>${cas.version}</version> + <exclusions> + <exclusion> + <groupId>org.apache.logging.log4j</groupId> + <artifactId>log4j-slf4j-impl</artifactId> + </exclusion> + </exclusions> + </dependency> <!-- metrics / logs --> <dependency> @@ -555,15 +600,7 @@ </exclusions> </dependency> - <!-- Spring overrides --> - <dependency> - <groupId>org.springframework</groupId> - <artifactId>spring-beans</artifactId> - <version>${spring.version}</version> - </dependency> - <!-- UTIL --> - <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> @@ -587,8 +624,8 @@ </dependency> <dependency> <groupId>org.thymeleaf</groupId> - <artifactId>thymeleaf-spring4</artifactId> - <version>${thymeleaf-spring4.version}</version> + <artifactId>thymeleaf-spring5</artifactId> + <version>${thymeleaf-spring5.version}</version> </dependency> <dependency> @@ -697,6 +734,14 @@ <outputStyle/> </configuration> </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <release>11</release> + </configuration> + </plugin> </plugins> </build> 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 8413ec39ce337b837be8226513757f1f450e73ba..100a91ddf6cefb1617a9f05cca878c2a07f4dcaa 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 @@ -90,7 +90,7 @@ spring.mail.properties.mail.smtp.auth: true spring.mail.properties.mail.smtp.starttls.enable: true -cas.authn.mfa.globalProviderId: mfa-sms +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 bb139a7369b659f1be39d63a161c47a202319bb2..5c1ae30b4c3beb91a4cd79eeda24598013aa0b3f 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 @@ -91,7 +91,7 @@ spring.mail.properties.mail.smtp.auth: true spring.mail.properties.mail.smtp.starttls.enable: true -cas.authn.mfa.globalProviderId: mfa-sms +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/DelegatedSurrogateAuthenticationPostProcessor.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/DelegatedSurrogateAuthenticationPostProcessor.java index b6092d09c486dde45dfc3ab40d8eecc6f77685cc..9f7f5db104730ab9cdf3d28993c56f96800f7ac1 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/DelegatedSurrogateAuthenticationPostProcessor.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/authentication/DelegatedSurrogateAuthenticationPostProcessor.java @@ -65,7 +65,7 @@ public class DelegatedSurrogateAuthenticationPostProcessor extends SurrogateAuth final AuditableExecution registeredServiceAccessStrategyEnforcer, final AuditableExecution surrogateEligibilityAuditableExecution) { super(surrogateAuthenticationService, servicesManager, applicationEventPublisher, registeredServiceAccessStrategyEnforcer, - surrogateEligibilityAuditableExecution, null); + surrogateEligibilityAuditableExecution); } @Override 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 67eb0a784c024b0e2b4052433ce0183e97f4e171..bd7f4d2374b92eb535336c1c466a2cedfa841ed9 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 @@ -45,7 +45,9 @@ 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; /** @@ -67,11 +69,11 @@ public class SurrogatedUserPrincipalFactory implements PrincipalFactory { } @Override - public Principal createPrincipal(final String id, final Map<String, Object> attributes) { + public Principal createPrincipal(final String id, final Map<String, List<Object>> attributes) { LOGGER.debug("Creating username: {}", id); try { - Map<String, Object> surrogateAttribute = new HashMap<>(); - surrogateAttribute.put(SUPER_USER_ATTRIBUTE, ""); + Map<String, List<Object>> surrogateAttribute = new HashMap<>(); + surrogateAttribute.put(SUPER_USER_ATTRIBUTE, Collections.emptyList()); final Principal principal = resolver.resolve(id, surrogateAttribute); 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 75f560edd5674591f84469485fbb4477c1ea1f18..771f24b9fdf0454baa7bd4d53cc915ae1d5a7c2e 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 @@ -51,6 +51,7 @@ import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; import fr.gouv.vitamui.commons.api.enums.UserTypeEnum; import org.apereo.cas.authentication.*; +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.handler.support.AbstractUsernamePasswordAuthenticationHandler; 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 3321999f244b6e41e840885ba29934cb0f4b7bbe..2fa2c1fa6b3b3c949474a69d9e87c77d80ab51e6 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,20 +69,14 @@ import static fr.gouv.vitamui.commons.api.CommonConstants.TENANTS_BY_APP_ATTRIBU import static fr.gouv.vitamui.commons.api.CommonConstants.TYPE_ATTRIBUTE; import static fr.gouv.vitamui.commons.api.CommonConstants.USER_ID_ATTRIBUTE; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import javax.servlet.http.HttpServletRequest; import org.apereo.cas.authentication.AuthenticationHandler; import org.apereo.cas.authentication.Credential; import org.apereo.cas.authentication.SurrogateUsernamePasswordCredential; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.authentication.principal.ClientCredential; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.authentication.principal.PrincipalFactory; @@ -132,7 +126,7 @@ public class UserPrincipalResolver implements PrincipalResolver { return resolve(principal.get().getId(), principal.get().getAttributes()); } - public Principal resolve(final String username, final Map<String, Object> oldAttributes) { + public Principal resolve(final String username, final Map<String, List<Object>> oldAttributes) { String superUsername = null; final RequestContext requestContext = RequestContextHolder.getRequestContext(); if (requestContext != null) { @@ -188,41 +182,41 @@ public class UserPrincipalResolver implements PrincipalResolver { LOGGER.debug("User cannot login: {} - User {}", username, user.toString()); return null; } - final Map<String, Object> attributes = new HashMap<>(); - attributes.put(USER_ID_ATTRIBUTE, user.getId()); - attributes.put(CUSTOMER_ID_ATTRIBUTE, user.getCustomerId()); - attributes.put(EMAIL_ATTRIBUTE, username); - attributes.put(FIRSTNAME_ATTRIBUTE, user.getFirstname()); - attributes.put(LASTNAME_ATTRIBUTE, user.getLastname()); - attributes.put(IDENTIFIER_ATTRIBUTE, user.getIdentifier()); - attributes.put(OTP_ATTRIBUTE, user.isOtp()); - attributes.put(SUBROGEABLE_ATTRIBUTE, user.isSubrogeable()); - attributes.put(LANGUAGE_ATTRIBUTE, user.getLanguage()); - attributes.put(PHONE_ATTRIBUTE, user.getPhone()); - attributes.put(MOBILE_ATTRIBUTE, user.getMobile()); - attributes.put(STATUS_ATTRIBUTE, user.getStatus()); - attributes.put(TYPE_ATTRIBUTE, user.getType()); - attributes.put(READONLY_ATTRIBUTE, user.isReadonly()); - attributes.put(LEVEL_ATTRIBUTE, user.getLevel()); - attributes.put(LAST_CONNECTION_ATTRIBUTE, user.getLastConnection()); - attributes.put(NB_FAILED_ATTEMPTS_ATTRIBUTE, user.getNbFailedAttempts()); - attributes.put(PASSWORD_EXPIRATION_DATE_ATTRIBUTE, user.getPasswordExpirationDate()); - attributes.put(GROUP_ID_ATTRIBUTE, user.getGroupId()); - attributes.put(ADDRESS_ATTRIBUTE, new CasJsonWrapper(user.getAddress())); + final Map<String, List<Object>> attributes = new HashMap<>(); + attributes.put(USER_ID_ATTRIBUTE, Collections.singletonList(user.getId())); + attributes.put(CUSTOMER_ID_ATTRIBUTE, Collections.singletonList(user.getCustomerId())); + attributes.put(EMAIL_ATTRIBUTE, Collections.singletonList(username)); + attributes.put(FIRSTNAME_ATTRIBUTE, Collections.singletonList(user.getFirstname())); + attributes.put(LASTNAME_ATTRIBUTE, Collections.singletonList(user.getLastname())); + attributes.put(IDENTIFIER_ATTRIBUTE, Collections.singletonList(user.getIdentifier())); + attributes.put(OTP_ATTRIBUTE, Collections.singletonList(user.isOtp())); + attributes.put(SUBROGEABLE_ATTRIBUTE, Collections.singletonList(user.isSubrogeable())); + attributes.put(LANGUAGE_ATTRIBUTE, Collections.singletonList(user.getLanguage())); + attributes.put(PHONE_ATTRIBUTE, Collections.singletonList(user.getPhone())); + attributes.put(MOBILE_ATTRIBUTE, Collections.singletonList(user.getMobile())); + attributes.put(STATUS_ATTRIBUTE, Collections.singletonList(user.getStatus())); + attributes.put(TYPE_ATTRIBUTE, Collections.singletonList(user.getType())); + attributes.put(READONLY_ATTRIBUTE, Collections.singletonList(user.isReadonly())); + attributes.put(LEVEL_ATTRIBUTE, Collections.singletonList(user.getLevel())); + attributes.put(LAST_CONNECTION_ATTRIBUTE, Collections.singletonList(user.getLastConnection())); + attributes.put(NB_FAILED_ATTEMPTS_ATTRIBUTE, Collections.singletonList(user.getNbFailedAttempts())); + attributes.put(PASSWORD_EXPIRATION_DATE_ATTRIBUTE, Collections.singletonList(user.getPasswordExpirationDate())); + attributes.put(GROUP_ID_ATTRIBUTE, Collections.singletonList(user.getGroupId())); + attributes.put(ADDRESS_ATTRIBUTE, Collections.singletonList(new CasJsonWrapper(user.getAddress()))); if (finalSurrogationCall) { - attributes.put(SUPER_USER_ATTRIBUTE, superUsername); + attributes.put(SUPER_USER_ATTRIBUTE, Collections.singletonList(superUsername)); final UserDto superUser = casExternalRestClient.getUserByEmail(utils.buildContext(superUsername), superUsername, Optional.empty()); - attributes.put(SUPER_USER_IDENTIFIER_ATTRIBUTE, superUser.getIdentifier()); + attributes.put(SUPER_USER_IDENTIFIER_ATTRIBUTE, Collections.singletonList(superUser.getIdentifier())); } if (user instanceof AuthUserDto) { final AuthUserDto authUser = (AuthUserDto) user; - attributes.put(PROFILE_GROUP_ATTRIBUTE, new CasJsonWrapper(authUser.getProfileGroup())); - attributes.put(CUSTOMER_IDENTIFIER_ATTRIBUTE, authUser.getCustomerIdentifier()); - attributes.put(BASIC_CUSTOMER_ATTRIBUTE, new CasJsonWrapper(authUser.getBasicCustomer())); - attributes.put(AUTHTOKEN_ATTRIBUTE, authUser.getAuthToken()); - attributes.put(PROOF_TENANT_ID_ATTRIBUTE, authUser.getProofTenantIdentifier()); - attributes.put(TENANTS_BY_APP_ATTRIBUTE, new CasJsonWrapper(authUser.getTenantsByApp())); + attributes.put(PROFILE_GROUP_ATTRIBUTE, Collections.singletonList(new CasJsonWrapper(authUser.getProfileGroup()))); + attributes.put(CUSTOMER_IDENTIFIER_ATTRIBUTE, Collections.singletonList(authUser.getCustomerIdentifier())); + attributes.put(BASIC_CUSTOMER_ATTRIBUTE, Collections.singletonList(new CasJsonWrapper(authUser.getBasicCustomer()))); + attributes.put(AUTHTOKEN_ATTRIBUTE, Collections.singletonList(authUser.getAuthToken())); + attributes.put(PROOF_TENANT_ID_ATTRIBUTE, Collections.singletonList(authUser.getProofTenantIdentifier())); + attributes.put(TENANTS_BY_APP_ATTRIBUTE, Collections.singletonList(new CasJsonWrapper(authUser.getTenantsByApp()))); final Set<String> roles = new HashSet<>(); final List<ProfileDto> profiles = authUser.getProfileGroup().getProfiles(); profiles.forEach(profile -> profile.getRoles().forEach(role -> roles.add(role.getName()))); 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 22f81cf64fced93b56735aacf5902055d0a0bef2..a01aac5423e2caa45dca2e8127e14ee3de4b135a 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 @@ -36,7 +36,6 @@ */ package fr.gouv.vitamui.cas.config; -import org.apereo.cas.CipherExecutor; import org.apereo.cas.audit.AuditableExecution; import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer; import org.apereo.cas.authentication.AuthenticationHandler; @@ -45,20 +44,13 @@ import org.apereo.cas.authentication.AuthenticationPostProcessor; import org.apereo.cas.authentication.principal.PrincipalFactory; import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService; import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.configuration.model.support.oauth.OAuthAccessTokenProperties; -import org.apereo.cas.configuration.support.Beans; import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.ticket.BaseTicketCatalogConfigurer; -import org.apereo.cas.ticket.ExpirationPolicy; -import org.apereo.cas.ticket.TicketCatalog; -import org.apereo.cas.ticket.TicketDefinition; -import org.apereo.cas.ticket.TicketGrantingTicketFactory; -import org.apereo.cas.ticket.UniqueTicketIdGenerator; -import org.apereo.cas.ticket.accesstoken.AccessTokenFactory; -import org.apereo.cas.ticket.accesstoken.AccessTokenImpl; -import org.apereo.cas.ticket.accesstoken.OAuthAccessTokenExpirationPolicy; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; +import org.apereo.cas.ticket.*; +import org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenFactory; +import org.apereo.cas.ticket.accesstoken.OAuth20DefaultAccessToken; +import org.apereo.cas.token.JwtBuilder; +import org.apereo.cas.util.crypto.CipherExecutor; +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; @@ -80,9 +72,8 @@ import fr.gouv.vitamui.cas.authentication.DelegatedSurrogateAuthenticationPostPr import fr.gouv.vitamui.cas.authentication.SurrogatedUserPrincipalFactory; import fr.gouv.vitamui.cas.authentication.UserAuthenticationHandler; import fr.gouv.vitamui.cas.authentication.UserPrincipalResolver; -import fr.gouv.vitamui.cas.metrics.TimedAspect; import fr.gouv.vitamui.cas.provider.ProvidersService; -import fr.gouv.vitamui.cas.ticket.CustomAccessTokenFactory; +import fr.gouv.vitamui.cas.ticket.CustomOAuth20DefaultAccessTokenFactory; import fr.gouv.vitamui.cas.ticket.DynamicTicketGrantingTicketFactory; import fr.gouv.vitamui.cas.util.Utils; import fr.gouv.vitamui.commons.api.identity.ServerIdentityAutoConfiguration; @@ -94,7 +85,6 @@ import fr.gouv.vitamui.iam.common.utils.Saml2ClientBuilder; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; import fr.gouv.vitamui.iam.external.client.IamExternalRestClientFactory; import fr.gouv.vitamui.iam.external.client.IdentityProviderExternalRestClient; -import io.micrometer.core.instrument.MeterRegistry; /** * Configure all beans to customize the CAS server. @@ -146,13 +136,6 @@ public class AppConfig extends BaseTicketCatalogConfigurer { @Qualifier("surrogateEligibilityAuditableExecution") private AuditableExecution surrogateEligibilityAuditableExecution; - @Autowired - @Qualifier("pac4jDelegatedSessionCookieManager") - private DelegatedSessionCookieManager delegatedSessionCookieManager; - - @Autowired - private DelegatedClientWebflowManager delegatedClientWebflowManager; - @Autowired private RestTemplateBuilder restTemplateBuilder; @@ -161,20 +144,26 @@ public class AppConfig extends BaseTicketCatalogConfigurer { private UniqueTicketIdGenerator ticketGrantingTicketUniqueIdGenerator; @Autowired - private ExpirationPolicy grantingTicketExpirationPolicy; + @Qualifier("accessTokenJwtBuilder") + private JwtBuilder accessTokenJwtBuilder; + + @Autowired + @Qualifier("grantingTicketExpirationPolicy") + private ObjectProvider<ExpirationPolicyBuilder> grantingTicketExpirationPolicy; @Autowired private CipherExecutor protocolTicketCipherExecutor; + @Autowired + @Qualifier("accessTokenExpirationPolicy") + private ExpirationPolicyBuilder accessTokenExpirationPolicy; + @Value("${token.api.cas}") private String tokenApiCas; @Value("${api.token.ttl}") private Integer apiTokenTtl; - @Autowired - private CasConfigurationProperties casProperties; - @Bean public UserAuthenticationHandler userAuthenticationHandler() { return new UserAuthenticationHandler(servicesManager, principalFactory); @@ -204,7 +193,7 @@ public class AppConfig extends BaseTicketCatalogConfigurer { public AuthenticationEventExecutionPlanConfigurer pac4jAuthenticationEventExecutionPlanConfigurer(final UserPrincipalResolver userResolver) { return plan -> { plan.registerAuthenticationHandlerWithPrincipalResolver(clientAuthenticationHandler, userResolver); - plan.registerMetadataPopulator(clientAuthenticationMetaDataPopulator); + plan.registerAuthenticationMetadataPopulator(clientAuthenticationMetaDataPopulator); }; } @@ -252,39 +241,26 @@ public class AppConfig extends BaseTicketCatalogConfigurer { @Bean public Utils utils() { - return new Utils(casRestClient(), delegatedClientWebflowManager, delegatedSessionCookieManager, tokenApiCas); + return new Utils(casRestClient(), tokenApiCas); } @Bean public TicketGrantingTicketFactory defaultTicketGrantingTicketFactory() { - return new DynamicTicketGrantingTicketFactory(ticketGrantingTicketUniqueIdGenerator, grantingTicketExpirationPolicy, protocolTicketCipherExecutor); - } - - @Bean - public TimedAspect timedAspect(@Autowired final MeterRegistry meterRegistry) { - return new TimedAspect(meterRegistry); - } - - @Bean - public ExpirationPolicy accessTokenExpirationPolicy() { - final OAuthAccessTokenProperties oauth = casProperties.getAuthn().getOauth().getAccessToken(); - if (casProperties.getLogout().isRemoveDescendantTickets()) { - return new OAuthAccessTokenExpirationPolicy(Beans.newDuration(oauth.getMaxTimeToLiveInSeconds()).getSeconds(), - Beans.newDuration(oauth.getTimeToKillInSeconds()).getSeconds()); - } - return new OAuthAccessTokenExpirationPolicy.OAuthAccessTokenSovereignExpirationPolicy(Beans.newDuration(oauth.getMaxTimeToLiveInSeconds()).getSeconds(), - Beans.newDuration(oauth.getTimeToKillInSeconds()).getSeconds()); + return new DynamicTicketGrantingTicketFactory(ticketGrantingTicketUniqueIdGenerator, grantingTicketExpirationPolicy.getObject(), + protocolTicketCipherExecutor); } @Bean @RefreshScope - public AccessTokenFactory defaultAccessTokenFactory() { - return new CustomAccessTokenFactory(accessTokenExpirationPolicy()); + public OAuth20AccessTokenFactory defaultAccessTokenFactory() { + return new CustomOAuth20DefaultAccessTokenFactory(accessTokenExpirationPolicy, + accessTokenJwtBuilder, + servicesManager); } @Override public void configureTicketCatalog(final TicketCatalog plan) { - final TicketDefinition metadata = buildTicketDefinition(plan, "TOK", AccessTokenImpl.class, Ordered.HIGHEST_PRECEDENCE); + final TicketDefinition metadata = buildTicketDefinition(plan, "TOK", OAuth20DefaultAccessToken.class, Ordered.HIGHEST_PRECEDENCE); metadata.getProperties().setStorageName("oauthAccessTokensCache"); metadata.getProperties().setStorageTimeout(apiTokenTtl); registerTicketDefinition(plan, metadata); 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 index 3fedf9216019fa74166d68a6bb2c29d575006a98..c393d9bcc49a98195423bb3f7f139bd73abcdadf 100644 --- 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 @@ -45,7 +45,8 @@ 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.spring4.SpringTemplateEngine; +import org.thymeleaf.spring5.SpringTemplateEngine; + /** * Web customizations. 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 9a1fcafc4f1fd00ab6f9cf1fbb36ce244b1ac58d..6cfab56e99005244e2d11daf3e992c6cd496f25f 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 @@ -44,6 +44,7 @@ import fr.gouv.vitamui.iam.common.utils.IdentityProviderHelper; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; import org.apereo.cas.CentralAuthenticationService; import org.apereo.cas.audit.AuditableExecution; +import org.apereo.cas.authentication.AuthenticationEventExecutionPlan; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; import org.apereo.cas.authentication.AuthenticationSystemSupport; import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy; @@ -52,16 +53,20 @@ import org.apereo.cas.pm.PasswordManagementService; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.ticket.TicketFactory; import org.apereo.cas.ticket.registry.TicketRegistry; +import org.apereo.cas.ticket.registry.TicketRegistrySupport; import org.apereo.cas.util.CollectionUtils; 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; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.apereo.cas.web.support.ArgumentExtractor; -import org.apereo.cas.web.support.CookieRetrievingCookieGenerator; 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.cloud.context.config.annotation.RefreshScope; @@ -85,15 +90,19 @@ public class WebflowConfig { @Autowired @Qualifier("servicesManager") - private ServicesManager servicesManager; + private ObjectProvider<ServicesManager> servicesManager; + + @Autowired + @Qualifier("ticketGrantingTicketCookieGenerator") + private ObjectProvider<CasCookieBuilder> ticketGrantingTicketCookieGenerator; @Autowired @Qualifier("warnCookieGenerator") - private CookieRetrievingCookieGenerator warnCookieGenerator; + private ObjectProvider<CasCookieBuilder> warnCookieGenerator; @Autowired @Qualifier("authenticationServiceSelectionPlan") - private AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies; + private ObjectProvider<AuthenticationServiceSelectionPlan> authenticationRequestServiceSelectionStrategies; @Autowired @Qualifier("communicationsManager") @@ -103,10 +112,6 @@ public class WebflowConfig { @Qualifier("passwordChangeService") private PasswordManagementService passwordManagementService; - @Autowired - @Qualifier("ticketGrantingTicketCookieGenerator") - private CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator; - @Autowired private CasConfigurationProperties casProperties; @@ -135,38 +140,34 @@ public class WebflowConfig { @Autowired @Qualifier("initialAuthenticationAttemptWebflowEventResolver") - private CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver; + private ObjectProvider<CasDelegatingWebflowEventResolver> initialAuthenticationAttemptWebflowEventResolver; @Autowired @Qualifier("builtClients") - private Clients builtClients; + private ObjectProvider<Clients> builtClients; @Autowired @Qualifier("serviceTicketRequestWebflowEventResolver") - private CasWebflowEventResolver serviceTicketRequestWebflowEventResolver; + private ObjectProvider<CasWebflowEventResolver> serviceTicketRequestWebflowEventResolver; @Autowired @Qualifier("adaptiveAuthenticationPolicy") - private AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy; + private ObjectProvider<AdaptiveAuthenticationPolicy> adaptiveAuthenticationPolicy; @Autowired @Qualifier("registeredServiceDelegatedAuthenticationPolicyAuditableEnforcer") - private AuditableExecution registeredServiceDelegatedAuthenticationPolicyAuditableEnforcer; - - @Autowired - @Qualifier("pac4jDelegatedSessionCookieManager") - private DelegatedSessionCookieManager delegatedSessionCookieManager; + private ObjectProvider<AuditableExecution> registeredServiceDelegatedAuthenticationPolicyAuditableEnforcer; @Autowired @Qualifier("defaultAuthenticationSystemSupport") - private AuthenticationSystemSupport authenticationSystemSupport; + private ObjectProvider<AuthenticationSystemSupport> authenticationSystemSupport; @Autowired private TicketRegistry ticketRegistry; @Autowired @Qualifier("argumentExtractor") - private ArgumentExtractor argumentExtractor; + private ObjectProvider<ArgumentExtractor> argumentExtractor; @Autowired @Qualifier("defaultTicketFactory") @@ -174,7 +175,23 @@ public class WebflowConfig { @Autowired @Qualifier("centralAuthenticationService") - private CentralAuthenticationService centralAuthenticationService; + private ObjectProvider<CentralAuthenticationService> centralAuthenticationService; + + @Autowired + @Qualifier("authenticationEventExecutionPlan") + private ObjectProvider<AuthenticationEventExecutionPlan> authenticationEventExecutionPlan; + + @Autowired + @Qualifier("singleSignOnParticipationStrategy") + private ObjectProvider<SingleSignOnParticipationStrategy> webflowSingleSignOnParticipationStrategy; + + @Autowired + @Qualifier("defaultTicketRegistrySupport") + private ObjectProvider<TicketRegistrySupport> ticketRegistrySupport; + + @Autowired + @Qualifier("delegatedClientDistributedSessionStore") + private ObjectProvider<SessionStore> delegatedClientDistributedSessionStore; @Bean public DispatcherAction dispatcherAction() { @@ -183,17 +200,22 @@ public class WebflowConfig { @Bean @RefreshScope public Action sendPasswordResetInstructionsAction() { - return new I18NSendPasswordResetInstructionsAction(casProperties, communicationsManager, passwordManagementService); + return new I18NSendPasswordResetInstructionsAction(casProperties, communicationsManager, passwordManagementService, + ticketRegistry, ticketFactory); } @RefreshScope @Bean public Action initialFlowSetupAction() { - return new CustomInitialFlowSetupAction(CollectionUtils.wrap(argumentExtractor), - servicesManager, - authenticationRequestServiceSelectionStrategies, - ticketGrantingTicketCookieGenerator, - warnCookieGenerator, casProperties); + return new CustomInitialFlowSetupAction(CollectionUtils.wrap(argumentExtractor.getObject()), + servicesManager.getObject(), + authenticationRequestServiceSelectionStrategies.getObject(), + ticketGrantingTicketCookieGenerator.getObject(), + warnCookieGenerator.getObject(), + casProperties, + authenticationEventExecutionPlan.getObject(), + webflowSingleSignOnParticipationStrategy.getObject(), + ticketRegistrySupport.getObject()); } @Bean @@ -219,20 +241,22 @@ public class WebflowConfig { @RefreshScope @Bean @Lazy - public Action clientAction() { - return new AutomaticDelegatedClientAuthenticationAction(initialAuthenticationAttemptWebflowEventResolver, - serviceTicketRequestWebflowEventResolver, - adaptiveAuthenticationPolicy, - builtClients, - servicesManager, - registeredServiceDelegatedAuthenticationPolicyAuditableEnforcer, + public Action delegatedAuthenticationAction() { + return new DelegatedClientAuthenticationAction( + initialAuthenticationAttemptWebflowEventResolver.getObject(), + serviceTicketRequestWebflowEventResolver.getObject(), + adaptiveAuthenticationPolicy.getObject(), + builtClients.getObject(), + servicesManager.getObject(), + registeredServiceDelegatedAuthenticationPolicyAuditableEnforcer.getObject(), delegatedClientWebflowManager(), - delegatedSessionCookieManager, - authenticationSystemSupport, - casProperties.getLocale().getParamName(), - casProperties.getTheme().getParamName(), - authenticationRequestServiceSelectionStrategies, - centralAuthenticationService); + authenticationSystemSupport.getObject(), + casProperties, + authenticationRequestServiceSelectionStrategies.getObject(), + centralAuthenticationService.getObject(), + webflowSingleSignOnParticipationStrategy.getObject(), + delegatedClientDistributedSessionStore.getObject(), + CollectionUtils.wrap(argumentExtractor.getObject())); } @RefreshScope @@ -240,19 +264,18 @@ public class WebflowConfig { public DelegatedClientWebflowManager delegatedClientWebflowManager() { return new CustomDelegatedClientWebflowManager(ticketRegistry, ticketFactory, - casProperties.getTheme().getParamName(), - casProperties.getLocale().getParamName(), - authenticationRequestServiceSelectionStrategies, - argumentExtractor + casProperties, + authenticationRequestServiceSelectionStrategies.getObject(), + argumentExtractor.getObject() ); } @Bean @RefreshScope public Action terminateSessionAction() { - return new GeneralTerminateSessionAction(centralAuthenticationService, - ticketGrantingTicketCookieGenerator, - warnCookieGenerator, + return new GeneralTerminateSessionAction(centralAuthenticationService.getObject(), + ticketGrantingTicketCookieGenerator.getObject(), + warnCookieGenerator.getObject(), casProperties.getLogout()); } } diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/metrics/TimedAspect.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/metrics/TimedAspect.java deleted file mode 100644 index 3ab3ce9af3b77904216f165e46150bc9ece1b6ef..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/metrics/TimedAspect.java +++ /dev/null @@ -1,81 +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.metrics; - -import com.codahale.metrics.annotation.Timed; -import io.micrometer.core.instrument.MeterRegistry; -import io.micrometer.core.instrument.Timer; -import org.aspectj.lang.ProceedingJoinPoint; -import org.aspectj.lang.annotation.Around; -import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.reflect.MethodSignature; - -import java.lang.reflect.Method; - -/** - * Aspect to catch Dropwizard Timed annotation uses and monitor with micrometer Timers. - * - * - */ -@Aspect -public class TimedAspect { - - private final MeterRegistry registry; - - public TimedAspect(MeterRegistry registry) { - this.registry = registry; - } - - @Around("execution (@com.codahale.metrics.annotation.Timed * *.*(..))") - public Object logExecutionTime(ProceedingJoinPoint pjp) throws Throwable { - Method method = ((MethodSignature) pjp.getSignature()).getMethod(); - Timed timed = method.getAnnotation(Timed.class); - - if (timed.name().isEmpty()) { - return pjp.proceed(); - } - - Timer.Sample sample = Timer.start(registry); - try { - return pjp.proceed(); - } finally { - final String name = "cas_" + timed.name().replaceAll("_TIMER", "").toLowerCase(); - sample.stop(Timer.builder(name) - .register(registry)); - } - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/SmsMultifactorAuthenticationProvider.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/SmsMultifactorAuthenticationProvider.java deleted file mode 100644 index 40c5335a26152ff3aae98eecb9699aee539795d8..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/SmsMultifactorAuthenticationProvider.java +++ /dev/null @@ -1,62 +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.mfa; - -import fr.gouv.vitamui.cas.util.Constants; -import org.apereo.cas.authentication.AbstractMultifactorAuthenticationProvider; - -/** - * The MFA SMS provider. - * - * - */ -public class SmsMultifactorAuthenticationProvider extends AbstractMultifactorAuthenticationProvider { - - public SmsMultifactorAuthenticationProvider() { - } - - @Override - public String getId() { - return Constants.MFA_SMS_EVENT_ID; - } - - - @Override - public String getFriendlyName() { - return "MFA-SMS"; - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/IamMultifactorAuthenticationProviderBypass.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/IamMultifactorAuthenticationProviderBypass.java deleted file mode 100644 index 4e49930e146bb53d20371b189deeee27baca6623..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/IamMultifactorAuthenticationProviderBypass.java +++ /dev/null @@ -1,102 +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.mfa.authentication; - -import javax.servlet.http.HttpServletRequest; - -import fr.gouv.vitamui.cas.provider.ProvidersService; -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 org.apereo.cas.authentication.Authentication; -import org.apereo.cas.authentication.DefaultMultifactorAuthenticationProviderBypass; -import org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProviderBypassProperties; -import org.apereo.cas.services.MultifactorAuthenticationProvider; -import org.apereo.cas.services.RegisteredService; - -import fr.gouv.vitamui.cas.util.Utils; -import fr.gouv.vitamui.commons.api.domain.UserDto; -import org.springframework.beans.factory.annotation.Autowired; - -/** - * Check if the MFA SMS authentication should be bypassed, depending on the IAM WS. - * - * - */ -public class IamMultifactorAuthenticationProviderBypass extends DefaultMultifactorAuthenticationProviderBypass { - - private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(IamMultifactorAuthenticationProviderBypass.class); - - @Autowired - private IdentityProviderHelper identityProviderHelper; - - @Autowired - private ProvidersService providersService; - - @Autowired - private Utils utils; - - public IamMultifactorAuthenticationProviderBypass(final MultifactorAuthenticationProviderBypassProperties prop) { - super(prop); - } - - @Override - public boolean shouldMultifactorAuthenticationProviderExecute(final Authentication authentication, - final RegisteredService registeredService, final MultifactorAuthenticationProvider provider, - final HttpServletRequest request) { - try { - final UserDto user = utils.getRealUser(authentication); - if (user != null) { - - final String email = user.getEmail(); - final boolean mfa = user.isOtp(); - LOGGER.debug("bypassing for (super)user: {}? -> mfa: {}", email, mfa); - - if (mfa && !identityProviderHelper.identifierMatchProviderPattern(providersService.getProviders(), email)) { - LOGGER.debug("The user: {} requests MFA but is internal: MFA is ABORTED!", email); - return false; - } - - return mfa; - } - } - catch (final Exception e) { - LOGGER.error(e.getMessage(), e); - } - return super.shouldMultifactorAuthenticationProviderExecute(authentication, registeredService, provider, request); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/SmsAuthenticationHandler.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/SmsAuthenticationHandler.java deleted file mode 100644 index 0d96dffaa506755e99aee057d8b0a4dcfa4176a0..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/authentication/SmsAuthenticationHandler.java +++ /dev/null @@ -1,128 +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.mfa.authentication; - -import java.security.GeneralSecurityException; -import java.util.ArrayList; - -import javax.security.auth.login.FailedLoginException; -import javax.servlet.http.HttpServletRequest; - -import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult; -import org.apereo.cas.authentication.Credential; -import org.apereo.cas.authentication.OneTimeTokenCredential; -import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler; -import org.apereo.cas.authentication.principal.Principal; -import org.apereo.cas.authentication.principal.PrincipalFactory; -import org.apereo.cas.services.ServicesManager; -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.context.ExternalContext; -import org.springframework.webflow.execution.RequestContext; -import org.springframework.webflow.execution.RequestContextHolder; - -import fr.gouv.vitamui.cas.util.Constants; -import fr.gouv.vitamui.cas.util.Utils; -import fr.gouv.vitamui.cas.util.VitamStatusCode; -import fr.gouv.vitamui.commons.api.CommonConstants; -import fr.gouv.vitamui.commons.api.logger.VitamUILogger; -import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; - -/** - * The authentication handler to validate SMS code. - * - * - */ -public class SmsAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler { - - private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(SmsAuthenticationHandler.class); - - @Autowired - private Utils utils; - - @Value("${ip.header}") - private String ipHeaderName; - - public SmsAuthenticationHandler(final String name, final ServicesManager servicesManager, - final PrincipalFactory principalFactory, final Integer order) { - super(name, servicesManager, principalFactory, order); - } - - @Override - protected AuthenticationHandlerExecutionResult doAuthentication(final Credential credential) - throws GeneralSecurityException { - final OneTimeTokenCredential smsCodeCredential = (OneTimeTokenCredential) credential; - final String credentialCode = smsCodeCredential.getToken(); - - final RequestContext requestContext = RequestContextHolder.getRequestContext(); - final String generatedCode = (String) requestContext.getFlowScope().get(Constants.GENERATED_CODE); - LOGGER.debug("credential code: {} vs generated code: {}", credentialCode, generatedCode); - - final Principal principal = WebUtils.getAuthentication(requestContext).getPrincipal(); - if (generatedCode.equals(credentialCode)) { - log(VitamStatusCode.OK, principal, null); - return createHandlerResult(smsCodeCredential, principal, new ArrayList<>()); - } else { - final String errorMessage = "the provided SMS code does not match the generated one"; - log(VitamStatusCode.KO, principal, errorMessage); - throw new FailedLoginException(errorMessage); - } - } - - protected void log(final VitamStatusCode status, final Principal principal, final String errorMessage) { - - final RequestContext requestContext = RequestContextHolder.getRequestContext(); - String ip = null; - if (requestContext != null) { - final ExternalContext externalContext = requestContext.getExternalContext(); - if (externalContext != null) { - ip = ((HttpServletRequest) externalContext.getNativeRequest()).getHeader(ipHeaderName); - } - } - - LOGGER.info( - "OTP authentication / status: {} / (super)UserIdentifier: {} / email: {} / IP: {} / errorMessage: {}", - status, utils.getAttributeValue(principal, CommonConstants.IDENTIFIER_ATTRIBUTE), - utils.getAttributeValue(principal, CommonConstants.EMAIL_ATTRIBUTE), ip, errorMessage); - } - - @Override - public boolean supports(final Credential credential) { - return OneTimeTokenCredential.class.isAssignableFrom(credential.getClass()); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaAuthenticationEventExecutionPlanConfiguration.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaAuthenticationEventExecutionPlanConfiguration.java deleted file mode 100644 index 39c70098d5ca043a43113026219901031ef72bdd..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaAuthenticationEventExecutionPlanConfiguration.java +++ /dev/null @@ -1,118 +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.mfa.config; - -import fr.gouv.vitamui.cas.mfa.authentication.IamMultifactorAuthenticationProviderBypass; -import fr.gouv.vitamui.cas.mfa.authentication.SmsAuthenticationHandler; -import fr.gouv.vitamui.cas.mfa.SmsMultifactorAuthenticationProvider; -import fr.gouv.vitamui.cas.util.Constants; -import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer; -import org.apereo.cas.authentication.AuthenticationHandler; -import org.apereo.cas.authentication.AuthenticationMetaDataPopulator; -import org.apereo.cas.authentication.OneTimeTokenCredential; -import org.apereo.cas.authentication.metadata.AuthenticationContextAttributeMetaDataPopulator; -import org.apereo.cas.authentication.principal.PrincipalFactory; -import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.configuration.model.support.mfa.MultifactorAuthenticationProviderBypassProperties; -import org.apereo.cas.services.MultifactorAuthenticationProvider; -import org.apereo.cas.services.RegisteredServiceMultifactorPolicy; -import org.apereo.cas.services.ServicesManager; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * The MFA SMS authentication configuration. - * - * - */ -@Configuration("smsAuthenticationEventExecutionPlanConfiguration") -@EnableConfigurationProperties(CasConfigurationProperties.class) -public class SmsMfaAuthenticationEventExecutionPlanConfiguration { - - @Autowired - private CasConfigurationProperties casProperties; - - @Autowired - @Qualifier("servicesManager") - private ServicesManager servicesManager; - - @Autowired - @Qualifier("principalFactory") - private PrincipalFactory principalFactory; - - @Bean - public IamMultifactorAuthenticationProviderBypass iamMultifactorAuthenticationProviderBypass() { - final MultifactorAuthenticationProviderBypassProperties prop = new MultifactorAuthenticationProviderBypassProperties(); - prop.setCredentialClassType(OneTimeTokenCredential.class.toString()); - return new IamMultifactorAuthenticationProviderBypass(prop); - } - - @Bean - public MultifactorAuthenticationProvider smsAuthenticatorAuthenticationProvider() { - final SmsMultifactorAuthenticationProvider p = new SmsMultifactorAuthenticationProvider(); - p.setBypassEvaluator(iamMultifactorAuthenticationProviderBypass()); - p.setFailureMode(RegisteredServiceMultifactorPolicy.FailureModes.CLOSED.toString()); - p.setOrder(1); - p.setId(Constants.MFA_SMS_EVENT_ID); - return p; - } - - @Bean - public AuthenticationMetaDataPopulator smsAuthenticationMetaDataPopulator() { - return new AuthenticationContextAttributeMetaDataPopulator( - casProperties.getAuthn().getMfa().getAuthenticationContextAttribute(), - smsAuthenticationHandler(), - smsAuthenticatorAuthenticationProvider().getId() - ); - } - - @Bean - public AuthenticationHandler smsAuthenticationHandler() { - return new SmsAuthenticationHandler(Constants.MFA_SMS_EVENT_ID, servicesManager, principalFactory, null); - } - - @Bean - public AuthenticationEventExecutionPlanConfigurer smsAuthenticationEventExecutionPlanConfigurer() { - return plan -> { - plan.registerAuthenticationHandler(smsAuthenticationHandler()); - plan.registerMetadataPopulator(smsAuthenticationMetaDataPopulator()); - }; - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaConfiguration.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaConfiguration.java deleted file mode 100644 index 6a47ae0cba9108bf606c40e021214d849c1d1f59..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/config/SmsMfaConfiguration.java +++ /dev/null @@ -1,155 +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.mfa.config; - -import fr.gouv.vitamui.cas.mfa.webflow.*; -import org.apereo.cas.CentralAuthenticationService; -import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; -import org.apereo.cas.authentication.AuthenticationSystemSupport; -import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.services.MultifactorAuthenticationProviderSelector; -import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.ticket.registry.TicketRegistrySupport; -import org.apereo.cas.web.flow.CasWebflowConfigurer; -import org.apereo.cas.web.flow.authentication.RankedMultifactorAuthenticationProviderSelector; -import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.context.ApplicationContext; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.web.util.CookieGenerator; -import org.springframework.webflow.config.FlowDefinitionRegistryBuilder; -import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; -import org.springframework.webflow.engine.builder.support.FlowBuilderServices; -import org.springframework.webflow.execution.Action; - -/** - * The MFA SMS configuration. - * - * - */ -@Configuration("smsMfaConfiguration") -@EnableConfigurationProperties(CasConfigurationProperties.class) -public class SmsMfaConfiguration { - - @Autowired - private CasConfigurationProperties casProperties; - - @Autowired - private ApplicationContext applicationContext; - - @Autowired - @Qualifier("loginFlowRegistry") - private FlowDefinitionRegistry loginFlowDefinitionRegistry; - - @Autowired - private FlowBuilderServices flowBuilderServices; - - @Autowired - @Qualifier("defaultAuthenticationSystemSupport") - private AuthenticationSystemSupport authenticationSystemSupport; - - @Autowired - @Qualifier("centralAuthenticationService") - private CentralAuthenticationService centralAuthenticationService; - - @Autowired - @Qualifier("servicesManager") - private ServicesManager servicesManager; - - @Autowired - @Qualifier("defaultTicketRegistrySupport") - private TicketRegistrySupport ticketRegistrySupport; - - @Autowired - @Qualifier("warnCookieGenerator") - private CookieGenerator warnCookieGenerator; - - @Autowired - @Qualifier("authenticationServiceSelectionPlan") - private AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies; - - @Autowired(required = false) - @Qualifier("multifactorAuthenticationProviderSelector") - private MultifactorAuthenticationProviderSelector multifactorAuthenticationProviderSelector = - new RankedMultifactorAuthenticationProviderSelector(); - - @Bean - @DependsOn("defaultWebflowConfigurer") - @RefreshScope - public CasWebflowConfigurer smsMultifactorWebflowConfigurer() { - final CasWebflowConfigurer w = new SmsMultifactorWebflowConfigurer(flowBuilderServices, - loginFlowDefinitionRegistry, smsAuthenticatorFlowRegistry(), - applicationContext, casProperties); - - w.initialize(); - return w; - } - - @Bean - public FlowDefinitionRegistry smsAuthenticatorFlowRegistry() { - final FlowDefinitionRegistryBuilder builder = new FlowDefinitionRegistryBuilder(this.applicationContext, this.flowBuilderServices); - builder.setBasePath("classpath*:/webflow"); - builder.addFlowLocationPattern("/mfa-sms/*-webflow.xml"); - return builder.build(); - } - - @Bean - public Action sendOTPAction() { - return new SendOTPAction(); - } - - @Bean - public CasWebflowEventResolver smsAuthenticationWebflowEventResolver() { - return new SmsAuthenticationWebflowEventResolver(authenticationSystemSupport, - centralAuthenticationService, - servicesManager, - ticketRegistrySupport, - warnCookieGenerator, - authenticationRequestServiceSelectionStrategies, - multifactorAuthenticationProviderSelector); - } - - @Bean - public Action smsAuthenticationWebflowAction() { - return new SmsAuthenticationWebflowAction(smsAuthenticationWebflowEventResolver()); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/util/CodeStringGenerator.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/util/CodeStringGenerator.java deleted file mode 100644 index 2968dbc57e88c217a3db21524f6dd76575ba3e6a..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/util/CodeStringGenerator.java +++ /dev/null @@ -1,77 +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.mfa.util; - -import org.apereo.cas.util.gen.AbstractRandomStringGenerator; - -import java.util.stream.IntStream; - -/** - * A SMS code generator. - * - * - */ -public class CodeStringGenerator extends AbstractRandomStringGenerator { - - /** The array of printable characters to be used in our random string. */ - private static final char[] PRINTABLE_CHARACTERS = "012345679".toCharArray(); - - public CodeStringGenerator() { - super(4); - } - - public CodeStringGenerator(final int defaultLength) { - super(defaultLength); - } - - /** - * Convert bytes to string, taking into account {@link #PRINTABLE_CHARACTERS}. - * - * @param random the random - * @return the string - */ - @Override - protected String convertBytesToString(final byte[] random) { - final char[] output = new char[random.length]; - IntStream.range(0, random.length).forEach(i -> { - final int index = Math.abs(random[i] % PRINTABLE_CHARACTERS.length); - output[i] = PRINTABLE_CHARACTERS[index]; - }); - - return new String(output); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SendOTPAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SendOTPAction.java deleted file mode 100644 index a35a2d32e37df2af4846ba11f624702e47de9d08..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SendOTPAction.java +++ /dev/null @@ -1,123 +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.mfa.webflow; - -import fr.gouv.vitamui.cas.mfa.util.CodeStringGenerator; -import fr.gouv.vitamui.cas.util.Constants; -import fr.gouv.vitamui.cas.util.Utils; -import fr.gouv.vitamui.commons.api.logger.VitamUILogger; -import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; -import fr.gouv.vitamui.commons.api.domain.UserDto; -import org.apache.commons.lang3.StringUtils; -import org.apereo.cas.authentication.Authentication; -import org.apereo.cas.util.gen.RandomStringGenerator; -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.beans.factory.annotation.Value; -import org.springframework.context.HierarchicalMessageSource; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.webflow.action.AbstractAction; -import org.springframework.webflow.core.collection.MutableAttributeMap; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; - -import java.util.Date; - -/** - * The webflow action to send SMS code. - * - * - */ -public class SendOTPAction extends AbstractAction { - - private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(SendOTPAction.class); - - private static final String CODE_TEXT_KEY = "cas.mfa.sms.code.text"; - - private final RandomStringGenerator codeGenerator = new CodeStringGenerator(); - - @Autowired - @Qualifier("communicationsManager") - private CommunicationsManager communicationsManager; - - @Value("${mfa.sms.sender}") - private String smsSender; - - @Autowired - @Qualifier("messageSource") - private HierarchicalMessageSource messageSource; - - @Autowired - private Utils utils; - - @Override - protected Event doExecute(final RequestContext context) { - - final Authentication authentication = WebUtils.getAuthentication(context); - LOGGER.debug("authentication: {}", authentication); - final UserDto user = utils.getRealUser(authentication); - LOGGER.debug("user: {}", user); - final String mobile = user.getMobile(); - LOGGER.debug("mobile: {}", mobile); - final MutableAttributeMap<Object> flowScope = context.getFlowScope(); - - if (StringUtils.isNotBlank(mobile)) { - final String code = codeGenerator.getNewString(); - flowScope.put(Constants.GENERATED_CODE, code); - flowScope.put(Constants.GENERATION_DATE, new Date()); - LOGGER.debug("Generating code: {}", code); - - final String message = messageSource.getMessage(CODE_TEXT_KEY, new Object[] { code }, LocaleContextHolder.getLocale()); - communicationsManager.sms(smsSender, mobile, message); - - flowScope.put("mobile", obfuscateMobile(mobile)); - return success(); - } else { - final String firstName = user.getFirstname(); - flowScope.put("firstname", firstName); - return error(); - } - } - - private String obfuscateMobile(final String mobile) { - String m = mobile.replaceFirst("\\+33", "0"); - m = m.substring(0, 2) + " XX XX XX " + m.substring(m.length() - 2); - return m; - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowAction.java deleted file mode 100644 index 2bc2c2c462381144141389ff59714acd672cf8b5..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowAction.java +++ /dev/null @@ -1,80 +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.mfa.webflow; - -import fr.gouv.vitamui.cas.util.Constants; -import org.apache.commons.lang.time.DateUtils; -import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; -import org.springframework.webflow.action.AbstractAction; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; - -import java.util.Date; - -/** - * The webflow action to validate that SMS codes are not expired. - * - * - */ -public class SmsAuthenticationWebflowAction extends AbstractAction { - - private final CasWebflowEventResolver casWebflowEventResolver; - - public SmsAuthenticationWebflowAction(final CasWebflowEventResolver casWebflowEventResolver) { - this.casWebflowEventResolver = casWebflowEventResolver; - } - - @Override - protected Event doExecute(final RequestContext requestContext) { - - // retrieve the code generation date and check if the code is expired - final Date generationDate = (Date) requestContext.getFlowScope().get(Constants.GENERATION_DATE); - if (generationDate == null) { - return expired(); - } - final Date nowLessAMinute = DateUtils.addMinutes(new Date(), -1); - if (generationDate.before(nowLessAMinute)) { - return expired(); - } - - return this.casWebflowEventResolver.resolveSingle(requestContext); - } - - protected Event expired() { - return getEventFactorySupport().event(this, "expired"); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowEventResolver.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowEventResolver.java deleted file mode 100644 index 96cd36011c8d26bd25751574b084873991599c34..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsAuthenticationWebflowEventResolver.java +++ /dev/null @@ -1,81 +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.mfa.webflow; - -import org.apereo.cas.CentralAuthenticationService; -import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; -import org.apereo.cas.authentication.AuthenticationSystemSupport; -import org.apereo.cas.services.MultifactorAuthenticationProviderSelector; -import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.ticket.registry.TicketRegistrySupport; -import org.apereo.cas.web.flow.resolver.impl.AbstractCasWebflowEventResolver; -import org.apereo.inspektr.audit.annotation.Audit; -import org.springframework.web.util.CookieGenerator; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; - -import java.util.Set; - -/** - * The MFA SMS webflow event resolver. - * - * - */ -public class SmsAuthenticationWebflowEventResolver extends AbstractCasWebflowEventResolver { - - public SmsAuthenticationWebflowEventResolver(final AuthenticationSystemSupport authenticationSystemSupport, - final CentralAuthenticationService centralAuthenticationService, final ServicesManager servicesManager, - final TicketRegistrySupport ticketRegistrySupport, final CookieGenerator warnCookieGenerator, - final AuthenticationServiceSelectionPlan authenticationSelectionStrategies, - final MultifactorAuthenticationProviderSelector selector) { - super(authenticationSystemSupport, centralAuthenticationService, servicesManager, ticketRegistrySupport, warnCookieGenerator, - authenticationSelectionStrategies, selector); - } - - @Override - public Set<Event> resolveInternal(final RequestContext context) { - return handleAuthenticationTransactionAndGrantTicketGrantingTicket(context); - } - - @Audit(action = "AUTHENTICATION_EVENT", - actionResolverName = "AUTHENTICATION_EVENT_ACTION_RESOLVER", - resourceResolverName = "AUTHENTICATION_EVENT_RESOURCE_RESOLVER") - @Override - public Event resolveSingle(final RequestContext context) { - return super.resolveSingle(context); - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsMultifactorWebflowConfigurer.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsMultifactorWebflowConfigurer.java deleted file mode 100644 index 8aad2aaa142dc0fec9abeaa20aa7aa4d6a1d5128..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/mfa/webflow/SmsMultifactorWebflowConfigurer.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.mfa.webflow; - -import fr.gouv.vitamui.cas.util.Constants; -import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.web.flow.configurer.AbstractCasMultifactorWebflowConfigurer; -import org.springframework.context.ApplicationContext; -import org.springframework.webflow.definition.registry.FlowDefinitionRegistry; -import org.springframework.webflow.engine.builder.support.FlowBuilderServices; - -/** - * The MFA SMS webflow configurer. - * - * - */ -public class SmsMultifactorWebflowConfigurer extends AbstractCasMultifactorWebflowConfigurer { - - private final FlowDefinitionRegistry flowDefinitionRegistry; - - public SmsMultifactorWebflowConfigurer(final FlowBuilderServices flowBuilderServices, final FlowDefinitionRegistry loginFlowDefinitionRegistry, - final FlowDefinitionRegistry flowDefinitionRegistry, - final ApplicationContext applicationContext, - final CasConfigurationProperties casProperties) { - super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties); - this.flowDefinitionRegistry = flowDefinitionRegistry; - } - - @Override - protected void doInitialize() { - registerMultifactorProviderAuthenticationWebflow(getLoginFlow(), Constants.MFA_SMS_EVENT_ID, this.flowDefinitionRegistry, Constants.MFA_SMS_EVENT_ID); - } -} 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/IamRestPasswordManagementService.java index 0facac03fac27650a1a7b0ab2b18c8fc5aeee605..e204936c7b14467e0427ff7bcc35dbc8a1db067e 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/IamRestPasswordManagementService.java @@ -42,11 +42,11 @@ import java.util.Optional; import org.apereo.cas.DefaultCentralAuthenticationService; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.authentication.Credential; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.configuration.model.support.pm.PasswordManagementProperties; import org.apereo.cas.pm.BasePasswordManagementService; import org.apereo.cas.pm.InvalidPasswordException; -import org.apereo.cas.pm.PasswordChangeBean; +import org.apereo.cas.pm.PasswordChangeRequest; import org.apereo.cas.ticket.TicketGrantingTicket; import org.apereo.cas.ticket.registry.TicketRegistry; import org.apereo.cas.web.support.WebUtils; @@ -101,9 +101,11 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ @Autowired private TicketRegistry ticketRegistry; - public IamRestPasswordManagementService(final CasExternalRestClient casExternalRestClient, final PasswordManagementProperties passwordManagementProperties, - final ProvidersService providersService, final IdentityProviderHelper identityProviderHelper) { - super(passwordManagementProperties, null, null); + public IamRestPasswordManagementService(final CasExternalRestClient casExternalRestClient, + final PasswordManagementProperties passwordManagementProperties, + final ProvidersService providersService, + final IdentityProviderHelper identityProviderHelper) { + super(passwordManagementProperties, null, null, null); this.casExternalRestClient = casExternalRestClient; this.providersService = providersService; this.identityProviderHelper = identityProviderHelper; @@ -128,7 +130,7 @@ public class IamRestPasswordManagementService extends BasePasswordManagementServ } @Override - public boolean changeInternal(final Credential c, final PasswordChangeBean bean) { + public boolean changeInternal(final Credential c, final PasswordChangeRequest bean) throws InvalidPasswordException { final RequestContext requestContext = blockIfSubrogation(); final MutableAttributeMap flowScope = requestContext.getFlowScope(); if (flowScope != null) { 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 index 4407cde943855ed9ba7d7b879d07baf36dd3ab0d..5d0a943f0121e53b1be3c6e5e152f18cf18a3ef4 100644 --- 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 @@ -43,7 +43,7 @@ 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.support.HardTimeoutExpirationPolicy; +import org.apereo.cas.ticket.expiration.HardTimeoutExpirationPolicy; /** * Specific ticket for the password management token. 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 b3e881c1372d8216a4f527276dd7b97f926e01a0..01cda714b15ecb3ddb105a7dd4fa42d9cd1578db 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 @@ -45,7 +45,7 @@ import javax.mail.internet.MimeMessage; import org.apache.commons.lang3.StringUtils; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.pm.PasswordManagementService; -import org.apereo.cas.pm.web.flow.actions.SendPasswordResetInstructionsAction; +import org.apereo.cas.pm.web.flow.PasswordManagementWebflowUtils; import org.apereo.cas.ticket.registry.TicketRegistry; import org.apereo.cas.util.io.CommunicationsManager; import org.springframework.beans.factory.annotation.Autowired; @@ -143,8 +143,8 @@ public class ResetPasswordController { 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 + '?' + SendPasswordResetInstructionsAction.PARAMETER_NAME_TOKEN + '=') - .concat(token); + return casProperties.getServer().getPrefix().concat('/' + FLOW_ID_LOGIN + '?' + + PasswordManagementWebflowUtils.REQUEST_PARAMETER_NAME_PASSWORD_RESET_TOKEN + '=').concat(token); } protected String createToken(final String to, final int expMinutes) { diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomAccessTokenFactory.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomAccessTokenFactory.java deleted file mode 100644 index 65d8c686e894694b1e33bb07f83a36dabb5c4065..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomAccessTokenFactory.java +++ /dev/null @@ -1,78 +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.ticket; - -import fr.gouv.vitamui.commons.api.CommonConstants; -import org.apereo.cas.authentication.Authentication; -import org.apereo.cas.authentication.principal.Principal; -import org.apereo.cas.authentication.principal.Service; -import org.apereo.cas.ticket.ExpirationPolicy; -import org.apereo.cas.ticket.TicketGrantingTicket; -import org.apereo.cas.ticket.accesstoken.AccessToken; -import org.apereo.cas.ticket.accesstoken.AccessTokenImpl; -import org.apereo.cas.ticket.accesstoken.DefaultAccessTokenFactory; - -import java.util.Collection; -import java.util.List; - -/** - * Specific factory for access tokens using the auth token as identifier. - * - * - */ -public class CustomAccessTokenFactory extends DefaultAccessTokenFactory { - - public CustomAccessTokenFactory(final ExpirationPolicy expirationPolicy) { - super(expirationPolicy); - } - - @Override - public AccessToken create(final Service service, final Authentication authentication, - final TicketGrantingTicket ticketGrantingTicket, final Collection<String> scopes) { - final Principal principal = authentication.getPrincipal(); - final List<String> authToken = (List<String>) principal.getAttributes().get(CommonConstants.AUTHTOKEN_ATTRIBUTE); - if (authToken == null || authToken.size() == 0) { - throw new RuntimeException("Cannot create access token for null authtoken: " + principal); - } - final AccessToken at = new AccessTokenImpl(authToken.get(0), service, authentication, - this.expirationPolicy, ticketGrantingTicket, scopes); - if (ticketGrantingTicket != null) { - ticketGrantingTicket.getDescendantTickets().add(at.getId()); - } - return at; - } -} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomOAuth20DefaultAccessTokenFactory.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomOAuth20DefaultAccessTokenFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..3416cc57bc760f75aba039009ad9f48f258f14d8 --- /dev/null +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/CustomOAuth20DefaultAccessTokenFactory.java @@ -0,0 +1,148 @@ +/** + * 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.ticket; + +import fr.gouv.vitamui.commons.api.CommonConstants; +import org.apereo.cas.authentication.Authentication; +import org.apereo.cas.authentication.principal.Principal; +import org.apereo.cas.authentication.principal.Service; +import org.apereo.cas.configuration.support.Beans; +import org.apereo.cas.services.ServicesManager; +import org.apereo.cas.support.oauth.services.OAuthRegisteredService; +import org.apereo.cas.support.oauth.util.OAuth20Utils; +import org.apereo.cas.ticket.ExpirationPolicy; +import org.apereo.cas.ticket.ExpirationPolicyBuilder; +import org.apereo.cas.ticket.Ticket; +import org.apereo.cas.ticket.TicketFactory; +import org.apereo.cas.ticket.TicketGrantingTicket; +import org.apereo.cas.ticket.accesstoken.OAuth20AccessToken; +import org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenExpirationPolicy; +import org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenFactory; +import org.apereo.cas.ticket.accesstoken.OAuth20DefaultAccessToken; +import org.apereo.cas.token.JwtBuilder; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.val; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Specific factory for access tokens using the auth token as identifier. + * + * + */ +@RequiredArgsConstructor +@Getter +public class CustomOAuth20DefaultAccessTokenFactory implements OAuth20AccessTokenFactory { + + /** + * ExpirationPolicy for refresh tokens. + */ + protected final ExpirationPolicyBuilder<OAuth20AccessToken> expirationPolicy; + + /** + * JWT builder instance. + */ + protected final JwtBuilder jwtBuilder; + + /** + * Services manager. + */ + protected final ServicesManager servicesManager; + + @Override + public OAuth20AccessToken create(final Service service, final Authentication authentication, + final TicketGrantingTicket ticketGrantingTicket, + final Collection<String> scopes, final String clientId, + final Map<String, Map<String, Object>> requestClaims) { + val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(jwtBuilder.getServicesManager(), clientId); + val expirationPolicyToUse = determineExpirationPolicyForService(registeredService); + val accessTokenId = generateAccessTokenId(authentication); + + val at = new OAuth20DefaultAccessToken(accessTokenId, service, authentication, + expirationPolicyToUse, ticketGrantingTicket, scopes, + clientId, requestClaims); + if (ticketGrantingTicket != null) { + ticketGrantingTicket.getDescendantTickets().add(at.getId()); + } + return at; + } + + @Override + public OAuth20AccessToken create(final Service service, final Authentication authentication, + final Collection<String> scopes, final String clientId, + final Map<String, Map<String, Object>> requestClaims) { + val accessTokenId = generateAccessTokenId(authentication); + val registeredService = OAuth20Utils.getRegisteredOAuthServiceByClientId(jwtBuilder.getServicesManager(), clientId); + val expirationPolicyToUse = determineExpirationPolicyForService(registeredService); + return new OAuth20DefaultAccessToken(accessTokenId, service, authentication, + expirationPolicyToUse, null, + scopes, clientId, requestClaims); + } + + private String generateAccessTokenId(final Authentication authentication) { + final Principal principal = authentication.getPrincipal(); + final List<Object> authToken = principal.getAttributes().get(CommonConstants.AUTHTOKEN_ATTRIBUTE); + if (authToken == null || authToken.size() == 0) { + throw new RuntimeException("Cannot create access token for null authtoken: " + principal); + } + return (String) authToken.get(0); + } + + @Override + public TicketFactory get(final Class<? extends Ticket> clazz) { + return this; + } + + private ExpirationPolicy determineExpirationPolicyForService(final OAuthRegisteredService registeredService) { + if (registeredService != null && registeredService.getAccessTokenExpirationPolicy() != null) { + val policy = registeredService.getAccessTokenExpirationPolicy(); + val maxTime = policy.getMaxTimeToLive(); + val ttl = policy.getTimeToKill(); + if (StringUtils.isNotBlank(maxTime) && StringUtils.isNotBlank(ttl)) { + return new OAuth20AccessTokenExpirationPolicy( + Beans.newDuration(maxTime).getSeconds(), + Beans.newDuration(ttl).getSeconds()); + } + } + return this.expirationPolicy.buildTicketExpirationPolicy(); + } +} diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/DynamicTicketGrantingTicketFactory.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/DynamicTicketGrantingTicketFactory.java index 693ba354b29fe698b4e42567dad05c954ed2affc..789a542a0efba99ab2e3f5fa93ffe8a4b263684a 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/DynamicTicketGrantingTicketFactory.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/ticket/DynamicTicketGrantingTicketFactory.java @@ -38,18 +38,16 @@ package fr.gouv.vitamui.cas.ticket; import fr.gouv.vitamui.cas.util.Utils; import fr.gouv.vitamui.commons.api.enums.UserTypeEnum; -import org.apereo.cas.CipherExecutor; import org.apereo.cas.authentication.Authentication; import org.apereo.cas.authentication.principal.Principal; -import org.apereo.cas.ticket.ExpirationPolicy; -import org.apereo.cas.ticket.TicketGrantingTicket; -import org.apereo.cas.ticket.TicketGrantingTicketImpl; -import org.apereo.cas.ticket.UniqueTicketIdGenerator; +import org.apereo.cas.ticket.*; +import org.apereo.cas.ticket.expiration.HardTimeoutExpirationPolicy; import org.apereo.cas.ticket.factory.DefaultTicketGrantingTicketFactory; -import org.apereo.cas.ticket.support.HardTimeoutExpirationPolicy; +import org.apereo.cas.util.crypto.CipherExecutor; import org.springframework.beans.factory.annotation.Autowired; import java.io.Serializable; +import java.util.List; import java.util.Map; import static fr.gouv.vitamui.commons.api.CommonConstants.*; @@ -64,7 +62,9 @@ public class DynamicTicketGrantingTicketFactory extends DefaultTicketGrantingTic @Autowired private Utils utils; - public DynamicTicketGrantingTicketFactory(final UniqueTicketIdGenerator ticketGrantingTicketUniqueTicketIdGenerator, final ExpirationPolicy ticketGrantingTicketExpirationPolicy, final CipherExecutor<Serializable, String> cipherExecutor) { + public DynamicTicketGrantingTicketFactory(final UniqueTicketIdGenerator ticketGrantingTicketUniqueTicketIdGenerator, + final ExpirationPolicyBuilder<TicketGrantingTicket> ticketGrantingTicketExpirationPolicy, + final CipherExecutor<Serializable, String> cipherExecutor) { super(ticketGrantingTicketUniqueTicketIdGenerator, ticketGrantingTicketExpirationPolicy, cipherExecutor); } @@ -72,9 +72,9 @@ public class DynamicTicketGrantingTicketFactory extends DefaultTicketGrantingTic protected <T extends TicketGrantingTicket> T produceTicket(final Authentication authentication, final String tgtId, final Class<T> clazz) { final Principal principal = authentication.getPrincipal(); - final Map<String, Object> attributes = principal.getAttributes(); - final String superUser = (String) attributes.get(SUPER_USER_ATTRIBUTE); - final UserTypeEnum type = (UserTypeEnum) utils.getAttributeValue(principal, TYPE_ATTRIBUTE); + final Map<String, List<Object>> attributes = principal.getAttributes(); + final String superUser = (String) utils.getAttributeValue(attributes, SUPER_USER_ATTRIBUTE); + final UserTypeEnum type = (UserTypeEnum) utils.getAttributeValue(attributes, TYPE_ATTRIBUTE); if (superUser != null && type == UserTypeEnum.GENERIC) { return (T) new TicketGrantingTicketImpl( tgtId, authentication, new HardTimeoutExpirationPolicy(170 * 60)); diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Constants.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Constants.java index 3de2728984c9648dbc64df52faab74bd87fa352e..ce080b43addcb7844f5506be782d8844eaf56af1 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Constants.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/util/Constants.java @@ -50,13 +50,6 @@ public abstract class Constants { public static final String SUPER_USER = "superUser"; - // for MFA: - public static final String GENERATED_CODE = "generatedCode"; - - public static final String GENERATION_DATE = "generationDate"; - - public static final String MFA_SMS_EVENT_ID = "mfa-sms"; - // web: public static final String PORTAL_URL = "portalUrl"; 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 72cd2ff471dd81df88fcaa26511d73e2d676e6c7..efdba632b3200cb3b62ab5976d09839fb438e018 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 @@ -42,7 +42,6 @@ import java.util.Map; import java.util.Optional; import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang.StringUtils; @@ -50,20 +49,12 @@ 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.services.UnauthorizedServiceException; -import org.apereo.cas.ticket.Ticket; -import org.apereo.cas.util.Pac4jUtils; -import org.apereo.cas.web.DelegatedClientWebflowManager; import org.apereo.cas.web.flow.CasWebflowConstants; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.apereo.cas.web.support.WebUtils; -import org.jasig.cas.client.util.URIBuilder; -import org.pac4j.core.context.J2EContext; -import org.pac4j.core.exception.HttpAction; -import org.pac4j.core.redirect.RedirectAction; +import org.pac4j.core.util.CommonHelper; +import org.pac4j.core.util.Pac4jConstants; import org.pac4j.saml.client.SAML2Client; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpStatus; import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.execution.Action; import org.springframework.webflow.execution.Event; @@ -89,10 +80,6 @@ public class Utils { private final CasExternalRestClient casExternalRestClient; - private final DelegatedClientWebflowManager delegatedClientWebflowManager; - - private final DelegatedSessionCookieManager delegatedSessionCookieManager; - private final String casToken; @Value("${vitamui.cas.tenant.identifier}") @@ -101,11 +88,8 @@ public class Utils { @Value("${vitamui.cas.identity}") private String casIdentity; - public Utils(final CasExternalRestClient casExternalRestClient, final DelegatedClientWebflowManager delegatedClientWebflowManager, - final DelegatedSessionCookieManager delegatedSessionCookieManager, final String casToken) { + public Utils(final CasExternalRestClient casExternalRestClient, final String casToken) { this.casExternalRestClient = casExternalRestClient; - this.delegatedClientWebflowManager = delegatedClientWebflowManager; - this.delegatedSessionCookieManager = delegatedSessionCookieManager; this.casToken = casToken; } @@ -114,33 +98,10 @@ public class Utils { } public Event performClientRedirection(final Action action, final SAML2Client client, final RequestContext requestContext) throws IOException { - final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext); final HttpServletResponse response = WebUtils.getHttpServletResponseFromExternalWebflowContext(requestContext); - try { - final J2EContext webContext = Pac4jUtils.getPac4jJ2EContext(request, response); - final Ticket ticket = delegatedClientWebflowManager.store(webContext, client); - - final RedirectAction redirectAction = client.getRedirectAction(webContext); - if (RedirectAction.RedirectType.SUCCESS.equals(redirectAction.getType())) { - webContext.writeResponseContent(redirectAction.getContent()); - } - else { - final URIBuilder builder = new URIBuilder(redirectAction.getLocation()); - final String url = builder.toString(); - LOGGER.debug("Redirecting client [{}] to [{}] based on identifier [{}]", client.getName(), url, ticket.getId()); - response.sendRedirect(url); - } - delegatedSessionCookieManager.store(webContext); - } - catch (final HttpAction e) { - if (e.getCode() == HttpStatus.UNAUTHORIZED.value()) { - LOGGER.debug("Authentication request was denied from the provider [{}]", client.getName(), e); - } - else { - LOGGER.warn(e.getMessage(), e); - } - throw new UnauthorizedServiceException(e.getMessage(), e); - } + + final String url = CommonHelper.addParameter("clientredirect", Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER, client.getName()); + response.sendRedirect(url); final ExternalContext externalContext = requestContext.getExternalContext(); externalContext.recordResponseComplete(); @@ -148,8 +109,7 @@ public class Utils { } public String getSuperUsername(final Authentication authentication) { - final Map<String, Object> authAttributes = authentication.getAttributes(); - final String username = (String) authAttributes.get(SurrogateAuthenticationService.AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL); + final String username = (String) getAttributeValue(authentication.getAttributes() ,SurrogateAuthenticationService.AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL); LOGGER.debug("is it currently a superUser: {}", username); return username; } @@ -180,15 +140,12 @@ public class Utils { return cookie; } - public Object getAttributeValue(final Principal principal, final String key) { - final Object attribute = principal.getAttributes().get(key); - if (attribute instanceof List) { - final List values = (List) attribute; - if (!values.isEmpty()) { - return values.get(0); - } + public Object getAttributeValue(final Map<String, List<Object>> attributes, final String key) { + final List<Object> attributeList = attributes.get(key); + if (attributeList != null && attributeList.size() > 0) { + return attributeList.get(0); } - return attribute; + return null; } public String sanitizePasswordResetUrl(final String url) { diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomDelegatedClientWebflowManager.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomDelegatedClientWebflowManager.java index b0a3739e9d42f9f291ca38321bf974a6d69ab756..4bea0c2e4e5ae4d491a96d45636c160b9937a5b4 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomDelegatedClientWebflowManager.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/CustomDelegatedClientWebflowManager.java @@ -39,12 +39,13 @@ package fr.gouv.vitamui.cas.webflow; import fr.gouv.vitamui.cas.util.Constants; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; import org.apereo.cas.authentication.principal.Service; +import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.ticket.TicketFactory; import org.apereo.cas.ticket.TransientSessionTicket; import org.apereo.cas.ticket.registry.TicketRegistry; import org.apereo.cas.web.DelegatedClientWebflowManager; import org.apereo.cas.web.support.ArgumentExtractor; -import org.pac4j.core.context.J2EContext; +import org.pac4j.core.context.JEEContext; import org.pac4j.core.context.WebContext; import org.springframework.webflow.execution.RequestContext; @@ -60,18 +61,17 @@ public class CustomDelegatedClientWebflowManager extends DelegatedClientWebflowM public CustomDelegatedClientWebflowManager(final TicketRegistry ticketRegistry, final TicketFactory ticketFactory, - final String themeParamName, - final String localParamName, + final CasConfigurationProperties casProperties, final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies, final ArgumentExtractor argumentExtractor) { - super(ticketRegistry, ticketFactory, themeParamName, localParamName, authenticationRequestServiceSelectionStrategies, argumentExtractor); + super(ticketRegistry, ticketFactory, casProperties, authenticationRequestServiceSelectionStrategies, argumentExtractor); } @Override - protected Map<String, Serializable> buildTicketProperties(final J2EContext webContext) { + protected Map<String, Serializable> buildTicketProperties(final JEEContext webContext) { final Map<String, Serializable> properties = super.buildTicketProperties(webContext); - properties.put(Constants.SURROGATE, (String) webContext.getRequestAttribute(Constants.SURROGATE)); + properties.put(Constants.SURROGATE, (String) webContext.getRequestAttribute(Constants.SURROGATE).orElse(null)); return properties; } 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 492ef673873549c2b4e25375fc838d3806eaa8a8..48ae7640a6285bcb27795a0b446cbb97dfca9480 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,6 +41,7 @@ import javax.security.auth.login.AccountNotFoundException; import javax.security.auth.login.CredentialExpiredException; import javax.security.auth.login.FailedLoginException; +import lombok.val; import org.apereo.cas.authentication.PrincipalException; import org.apereo.cas.authentication.adaptive.UnauthorizedAuthenticationException; import org.apereo.cas.authentication.exceptions.AccountDisabledException; @@ -95,12 +96,15 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer @Override protected void createTicketGrantingTicketCheckAction(final Flow flow) { - final ActionState action = createActionState(flow, CasWebflowConstants.STATE_ID_TICKET_GRANTING_TICKET_CHECK, - CasWebflowConstants.ACTION_ID_TICKET_GRANTING_TICKET_CHECK); - createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TGT_NOT_EXISTS, CasWebflowConstants.STATE_ID_GATEWAY_REQUEST_CHECK); - createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TGT_INVALID, CasWebflowConstants.STATE_ID_TERMINATE_SESSION); + val action = createActionState(flow, CasWebflowConstants.STATE_ID_TICKET_GRANTING_TICKET_CHECK, + CasWebflowConstants.ACTION_ID_TICKET_GRANTING_TICKET_CHECK); + createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TICKET_GRANTING_TICKET_NOT_EXISTS, + CasWebflowConstants.STATE_ID_GATEWAY_REQUEST_CHECK); + createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TICKET_GRANTING_TICKET_INVALID, + CasWebflowConstants.STATE_ID_TERMINATE_SESSION); // instead of STATE_ID_HAS_SERVICE_CHECK, send to STATE_ID_TRIGGER_CHANGE_PASSWORD - createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TGT_VALID, STATE_ID_TRIGGER_CHANGE_PASSWORD); + createTransitionForState(action, CasWebflowConstants.TRANSITION_ID_TICKET_GRANTING_TICKET_VALID, + STATE_ID_TRIGGER_CHANGE_PASSWORD); createTriggerChangePasswordAction(flow); } @@ -113,8 +117,8 @@ public class CustomLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer @Override protected void createHandleAuthenticationFailureAction(final Flow flow) { - final ActionState handler = createActionState(flow, CasWebflowConstants.STATE_ID_HANDLE_AUTHN_FAILURE, - CasWebflowConstants.ACTION_ID_AUTHENTICATION_EXCEPTION_HANDLER); + val handler = createActionState(flow, CasWebflowConstants.STATE_ID_HANDLE_AUTHN_FAILURE, + 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: @@ -130,5 +134,6 @@ 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); + } } diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/AutomaticDelegatedClientAuthenticationAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/AutomaticDelegatedClientAuthenticationAction.java index 309223367f3ead6ac8849c378336df5b52eb67ba..c9d4497e217a96e44ba552b782ad3fd76a9095bd 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/AutomaticDelegatedClientAuthenticationAction.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/AutomaticDelegatedClientAuthenticationAction.java @@ -56,11 +56,14 @@ import org.apereo.cas.ticket.TicketGrantingTicket; import org.apereo.cas.ticket.registry.TicketRegistry; import org.apereo.cas.web.DelegatedClientWebflowManager; 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; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; +import org.apereo.cas.web.support.ArgumentExtractor; import org.apereo.cas.web.support.WebUtils; import org.pac4j.core.client.Clients; +import org.pac4j.core.context.JEEContext; +import org.pac4j.core.context.session.SessionStore; import org.pac4j.saml.client.SAML2Client; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.webflow.execution.Event; @@ -70,6 +73,7 @@ import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; +import java.util.List; import java.util.Optional; /** @@ -103,15 +107,16 @@ public class AutomaticDelegatedClientAuthenticationAction extends DelegatedClien final ServicesManager servicesManager, final AuditableExecution delegatedAuthenticationPolicyEnforcer, final DelegatedClientWebflowManager delegatedClientWebflowManager, - final DelegatedSessionCookieManager delegatedSessionCookieManager, final AuthenticationSystemSupport authenticationSystemSupport, - final String localeParamName, - final String themeParamName, + final CasConfigurationProperties casProperties, final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies, - final CentralAuthenticationService centralAuthenticationService) { - super(initialAuthenticationAttemptWebflowEventResolver, serviceTicketRequestWebflowEventResolver, adaptiveAuthenticationPolicy, clients, servicesManager, - delegatedAuthenticationPolicyEnforcer, delegatedClientWebflowManager, delegatedSessionCookieManager, authenticationSystemSupport, - localeParamName, themeParamName, authenticationRequestServiceSelectionStrategies, centralAuthenticationService); + final CentralAuthenticationService centralAuthenticationService, + final SingleSignOnParticipationStrategy singleSignOnParticipationStrategy, + final SessionStore<JEEContext> sessionStore, final List<ArgumentExtractor> argumentExtractors) { + super(initialAuthenticationAttemptWebflowEventResolver, serviceTicketRequestWebflowEventResolver, adaptiveAuthenticationPolicy, + clients, servicesManager, delegatedAuthenticationPolicyEnforcer, delegatedClientWebflowManager, authenticationSystemSupport, + casProperties, authenticationRequestServiceSelectionStrategies, centralAuthenticationService, singleSignOnParticipationStrategy, + sessionStore, argumentExtractors); } @Override 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 5021c2beba9695722ab5c4c0338409e385a57e39..9d658b5eabc3941d2ddd6e69bd537d136db64c81 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 @@ -40,13 +40,16 @@ import fr.gouv.vitamui.cas.util.Constants; import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; import org.apache.commons.lang.StringUtils; +import org.apereo.cas.authentication.AuthenticationEventExecutionPlan; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.ServicesManager; +import org.apereo.cas.ticket.registry.TicketRegistrySupport; +import org.apereo.cas.web.cookie.CasCookieBuilder; +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.CookieRetrievingCookieGenerator; import org.apereo.cas.web.support.WebUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.webflow.core.collection.MutableAttributeMap; @@ -82,13 +85,16 @@ public class CustomInitialFlowSetupAction extends InitialFlowSetupAction { private String vitamuiLargeLogoPath; public CustomInitialFlowSetupAction(final List<ArgumentExtractor> argumentExtractors, - final ServicesManager servicesManager, - final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionPlan, - final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator, - final CookieRetrievingCookieGenerator warnCookieGenerator, - final CasConfigurationProperties casProperties) { - super(argumentExtractors, servicesManager, authenticationRequestServiceSelectionPlan, - ticketGrantingTicketCookieGenerator, warnCookieGenerator, casProperties); + final ServicesManager servicesManager, + final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies, + final CasCookieBuilder ticketGrantingTicketCookieGenerator, + final CasCookieBuilder warnCookieGenerator, + final CasConfigurationProperties casProperties, + final AuthenticationEventExecutionPlan authenticationEventExecutionPlan, + final SingleSignOnParticipationStrategy renewalStrategy, + final TicketRegistrySupport ticketRegistrySupport) { + super(argumentExtractors, servicesManager, authenticationRequestServiceSelectionStrategies, ticketGrantingTicketCookieGenerator, + warnCookieGenerator, casProperties, authenticationEventExecutionPlan, renewalStrategy, ticketRegistrySupport); } @Override 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 13374315d152624c2d79924793cdedbd2f8009b8..d1ee36d49f51ff2b2dbae3bdaceb12e63a101f85 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 @@ -52,7 +52,7 @@ import lombok.Getter; import lombok.Setter; import org.apache.commons.lang.StringUtils; import org.apereo.cas.CasProtocolConstants; -import org.apereo.cas.authentication.UsernamePasswordCredential; +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; @@ -103,7 +103,7 @@ public class DispatcherAction extends AbstractAction { @Override protected Event doExecute(final RequestContext requestContext) throws IOException { - final UsernamePasswordCredential credential = (UsernamePasswordCredential) WebUtils.getCredential(requestContext); + final UsernamePasswordCredential credential = WebUtils.getCredential(requestContext, UsernamePasswordCredential.class); final String username = credential.getUsername().toLowerCase(); String dispatchedUser = username; String surrogate = null; diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionAction.java index 4242020bce4c65912a156bc8e53a71de6534d82d..9803b129cba3ac37824318314eaf228b69ab0b06 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionAction.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionAction.java @@ -56,16 +56,16 @@ import org.apereo.cas.authentication.principal.SimpleWebApplicationServiceImpl; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.configuration.model.core.logout.LogoutProperties; import org.apereo.cas.logout.LogoutManager; -import org.apereo.cas.logout.LogoutRequest; +import org.apereo.cas.logout.slo.SingleLogoutRequest; import org.apereo.cas.services.RegisteredService; import org.apereo.cas.services.ServicesManager; import org.apereo.cas.ticket.InvalidTicketException; import org.apereo.cas.ticket.TicketGrantingTicket; import org.apereo.cas.ticket.TicketGrantingTicketImpl; -import org.apereo.cas.ticket.support.NeverExpiresExpirationPolicy; +import org.apereo.cas.ticket.expiration.NeverExpiresExpirationPolicy; +import org.apereo.cas.web.cookie.CasCookieBuilder; import org.apereo.cas.web.flow.logout.FrontChannelLogoutAction; import org.apereo.cas.web.flow.logout.TerminateSessionAction; -import org.apereo.cas.web.support.CookieRetrievingCookieGenerator; import org.apereo.cas.web.support.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.webflow.execution.Event; @@ -73,7 +73,6 @@ import org.springframework.webflow.execution.RequestContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.net.URL; import java.util.*; import static fr.gouv.vitamui.commons.api.CommonConstants.*; @@ -108,8 +107,8 @@ public class GeneralTerminateSessionAction extends TerminateSessionAction { private FrontChannelLogoutAction frontChannelLogoutAction; public GeneralTerminateSessionAction(final CentralAuthenticationService centralAuthenticationService, - final CookieRetrievingCookieGenerator ticketGrantingTicketCookieGenerator, - final CookieRetrievingCookieGenerator warnCookieGenerator, + final CasCookieBuilder ticketGrantingTicketCookieGenerator, + final CasCookieBuilder warnCookieGenerator, final LogoutProperties logoutProperties) { super(centralAuthenticationService, ticketGrantingTicketCookieGenerator, warnCookieGenerator, logoutProperties); } @@ -130,9 +129,10 @@ public class GeneralTerminateSessionAction extends TerminateSessionAction { if (ticket != null) { final Principal principal = ticket.getAuthentication().getPrincipal(); - final String authToken = (String) utils.getAttributeValue(principal, AUTHTOKEN_ATTRIBUTE); - final String principalEmail = (String) utils.getAttributeValue(principal, EMAIL_ATTRIBUTE); - final String emailSuperUser = (String) utils.getAttributeValue(principal, SUPER_USER_ATTRIBUTE); + final Map<String, List<Object>> attributes = principal.getAttributes(); + final String authToken = (String) utils.getAttributeValue(attributes, AUTHTOKEN_ATTRIBUTE); + final String principalEmail = (String) utils.getAttributeValue(attributes, EMAIL_ATTRIBUTE); + final String emailSuperUser = (String) utils.getAttributeValue(attributes, SUPER_USER_ATTRIBUTE); final ExternalHttpContext externalHttpContext; if (StringUtils.isNotBlank(emailSuperUser)) { @@ -158,12 +158,12 @@ public class GeneralTerminateSessionAction extends TerminateSessionAction { // fallback cases: // no CAS cookie -> general logout if (tgtId == null) { - final List<LogoutRequest> logoutRequests = performGeneralLogout("nocookie"); + final List<SingleLogoutRequest> logoutRequests = performGeneralLogout("nocookie"); WebUtils.putLogoutRequests(context, logoutRequests); // no ticket or expired -> general logout } else if (ticket == null || ticket.isExpired()) { - final List<LogoutRequest> logoutRequests = performGeneralLogout(tgtId); + final List<SingleLogoutRequest> logoutRequests = performGeneralLogout(tgtId); WebUtils.putLogoutRequests(context, logoutRequests); } @@ -176,7 +176,7 @@ public class GeneralTerminateSessionAction extends TerminateSessionAction { return event; } - protected List<LogoutRequest> performGeneralLogout(final String tgtId) { + protected List<SingleLogoutRequest> performGeneralLogout(final String tgtId) { try { final Map<String, AuthenticationHandlerExecutionResult> successes = new HashMap<>(); @@ -193,7 +193,7 @@ public class GeneralTerminateSessionAction extends TerminateSessionAction { final Collection<RegisteredService> registeredServices = servicesManager.getAllServices(); int i = 1; for (final RegisteredService registeredService : registeredServices) { - final URL logoutUrl = registeredService.getLogoutUrl(); + final String logoutUrl = registeredService.getLogoutUrl(); if (logoutUrl != null) { final String serviceId = logoutUrl.toString(); final String fakeSt = "ST-fake-" + i; 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 ebe72120fa4551c21456135791893f58d2ada74c..6792accc954d3a0e7e30997cc20c01b1eccf447e 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 @@ -42,12 +42,15 @@ import fr.gouv.vitamui.cas.util.Utils; 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 lombok.val; import org.apache.commons.lang3.StringUtils; -import org.apereo.cas.authentication.UsernamePasswordCredential; +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; @@ -96,9 +99,11 @@ public class I18NSendPasswordResetInstructionsAction extends SendPasswordResetIn private final PasswordManagementService passwordManagementService; public I18NSendPasswordResetInstructionsAction(final CasConfigurationProperties casProperties, - final CommunicationsManager communicationsManager, - final PasswordManagementService passwordManagementService) { - super(casProperties, communicationsManager, passwordManagementService); + final CommunicationsManager communicationsManager, + final PasswordManagementService passwordManagementService, + final TicketRegistry ticketRegistry, + final TicketFactory ticketFactory) { + super(casProperties, communicationsManager, passwordManagementService, ticketRegistry, ticketFactory); this.casProperties = casProperties; this.communicationsManager = communicationsManager; this.passwordManagementService = passwordManagementService; @@ -141,7 +146,8 @@ public class I18NSendPasswordResetInstructionsAction extends SendPasswordResetIn return success(); } - final String url = buildPasswordResetUrl(username, passwordManagementService, casProperties); + 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)", utils.sanitizePasswordResetUrl(url), pm.getReset().getExpirationMinutes()); diff --git a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/TriggerChangePasswordAction.java b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/TriggerChangePasswordAction.java index d1f88e8f11ae5f6504ee54d19e9594fb599dc45c..0bcba810baa5b906b934d822e3b7dc1219b09af1 100644 --- a/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/TriggerChangePasswordAction.java +++ b/cas/cas-server/src/main/java/fr/gouv/vitamui/cas/webflow/actions/TriggerChangePasswordAction.java @@ -40,7 +40,7 @@ import fr.gouv.vitamui.cas.util.Utils; import fr.gouv.vitamui.commons.api.CommonConstants; import fr.gouv.vitamui.commons.api.logger.VitamUILogger; import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.pm.web.flow.PasswordManagementWebflowConfigurer; import org.apereo.cas.ticket.registry.TicketRegistrySupport; @@ -79,7 +79,7 @@ public class TriggerChangePasswordAction extends AbstractAction { // and a specific property: pswdChangePostLogin in the flow final RequestContext requestContext = RequestContextHolder.getRequestContext(); final Principal principal = WebUtils.getPrincipalFromRequestContext(requestContext, ticketRegistrySupport); - final String username = (String) utils.getAttributeValue(principal, CommonConstants.EMAIL_ATTRIBUTE); + final String username = (String) utils.getAttributeValue(principal.getAttributes(), CommonConstants.EMAIL_ATTRIBUTE); final UsernamePasswordCredential credential = new UsernamePasswordCredential(username, null); WebUtils.putCredential(requestContext, credential); requestContext.getFlowScope().put("pswdChangePostLogin", true); diff --git a/cas/cas-server/src/main/java/org/apereo/cas/CasEmbeddedContainerUtils.java b/cas/cas-server/src/main/java/org/apereo/cas/CasEmbeddedContainerUtils.java index faf707971f5e029563149b143b6126395ed13c49..eb89f47e5fdb0634980e7f75458722040978288d 100644 --- a/cas/cas-server/src/main/java/org/apereo/cas/CasEmbeddedContainerUtils.java +++ b/cas/cas-server/src/main/java/org/apereo/cas/CasEmbeddedContainerUtils.java @@ -1,23 +1,23 @@ package org.apereo.cas; +import lombok.experimental.UtilityClass; +import lombok.val; import org.apereo.cas.util.spring.boot.AbstractCasBanner; import org.springframework.boot.Banner; -import java.util.LinkedHashMap; +import java.util.HashMap; import java.util.Map; /** - * Copy/pasted from the original CAS class, removing the log4j2 stuffs and directly using a custom banner (avoid the search by reflection). + * Copy/pasted from the original CAS class, directly using a custom banner (avoid the search by reflection). */ -public final class CasEmbeddedContainerUtils { +@UtilityClass +public class CasEmbeddedContainerUtils { /** * Property to dictate to the environment whether embedded container is running CAS. */ public static final String EMBEDDED_CONTAINER_CONFIG_ACTIVE = "CasEmbeddedContainerConfigurationActive"; - private CasEmbeddedContainerUtils() { - } - /** * Gets runtime properties. * @@ -25,7 +25,7 @@ public final class CasEmbeddedContainerUtils { * @return the runtime properties */ public static Map<String, Object> getRuntimeProperties(final Boolean embeddedContainerActive) { - final Map<String, Object> properties = new LinkedHashMap<>(); + val properties = new HashMap<String, Object>(); properties.put(EMBEDDED_CONTAINER_CONFIG_ACTIVE, embeddedContainerActive); return properties; } diff --git a/cas/cas-server/src/main/java/org/apereo/cas/config/CasMetricsConfiguration.java b/cas/cas-server/src/main/java/org/apereo/cas/config/CasMetricsConfiguration.java deleted file mode 100644 index a782e9b4ef4ffccead65c4c41c6835a17808336d..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/org/apereo/cas/config/CasMetricsConfiguration.java +++ /dev/null @@ -1,45 +0,0 @@ -package org.apereo.cas.config; - -import com.codahale.metrics.MetricRegistry; -import com.codahale.metrics.health.HealthCheckRegistry; -import com.ryantenney.metrics.spring.config.annotation.MetricsConfigurerAdapter; -import org.springframework.cloud.context.config.annotation.RefreshScope; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * Copy/pasted from the origin CAS class. Disable EnableMetrics, clean the MetricsRegistry and remove the reporters. - */ -@Configuration("casMetricsConfiguration") -public class CasMetricsConfiguration extends MetricsConfigurerAdapter { - - /** - * Metric registry metric registry. - * - * @return the metric registry - */ - @RefreshScope - @Bean - public MetricRegistry metrics() { - return new MetricRegistry(); - } - - @Bean - public HealthCheckRegistry healthCheckMetrics() { - return new HealthCheckRegistry(); - } - - @Override - public MetricRegistry getMetricRegistry() { - return metrics(); - } - - @Override - public HealthCheckRegistry getHealthCheckRegistry() { - return healthCheckMetrics(); - } - - @Override - public void configureReporters(final MetricRegistry metricRegistry) { - } -} diff --git a/cas/cas-server/src/main/java/org/apereo/cas/config/CasWebAppConfiguration.java b/cas/cas-server/src/main/java/org/apereo/cas/config/CasWebAppConfiguration.java deleted file mode 100644 index 05f64da9af660c6a82ad8b5a142edac5058f4b8f..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/org/apereo/cas/config/CasWebAppConfiguration.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.apereo.cas.config; - -import org.apache.commons.lang3.StringUtils; -import org.apereo.cas.configuration.CasConfigurationProperties; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -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; -import org.springframework.context.annotation.Lazy; -import org.springframework.web.servlet.LocaleResolver; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; -import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping; -import org.springframework.web.servlet.i18n.CookieLocaleResolver; -import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; -import org.springframework.web.servlet.mvc.Controller; -import org.springframework.web.servlet.mvc.ParameterizableViewController; -import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter; -import org.springframework.web.servlet.mvc.UrlFilenameViewController; -import org.springframework.web.servlet.theme.ThemeChangeInterceptor; -import org.springframework.web.servlet.view.RedirectView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -/** - * Copy/pasted from the CAS server, excluding the log4j stuff. - * - * - */ -@Configuration("casWebAppConfiguration") -@EnableConfigurationProperties(CasConfigurationProperties.class) -public class CasWebAppConfiguration extends WebMvcConfigurerAdapter { - - @Autowired - private CasConfigurationProperties casProperties; - - @Autowired - @Qualifier("localeChangeInterceptor") - private LocaleChangeInterceptor localeChangeInterceptor; - - @RefreshScope - @Bean - @Lazy - public ThemeChangeInterceptor themeChangeInterceptor() { - final ThemeChangeInterceptor bean = new ThemeChangeInterceptor(); - bean.setParamName(casProperties.getTheme().getParamName()); - return bean; - } - - @ConditionalOnMissingBean(name = "localeResolver") - @Bean - @Lazy - public LocaleResolver localeResolver() { - final CookieLocaleResolver bean = new CookieLocaleResolver() { - @Override - protected Locale determineDefaultLocale(final HttpServletRequest request) { - final Locale locale = request.getLocale(); - if (StringUtils.isBlank(casProperties.getLocale().getDefaultValue()) - || !locale.getLanguage().equals(casProperties.getLocale().getDefaultValue())) { - return locale; - } - return new Locale(casProperties.getLocale().getDefaultValue()); - } - }; - return bean; - } - - @Bean - @Lazy - protected UrlFilenameViewController passThroughController() { - return new UrlFilenameViewController(); - } - - @Bean - protected Controller rootController() { - return new ParameterizableViewController() { - @Override - protected ModelAndView handleRequestInternal(final HttpServletRequest request, - final HttpServletResponse response) { - final String queryString = request.getQueryString(); - final String url = request.getContextPath() + "/login" - + (queryString != null ? '?' + queryString : StringUtils.EMPTY); - return new ModelAndView(new RedirectView(response.encodeURL(url))); - } - - }; - } - - @Bean - @Lazy - public SimpleUrlHandlerMapping handlerMapping() { - final SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); - - final Controller root = rootController(); - mapping.setOrder(1); - mapping.setAlwaysUseFullPath(true); - mapping.setRootHandler(root); - final Map urls = new HashMap(); - urls.put("/", root); - - mapping.setUrlMap(urls); - return mapping; - } - - @Bean - @Lazy - public SimpleControllerHandlerAdapter simpleControllerHandlerAdapter() { - return new SimpleControllerHandlerAdapter(); - } - - @Override - public void addInterceptors(final InterceptorRegistry registry) { - registry.addInterceptor(localeChangeInterceptor) - .addPathPatterns("/**"); - } -} diff --git a/cas/cas-server/src/main/java/org/apereo/cas/config/SurrogateRestAuthenticationConfiguration.java b/cas/cas-server/src/main/java/org/apereo/cas/config/SurrogateRestAuthenticationConfiguration.java index 01b08e8a9c1017669002e14e48f007dbde3cd31b..fe2eff0c580df22f6be050bfb04da59dc911ffe6 100644 --- a/cas/cas-server/src/main/java/org/apereo/cas/config/SurrogateRestAuthenticationConfiguration.java +++ b/cas/cas-server/src/main/java/org/apereo/cas/config/SurrogateRestAuthenticationConfiguration.java @@ -1,8 +1,10 @@ package org.apereo.cas.config; +import lombok.extern.slf4j.Slf4j; import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.ServicesManager; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.EnableConfigurationProperties; @@ -18,16 +20,14 @@ import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; * * */ -@Configuration("surrogateRestAuthenticationConfiguration") +@Configuration(value = "surrogateRestAuthenticationConfiguration", proxyBeanMethods = false) @EnableConfigurationProperties(CasConfigurationProperties.class) +@Slf4j public class SurrogateRestAuthenticationConfiguration { @Autowired @Qualifier("servicesManager") - private ServicesManager servicesManager; - - @Autowired - private CasConfigurationProperties casProperties; + private ObjectProvider<ServicesManager> servicesManager; // customisation JLE: @Autowired @@ -37,9 +37,6 @@ public class SurrogateRestAuthenticationConfiguration { @Bean public SurrogateAuthenticationService surrogateAuthenticationService() { // customisation JLE: - /*final SurrogateAuthenticationProperties su = casProperties.getAuthn().getSurrogate(); - LOGGER.debug("Using REST endpoint [{}] with method [{}] to locate surrogate accounts", - su.getRest().getUrl(), su.getRest().getMethod());*/ - return new IamSurrogateRestAuthenticationService(casExternalRestClient, servicesManager); + return new IamSurrogateRestAuthenticationService(casExternalRestClient, servicesManager.getObject()); } } 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 index d89b503e8ef3250525063d0082a8521dbfff337e..fd739b754e504bbd80a52af6bbcc9909c37db974 100644 --- 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 @@ -1,30 +1,41 @@ 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.PasswordHistoryService; import org.apereo.cas.pm.PasswordManagementService; +import org.apereo.cas.util.crypto.CipherExecutor; + +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; 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; -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; - /** * Overrides the configuration class from the CAS server, using the CasExternalRestClient. * * */ -@Configuration("restPasswordManagementConfiguration") +@Configuration(value = "restPasswordManagementConfiguration", proxyBeanMethods = false) @EnableConfigurationProperties(CasConfigurationProperties.class) public class RestPasswordManagementConfiguration { - @Autowired private CasConfigurationProperties casProperties; + @Autowired + @Qualifier("passwordManagementCipherExecutor") + private ObjectProvider<CipherExecutor> passwordManagementCipherExecutor; + + @Autowired + @Qualifier("passwordHistoryService") + private ObjectProvider<PasswordHistoryService> passwordHistoryService; + // customisation JLE: @Autowired private CasExternalRestClient casExternalRestClient; @@ -38,7 +49,6 @@ public class RestPasswordManagementConfiguration { @RefreshScope @Bean public PasswordManagementService passwordChangeService() { - // customisation JLE: return new IamRestPasswordManagementService(casExternalRestClient, casProperties.getAuthn().getPm(), providersService, identityProviderHelper); } } diff --git a/cas/cas-server/src/main/java/org/apereo/cas/web/flow/DelegatedClientAuthenticationAction.java b/cas/cas-server/src/main/java/org/apereo/cas/web/flow/DelegatedClientAuthenticationAction.java deleted file mode 100644 index c060d43932595184f1b552d33e6ac50d39993517..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/org/apereo/cas/web/flow/DelegatedClientAuthenticationAction.java +++ /dev/null @@ -1,481 +0,0 @@ -package org.apereo.cas.web.flow; - -import java.io.IOException; -import java.io.Serializable; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.apereo.cas.CasProtocolConstants; -import org.apereo.cas.CentralAuthenticationService; -import org.apereo.cas.audit.AuditableContext; -import org.apereo.cas.audit.AuditableExecution; -import org.apereo.cas.audit.AuditableExecutionResult; -import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; -import org.apereo.cas.authentication.AuthenticationSystemSupport; -import org.apereo.cas.authentication.adaptive.AdaptiveAuthenticationPolicy; -import org.apereo.cas.authentication.principal.ClientCredential; -import org.apereo.cas.authentication.principal.ClientCustomPropertyConstants; -import org.apereo.cas.authentication.principal.Service; -import org.apereo.cas.authentication.principal.WebApplicationService; -import org.apereo.cas.services.RegisteredService; -import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.services.UnauthorizedServiceException; -import org.apereo.cas.support.pac4j.logout.RequestSloException; -import org.apereo.cas.ticket.AbstractTicketException; -import org.apereo.cas.ticket.Ticket; -import org.apereo.cas.util.CollectionUtils; -import org.apereo.cas.util.Pac4jUtils; -import org.apereo.cas.web.DelegatedClientNavigationController; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.flow.actions.AbstractAuthenticationAction; -import org.apereo.cas.web.flow.resolver.CasDelegatingWebflowEventResolver; -import org.apereo.cas.web.flow.resolver.CasWebflowEventResolver; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; -import org.apereo.cas.web.support.WebUtils; -import org.pac4j.core.client.BaseClient; -import org.pac4j.core.client.Client; -import org.pac4j.core.client.Clients; -import org.pac4j.core.client.IndirectClient; -import org.pac4j.core.context.J2EContext; -import org.pac4j.core.context.Pac4jConstants; -import org.pac4j.core.context.WebContext; -import org.pac4j.core.credentials.Credentials; -import org.pac4j.core.profile.CommonProfile; -import org.pac4j.saml.metadata.SAML2ServiceProviderMetadataResolver; -import org.springframework.http.HttpStatus; -import org.springframework.web.servlet.ModelAndView; -import org.springframework.web.util.UriComponentsBuilder; -import org.springframework.webflow.execution.Event; -import org.springframework.webflow.execution.RequestContext; - -import fr.gouv.vitamui.commons.api.logger.VitamUILogger; -import fr.gouv.vitamui.commons.api.logger.VitamUILoggerFactory; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -/** - * Copy/pasted from the original CAS class with a fix. - * - * - */ -@Getter -public class DelegatedClientAuthenticationAction extends AbstractAuthenticationAction { - - private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(DelegatedClientAuthenticationAction.class); - - /** - * All the urls and names of the pac4j clients. - */ - public static final String PAC4J_URLS = "pac4jUrls"; - - private static final Pattern PAC4J_CLIENT_SUFFIX_PATTERN = Pattern.compile("Client\\d*"); - - private static final Pattern PAC4J_CLIENT_CSS_CLASS_SUBSTITUTION_PATTERN = Pattern.compile("\\W"); - - /** - * The Clients. - */ - protected final Clients clients; - - /** - * The Services manager. - */ - protected final ServicesManager servicesManager; - - /** - * The Delegated authentication policy enforcer. - */ - protected final AuditableExecution delegatedAuthenticationPolicyEnforcer; - - /** - * The Delegated client webflow manager. - */ - protected final DelegatedClientWebflowManager delegatedClientWebflowManager; - - /** - * The Delegated session cookie manager. - */ - protected final DelegatedSessionCookieManager delegatedSessionCookieManager; - - /** - * The Authentication system support. - */ - protected final AuthenticationSystemSupport authenticationSystemSupport; - - /** - * The Locale param name. - */ - protected final String localeParamName; - - /** - * The Theme param name. - */ - protected final String themeParamName; - - private final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies; - - private final CentralAuthenticationService centralAuthenticationService; - - public DelegatedClientAuthenticationAction(final CasDelegatingWebflowEventResolver initialAuthenticationAttemptWebflowEventResolver, - final CasWebflowEventResolver serviceTicketRequestWebflowEventResolver, final AdaptiveAuthenticationPolicy adaptiveAuthenticationPolicy, - final Clients clients, final ServicesManager servicesManager, final AuditableExecution delegatedAuthenticationPolicyEnforcer, - final DelegatedClientWebflowManager delegatedClientWebflowManager, final DelegatedSessionCookieManager delegatedSessionCookieManager, - final AuthenticationSystemSupport authenticationSystemSupport, final String localeParamName, final String themeParamName, - final AuthenticationServiceSelectionPlan authenticationRequestServiceSelectionStrategies, - final CentralAuthenticationService centralAuthenticationService) { - super(initialAuthenticationAttemptWebflowEventResolver, serviceTicketRequestWebflowEventResolver, adaptiveAuthenticationPolicy); - this.clients = clients; - this.servicesManager = servicesManager; - this.delegatedAuthenticationPolicyEnforcer = delegatedAuthenticationPolicyEnforcer; - this.delegatedClientWebflowManager = delegatedClientWebflowManager; - this.delegatedSessionCookieManager = delegatedSessionCookieManager; - this.authenticationSystemSupport = authenticationSystemSupport; - this.localeParamName = localeParamName; - this.themeParamName = themeParamName; - this.authenticationRequestServiceSelectionStrategies = authenticationRequestServiceSelectionStrategies; - this.centralAuthenticationService = centralAuthenticationService; - } - - @Override - public Event doExecute(final RequestContext context) { - final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context); - final HttpServletResponse response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context); - - /** - * - * JLE FIX : - * - */ - /*if (singleSignOnSessionExists(context)) { - LOGGER.debug("An existing single sign-on session already exists. Skipping delegation and routing back to CAS authentication flow"); - return super.doExecute(context); - }*/ - - final String clientName = request.getParameter(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER); - LOGGER.debug("Delegated authentication is handled by client name [{}]", clientName); - if (hasDelegationRequestFailed(request, response.getStatus()).isPresent()) { - throw new IllegalArgumentException("Delegated authentication has failed with client " + clientName); - } - - final String logoutEndpoint = request.getParameter(SAML2ServiceProviderMetadataResolver.LOGOUT_ENDPOINT_PARAMETER); - final J2EContext webContext = Pac4jUtils.getPac4jJ2EContext(request, response); - if (StringUtils.isNotBlank(clientName)) { - final Service service; - if (StringUtils.isBlank(logoutEndpoint)) { - service = restoreAuthenticationRequestInContext(context, webContext, clientName); - } - else { - service = null; - } - final BaseClient<Credentials, CommonProfile> client = findDelegatedClientByName(request, clientName, service); - - final Credentials credentials; - try { - credentials = client.getCredentials(webContext); - LOGGER.debug("Retrieved credentials from client as [{}]", credentials); - if (credentials == null) { - throw new IllegalArgumentException("Unable to determine credentials from the context with client " + client.getName()); - } - } - catch (final Exception e) { - return handleException(webContext, client, e); - } - - final ClientCredential clientCredential = new ClientCredential(credentials, client.getName()); - WebUtils.putCredential(context, clientCredential); - WebUtils.putService(context, service); - final Service resolvedService = authenticationRequestServiceSelectionStrategies.resolveService(service); - final RegisteredService registeredService = servicesManager.findServiceBy(resolvedService); - WebUtils.putRegisteredService(context, registeredService); - return super.doExecute(context); - } - - prepareForLoginPage(context); - - if (response.getStatus() == HttpStatus.UNAUTHORIZED.value()) { - return stopWebflow(); - } - return error(); - } - - /** - * Handle the thrown exception. - * - * @param webContext the web context - * @param client the authentication client - * @param e the thrown exception - * @return the event to trigger - */ - protected Event handleException(final J2EContext webContext, final BaseClient<Credentials, CommonProfile> client, final Exception e) { - if (e instanceof RequestSloException) { - try { - webContext.getResponse().sendRedirect("logout"); - } - catch (final IOException ioe) { - throw new IllegalArgumentException("Unable to call logout", ioe); - } - return stopWebflow(); - } - else { - LOGGER.info(e.getMessage(), e); - throw new IllegalArgumentException("Delegated authentication has failed with client " + client.getName()); - } - } - - /** - * Find delegated client by name base client. - * - * @param request the request - * @param clientName the client name - * @param service the service - * @return the base client - */ - protected BaseClient<Credentials, CommonProfile> findDelegatedClientByName(final HttpServletRequest request, final String clientName, - final Service service) { - final BaseClient<Credentials, CommonProfile> client = (BaseClient<Credentials, CommonProfile>) clients.findClient(clientName); - LOGGER.debug("Delegated authentication client is [{}] with service [{}}", client, service); - if (service != null) { - request.setAttribute(CasProtocolConstants.PARAMETER_SERVICE, service.getId()); - if (!isDelegatedClientAuthorizedForService(client, service)) { - LOGGER.warn("Delegated client [{}] is not authorized by service [{}]", client, service); - throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, StringUtils.EMPTY); - } - } - return client; - } - - /** - * Prepare the data for the login page. - * - * @param context The current webflow context - */ - protected void prepareForLoginPage(final RequestContext context) { - final WebApplicationService currentService = WebUtils.getService(context); - final WebApplicationService service = authenticationRequestServiceSelectionStrategies.resolveService(currentService, WebApplicationService.class); - - final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(context); - final HttpServletResponse response = WebUtils.getHttpServletResponseFromExternalWebflowContext(context); - final WebContext webContext = Pac4jUtils.getPac4jJ2EContext(request, response); - - final Set<ProviderLoginPageConfiguration> urls = new LinkedHashSet<>(); - clients.findAllClients().stream().filter(client -> client instanceof IndirectClient && isDelegatedClientAuthorizedForService(client, service)) - .map(IndirectClient.class::cast).forEach(client -> { - try { - final Optional<ProviderLoginPageConfiguration> provider = buildProviderConfiguration(client, webContext, currentService); - provider.ifPresent(urls::add); - } - catch (final Exception e) { - LOGGER.error("Cannot process client [{}]", client, e); - } - }); - if (!urls.isEmpty()) { - context.getFlowScope().put(PAC4J_URLS, urls); - } - else if (response.getStatus() != HttpStatus.UNAUTHORIZED.value()) { - LOGGER.warn("No delegated authentication providers could be determined based on the provided configuration. " - + "Either no clients are configured, or the current access strategy rules prohibit CAS from using authentication providers for this request."); - } - } - - /** - * Build provider configuration optional. - * - * @param client the client - * @param webContext the web context - * @param service the service - * @return the optional - */ - protected Optional<ProviderLoginPageConfiguration> buildProviderConfiguration(final IndirectClient client, final WebContext webContext, - final WebApplicationService service) { - final String name = client.getName(); - final Matcher matcher = PAC4J_CLIENT_SUFFIX_PATTERN.matcher(client.getClass().getSimpleName()); - final String type = matcher.replaceAll(StringUtils.EMPTY).toLowerCase(); - final UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUriString(DelegatedClientNavigationController.ENDPOINT_REDIRECT) - .queryParam(Pac4jConstants.DEFAULT_CLIENT_NAME_PARAMETER, name); - - if (service != null) { - final String sourceParam = service.getSource(); - final String serviceParam = service.getOriginalUrl(); - if (StringUtils.isNotBlank(sourceParam) && StringUtils.isNotBlank(serviceParam)) { - uriBuilder.queryParam(sourceParam, serviceParam); - } - } - - final String methodParam = webContext.getRequestParameter(CasProtocolConstants.PARAMETER_METHOD); - if (StringUtils.isNotBlank(methodParam)) { - uriBuilder.queryParam(CasProtocolConstants.PARAMETER_METHOD, methodParam); - } - final String localeParam = webContext.getRequestParameter(localeParamName); - if (StringUtils.isNotBlank(localeParam)) { - uriBuilder.queryParam(localeParamName, localeParam); - } - final String themeParam = webContext.getRequestParameter(themeParamName); - if (StringUtils.isNotBlank(themeParam)) { - uriBuilder.queryParam(themeParamName, themeParam); - } - final String redirectUrl = uriBuilder.toUriString(); - final boolean autoRedirect = (Boolean) client.getCustomProperties().getOrDefault(ClientCustomPropertyConstants.CLIENT_CUSTOM_PROPERTY_AUTO_REDIRECT, - Boolean.FALSE); - final ProviderLoginPageConfiguration p = new ProviderLoginPageConfiguration(name, redirectUrl, type, getCssClass(name), autoRedirect); - return Optional.of(p); - } - - /** - * Get a valid CSS class for the given provider name. - * - * @param name Name of the provider - * @return the css class - */ - protected String getCssClass(final String name) { - String computedCssClass = "fa fa-lock"; - if (StringUtils.isNotBlank(name)) { - computedCssClass = computedCssClass.concat(' ' + PAC4J_CLIENT_CSS_CLASS_SUBSTITUTION_PATTERN.matcher(name).replaceAll("-")); - } - LOGGER.debug("cssClass for [{}] is [{}]", name, computedCssClass); - return computedCssClass; - } - - /** - * Stop webflow event. - * - * @return the event - */ - protected Event stopWebflow() { - return new Event(this, CasWebflowConstants.TRANSITION_ID_STOP); - } - - /** - * Determine if request has errors. - * - * @param request the request - * @param status the status - * @return the optional model and view, if request is an error. - */ - public static Optional<ModelAndView> hasDelegationRequestFailed(final HttpServletRequest request, final int status) { - final Map<String, String[]> params = request.getParameterMap(); - if (params.containsKey("error") || params.containsKey("error_code") || params.containsKey("error_description") || params.containsKey("error_message")) { - final Map<String, Object> model = new HashMap<>(); - if (params.containsKey("error_code")) { - model.put("code", StringEscapeUtils.escapeHtml4(request.getParameter("error_code"))); - } - else { - model.put("code", status); - } - model.put("error", StringEscapeUtils.escapeHtml4(request.getParameter("error"))); - model.put("reason", StringEscapeUtils.escapeHtml4(request.getParameter("error_reason"))); - if (params.containsKey("error_description")) { - model.put("description", StringEscapeUtils.escapeHtml4(request.getParameter("error_description"))); - } - else if (params.containsKey("error_message")) { - model.put("description", StringEscapeUtils.escapeHtml4(request.getParameter("error_message"))); - } - model.put(CasProtocolConstants.PARAMETER_SERVICE, request.getAttribute(CasProtocolConstants.PARAMETER_SERVICE)); - model.put("client", StringEscapeUtils.escapeHtml4(request.getParameter("client_name"))); - LOGGER.debug("Delegation request has failed. Details are [{}]", model); - return Optional.of(new ModelAndView("casPac4jStopWebflow", model)); - } - return Optional.empty(); - } - - /** - * Is delegated client authorized for service boolean. - * - * @param client the client - * @param service the service - * @return the boolean - */ - protected boolean isDelegatedClientAuthorizedForService(final Client client, final Service service) { - if (service == null || StringUtils.isBlank(service.getId())) { - LOGGER.debug("Can not evaluate delegated authentication policy since no service was provided in the request while processing client [{}]", client); - return true; - } - - final RegisteredService registeredService = servicesManager.findServiceBy(service); - if (registeredService == null || !registeredService.getAccessStrategy().isServiceAccessAllowed()) { - LOGGER.warn("Service access for [{}] is denied", registeredService); - return false; - } - LOGGER.debug("Located registered service definition [{}] matching [{}]", registeredService, service); - final AuditableContext context = AuditableContext.builder().registeredService(registeredService) - .properties(CollectionUtils.wrap(Client.class.getSimpleName(), client.getName())).build(); - final AuditableExecutionResult result = delegatedAuthenticationPolicyEnforcer.execute(context); - if (!result.isExecutionFailure()) { - LOGGER.debug("Delegated authentication policy for [{}] allows for using client [{}]", registeredService, client); - return true; - } - LOGGER.warn("Delegated authentication policy for [{}] refuses access to client [{}]", registeredService.getServiceId(), client); - return false; - } - - /** - * Restore authentication request in context service. - * - * @param requestContext the request context - * @param webContext the web context - * @param clientName the client name - * @return the service - */ - protected Service restoreAuthenticationRequestInContext(final RequestContext requestContext, final J2EContext webContext, final String clientName) { - try { - delegatedSessionCookieManager.restore(webContext); - final BaseClient<Credentials, CommonProfile> client = (BaseClient<Credentials, CommonProfile>) clients.findClient(clientName); - return delegatedClientWebflowManager.retrieve(requestContext, webContext, client); - } - catch (final Exception e) { - LOGGER.error(e.getMessage(), e); - } - throw new UnauthorizedServiceException(UnauthorizedServiceException.CODE_UNAUTHZ_SERVICE, "Service unauthorized"); - } - - private boolean singleSignOnSessionExists(final RequestContext requestContext) { - final String tgtId = WebUtils.getTicketGrantingTicketId(requestContext); - if (StringUtils.isBlank(tgtId)) { - LOGGER.trace("No ticket-granting ticket could be located in the webflow context"); - return false; - } - try { - final Ticket ticket = centralAuthenticationService.getTicket(tgtId, Ticket.class); - if (ticket != null && !ticket.isExpired()) { - LOGGER.trace("Located a valid ticket-granting ticket, honoring existing single sign-on session"); - return true; - } - } - catch (final AbstractTicketException e) { - LOGGER.trace("Could not retrieve ticket id [{}] from registry.", e.getMessage()); - } - LOGGER.trace("Ticket-granting ticket found in the webflow context is invalid or has expired"); - return false; - } - - /** - * The Provider login page configuration. - */ - @RequiredArgsConstructor - @Getter - @ToString - public static class ProviderLoginPageConfiguration implements Serializable { - - private static final long serialVersionUID = 6216882278086699364L; - - private final String name; - - private final String redirectUrl; - - private final String type; - - private final String cssClass; - - private final boolean autoRedirect; - } -} diff --git a/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingConfigController.java b/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingConfigController.java deleted file mode 100644 index 77a13bde8439e3c4a8d32b20dbb82a2a5841297e..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingConfigController.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.apereo.cas.web.report; - -import org.apereo.cas.audit.AuditTrailExecutionPlan; -import org.apereo.cas.configuration.CasConfigurationProperties; -import org.apereo.cas.web.BaseCasMvcEndpoint; - -/** - * Copy/pasted from the original CAS class and emptied (because of log4j2). - * - * - */ -public class LoggingConfigController extends BaseCasMvcEndpoint { - - public LoggingConfigController(final AuditTrailExecutionPlan auditTrailManager, final CasConfigurationProperties casProperties) { - super("casloggingconfig", "/logging", casProperties.getMonitor().getEndpoints().getLoggingConfig(), casProperties); - } -} diff --git a/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingOutputTailingService.java b/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingOutputTailingService.java deleted file mode 100644 index cf292dc87ef6e3612742350cd1b30129a5812ef5..0000000000000000000000000000000000000000 --- a/cas/cas-server/src/main/java/org/apereo/cas/web/report/LoggingOutputTailingService.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.apereo.cas.web.report; - -import org.apache.commons.io.input.TailerListenerAdapter; -import org.springframework.core.env.Environment; -import org.springframework.core.io.ResourceLoader; -import org.springframework.core.task.TaskExecutor; -import org.springframework.messaging.simp.SimpMessagingTemplate; - -/** - * Copy/pasted from the CAS and cleaned from the log4j2 stuffs. - * - * - */ -public class LoggingOutputTailingService extends TailerListenerAdapter { - - public LoggingOutputTailingService(final TaskExecutor taskExecutor, final SimpMessagingTemplate stompMessagingTemplate, final Environment environment, final ResourceLoader resourceLoader) { - } -} diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateRestAuthenticationServiceTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateRestAuthenticationServiceTest.java index 19578344f7dcdde459fc37bfd8bb03c14c4349e5..3ea77adee908085187ce0ee90747c24f25887a50 100644 --- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateRestAuthenticationServiceTest.java +++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/authentication/IamSurrogateRestAuthenticationServiceTest.java @@ -8,8 +8,6 @@ import fr.gouv.vitamui.iam.common.dto.SubrogationDto; import fr.gouv.vitamui.iam.common.enums.SubrogationStatusEnum; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,7 +43,7 @@ public final class IamSurrogateRestAuthenticationServiceTest { casExternalRestClient = mock(CasExternalRestClient.class); service = new IamSurrogateRestAuthenticationService(casExternalRestClient, mock(ServicesManager.class)); - final Utils utils = new Utils(casExternalRestClient, mock(DelegatedClientWebflowManager.class), mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); service.setUtils(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 d50f48432661b96fb8241bd2972d40d0ee70db8d..50a97d17627077e7ed02211606a4d4b64ff07f04 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 @@ -21,11 +21,9 @@ import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult; import org.apereo.cas.authentication.Credential; import org.apereo.cas.authentication.PreventedException; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.authentication.exceptions.AccountPasswordMustChangeException; import org.apereo.cas.authentication.principal.DefaultPrincipalFactory; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.junit.Before; import org.junit.Test; @@ -66,7 +64,7 @@ public final class UserAuthenticationHandlerTest { casExternalRestClient = mock(CasExternalRestClient.class); handler.setCasExternalRestClient(casExternalRestClient); credential = new UsernamePasswordCredential(USERNAME, PASSWORD); - final Utils utils = new Utils(casExternalRestClient, mock(DelegatedClientWebflowManager.class), mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); handler.setUtils(utils); } 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 6ae293d58cc7e832f863cf63d8f6bdb13494982c..f9c154775ec75c900a8c4da0755a99c84dec852a 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 @@ -15,8 +15,6 @@ import fr.gouv.vitamui.commons.security.client.dto.AuthUserDto; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; import org.apereo.cas.authentication.principal.DefaultPrincipalFactory; import org.apereo.cas.authentication.principal.Principal; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -26,10 +24,7 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; @@ -65,7 +60,7 @@ public final class UserPrincipalResolverTest { casExternalRestClient = mock(CasExternalRestClient.class); resolver.setCasExternalRestClient(casExternalRestClient); resolver.setPrincipalFactory(new DefaultPrincipalFactory()); - final Utils utils = new Utils(casExternalRestClient, mock(DelegatedClientWebflowManager.class), mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); resolver.setUtils(utils); final RequestContext context = mock(RequestContext.class); RequestContextHolder.setRequestContext(context); @@ -78,8 +73,8 @@ public final class UserPrincipalResolverTest { final Principal principal = resolver.resolve(USERNAME, new HashMap<>()); assertEquals(ID, principal.getId()); - final Map<String, Object> attributes = principal.getAttributes(); - assertEquals(USERNAME, attributes.get(CommonConstants.EMAIL_ATTRIBUTE)); + final Map<String, List<Object>> attributes = principal.getAttributes(); + assertEquals(USERNAME, attributes.get(CommonConstants.EMAIL_ATTRIBUTE).get(0)); assertEquals(Arrays.asList(ROLE_NAME), attributes.get(CommonConstants.ROLES_ATTRIBUTE)); } @@ -90,7 +85,7 @@ public final class UserPrincipalResolverTest { final Principal principal = resolver.resolve(USERNAME, new HashMap<>()); - AddressDto addressDto = (AddressDto) ((CasJsonWrapper) principal.getAttributes().get(CommonConstants.ADDRESS_ATTRIBUTE)).getData(); + AddressDto addressDto = (AddressDto) ((CasJsonWrapper) principal.getAttributes().get(CommonConstants.ADDRESS_ATTRIBUTE).get(0)).getData(); assertEquals(ID, principal.getId()); assertThat(addressDto).isEqualToComparingFieldByField(authUserDto.getAddress()); 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/IamRestPasswordManagementServiceTest.java index 7f749932875d85be04ba4f87165b488b8b96a163..a6dc3e04f095fd927af7c0f474cb671d17216a16 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/IamRestPasswordManagementServiceTest.java @@ -12,10 +12,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.time.ZonedDateTime; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; import fr.gouv.vitamui.cas.provider.ProvidersService; import fr.gouv.vitamui.cas.util.Utils; @@ -25,12 +22,10 @@ import fr.gouv.vitamui.iam.common.dto.IdentityProviderDto; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult; import org.apereo.cas.authentication.DefaultAuthentication; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.authentication.principal.Principal; import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService; -import org.apereo.cas.pm.PasswordChangeBean; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; +import org.apereo.cas.pm.PasswordChangeRequest; import org.junit.Before; import org.junit.Test; @@ -66,7 +61,7 @@ public final class IamRestPasswordManagementServiceTest { private ProvidersService providersService; - private Map<String, Object> authAttributes; + private Map<String, List<Object>> authAttributes; private IdentityProviderDto identityProviderDto; @@ -81,7 +76,7 @@ public final class IamRestPasswordManagementServiceTest { 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, mock(DelegatedClientWebflowManager.class), mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); service.setUtils(utils); final RequestContext context = mock(RequestContext.class); RequestContextHolder.setRequestContext(context); @@ -95,21 +90,22 @@ public final class IamRestPasswordManagementServiceTest { ZonedDateTime.now(), mock(Principal.class), authAttributes, - successes + successes, + new ArrayList<>() )); } @Test public void testChangePasswordSuccessfully() { - assertTrue(service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeBean())); + assertTrue(service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeRequest())); } @Test public void testChangePasswordFailsBecauseOfASuperUser() { - authAttributes.put(SurrogateAuthenticationService.AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL, "fakeSuperUser"); + authAttributes.put(SurrogateAuthenticationService.AUTHENTICATION_ATTR_SURROGATE_PRINCIPAL, Collections.singletonList("fakeSuperUser")); try { - service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeBean()); + service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeRequest()); fail("should fail"); } catch (final IllegalArgumentException e) { @@ -122,7 +118,7 @@ public final class IamRestPasswordManagementServiceTest { identityProviderDto.setInternal(null); try { - service.change(new UsernamePasswordCredential(EMAIL, null), new PasswordChangeBean()); + service.change(new UsernamePasswordCredential(EMAIL, null), new PasswordChangeRequest()); fail("should fail"); } catch (final IllegalArgumentException e) { @@ -135,7 +131,7 @@ public final class IamRestPasswordManagementServiceTest { when(identityProviderHelper.findByUserIdentifier(any(List.class), eq(EMAIL))).thenReturn(Optional.empty()); try { - service.change(new UsernamePasswordCredential(EMAIL, null), new PasswordChangeBean()); + service.change(new UsernamePasswordCredential(EMAIL, null), new PasswordChangeRequest()); fail("should fail"); } catch (final IllegalArgumentException e) { @@ -148,7 +144,7 @@ public final class IamRestPasswordManagementServiceTest { doThrow(new InvalidAuthenticationException("")).when(casExternalRestClient) .changePassword(any(ExternalHttpContext.class), any(String.class), any(String.class)); - assertFalse(service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeBean())); + assertFalse(service.change(new UsernamePasswordCredential(EMAIL, "password"), new PasswordChangeRequest())); } @Test 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 1b8f6a9915fc88c49dcae18e40b1a8e4c1a81385..b5c1c5c44c5a0d988c48708c2a045e8eabfdc75a 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 @@ -9,8 +9,6 @@ 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 org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -59,7 +57,7 @@ public final class ProvidersServiceTest { restClient = mock(IdentityProviderExternalRestClient.class); service.setIdentityProviderExternalRestClient(restClient); final CasExternalRestClient casExternalRestClient = mock(CasExternalRestClient.class); - final Utils utils = new Utils(casExternalRestClient, mock(DelegatedClientWebflowManager.class), mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); service.setUtils(utils); provider = new IdentityProviderDto(); 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/webflow/actions/BaseWebflowActionTest.java index 6a2aa46fd761673e707272b6ce1d344ddb6da94f..e90535594bbd682368e0ddab73ab6e0b03832746 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/webflow/actions/BaseWebflowActionTest.java @@ -39,6 +39,10 @@ public abstract class BaseWebflowActionTest { protected HttpSession session; + protected HttpServletRequest request; + + protected HttpServletResponse response; + @Before public void setUp() { context = mock(RequestContext.class); @@ -53,9 +57,10 @@ public abstract class BaseWebflowActionTest { final ServletExternalContext servletExternalContext = mock(ServletExternalContext.class); when(context.getExternalContext()).thenReturn(servletExternalContext); - final HttpServletRequest request = mock(HttpServletRequest.class); + request = mock(HttpServletRequest.class); + when(request.getMethod()).thenReturn("GET"); when(servletExternalContext.getNativeRequest()).thenReturn(request); - final HttpServletResponse response = mock(HttpServletResponse.class); + response = mock(HttpServletResponse.class); when(servletExternalContext.getNativeResponse()).thenReturn(response); session = mock(HttpSession.class); 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 4d3fef5202f1bc691d198ee8ae0ab9e4d52740db..4acfb606fb7c77db75557fdd50292efaa7824ac8 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 @@ -5,11 +5,14 @@ import static org.mockito.Mockito.mock; import java.util.ArrayList; +import org.apereo.cas.authentication.AuthenticationEventExecutionPlan; import org.apereo.cas.authentication.AuthenticationServiceSelectionPlan; -import org.apereo.cas.authentication.UsernamePasswordCredential; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.apereo.cas.configuration.CasConfigurationProperties; import org.apereo.cas.services.ServicesManager; -import org.apereo.cas.web.support.CookieRetrievingCookieGenerator; +import org.apereo.cas.ticket.registry.TicketRegistrySupport; +import org.apereo.cas.web.cookie.CasCookieBuilder; +import org.apereo.cas.web.flow.SingleSignOnParticipationStrategy; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,11 +44,11 @@ public final class CustomInitialFlowSetupActionTest extends BaseWebflowActionTes super.setUp(); action = new CustomInitialFlowSetupAction(new ArrayList<>(), mock(ServicesManager.class), - mock(AuthenticationServiceSelectionPlan.class), mock(CookieRetrievingCookieGenerator.class), - mock(CookieRetrievingCookieGenerator.class), new CasConfigurationProperties()); + mock(AuthenticationServiceSelectionPlan.class), mock(CasCookieBuilder.class), + mock(CasCookieBuilder.class), new CasConfigurationProperties(), mock(AuthenticationEventExecutionPlan.class), + mock(SingleSignOnParticipationStrategy.class), mock(TicketRegistrySupport.class)); action.setSeparator(","); } - @Test public void testUsernameNoSubrogation() { requestParameters.put("username", EMAIL1); 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 c79adda9f22988df6233b927277b7996fbdc8deb..529888669dbbb098b79ebfe3fca0072e59bccfb2 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 @@ -1,8 +1,7 @@ package fr.gouv.vitamui.cas.webflow.actions; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; +import static org.mockito.Matchers.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -14,17 +13,11 @@ import fr.gouv.vitamui.cas.util.Utils; import fr.gouv.vitamui.commons.api.exception.InvalidFormatException; import fr.gouv.vitamui.commons.rest.client.ExternalHttpContext; import fr.gouv.vitamui.iam.external.client.CasExternalRestClient; -import org.apereo.cas.authentication.UsernamePasswordCredential; -import org.apereo.cas.ticket.TransientSessionTicketImpl; -import org.apereo.cas.web.DelegatedClientWebflowManager; -import org.apereo.cas.web.pac4j.DelegatedSessionCookieManager; +import org.apereo.cas.authentication.credential.UsernamePasswordCredential; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.pac4j.core.context.J2EContext; -import org.pac4j.core.context.WebContext; -import org.pac4j.core.exception.HttpAction; -import org.pac4j.core.redirect.RedirectAction; +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; @@ -72,14 +65,14 @@ public final class DispatcherActionTests extends BaseWebflowActionTest { identityProviderHelper = mock(IdentityProviderHelper.class); casExternalRestClient = mock(CasExternalRestClient.class); - final DelegatedClientWebflowManager delegatedClientWebflowManager = mock(DelegatedClientWebflowManager.class); - when(delegatedClientWebflowManager.store(any(J2EContext.class), any(SAML2Client.class))).thenReturn(new TransientSessionTicketImpl()); action = new DispatcherAction(providersService, identityProviderHelper, casExternalRestClient); action.setSeparator(","); - final Utils utils = new Utils(casExternalRestClient, delegatedClientWebflowManager, mock(DelegatedSessionCookieManager.class), null); + final Utils utils = new Utils(casExternalRestClient, null); action.setUtils(utils); - provider = new SamlIdentityProviderDto(new IdentityProviderDto(), new MockSAML2Client()); + final FoundAction foundAction = new FoundAction("http://server"); + final SAML2Client client = new SAML2Client(); + provider = new SamlIdentityProviderDto(new IdentityProviderDto(), client); provider.setInternal(true); when(identityProviderHelper.findByUserIdentifier(any(LinkedList.class), eq(USERNAME))) .thenReturn(Optional.of(provider)); @@ -246,14 +239,4 @@ public final class DispatcherActionTests extends BaseWebflowActionTest { ((UsernamePasswordCredential) flowParameters.get("credential")).getUsername()); assertEquals("disabled", event.getId()); } - - static class MockSAML2Client extends SAML2Client { - - @Override - public RedirectAction getRedirectAction(final WebContext context) throws HttpAction { - final RedirectAction action = mock(RedirectAction.class); - when(action.getLocation()).thenReturn("http://server"); - return action; - } - } } diff --git a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionActionTest.java b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionActionTest.java index 9bdefd07d06146afeba0f5aed3ede4ee2000ceea..298e658c5dc368a0140c47f5387050e8c28e4f95 100644 --- a/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionActionTest.java +++ b/cas/cas-server/src/test/java/fr/gouv/vitamui/cas/webflow/actions/GeneralTerminateSessionActionTest.java @@ -5,7 +5,6 @@ import org.apereo.cas.services.RegexRegisteredService; import org.apereo.cas.services.ServicesManager; import org.junit.Test; -import java.net.URL; import java.util.Arrays; import static org.mockito.Mockito.*; @@ -20,10 +19,10 @@ public final class GeneralTerminateSessionActionTest { private static final String LOGOUT_URL = "http://dev.vitamui.com:8080/cas/app1/callback"; @Test - public void test() throws Exception { + public void test() { final ServicesManager servicesManager = mock(ServicesManager.class); RegexRegisteredService registeredService = new RegexRegisteredService(); - registeredService.setLogoutUrl(new URL(LOGOUT_URL)); + registeredService.setLogoutUrl(LOGOUT_URL); when(servicesManager.getAllServices()).thenReturn(Arrays.asList(registeredService)); final LogoutManager logoutManager = mock(LogoutManager.class); diff --git a/pom.xml b/pom.xml index 19ce826046f1226e42a20d8140518c03be3edfe2..00cdc9739a1ec47cd151b34bed337e27577eb74c 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ <apache.pdfbox.xmpbox.version>2.0.16</apache.pdfbox.xmpbox.version> <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version> <bouncycastle.version>1.60</bouncycastle.version> - <cas.version>5.3.8</cas.version> + <cas.version>6.1.5</cas.version> <commons.collections.version>3.2.2</commons.collections.version> <commons.collections4.version>4.4</commons.collections4.version> <commons.compress.version>1.20</commons.compress.version> @@ -106,7 +106,7 @@ <lombok.version>1.18.8</lombok.version> <micrometer.version>1.1.5</micrometer.version> <nio.multipart.parser.version>1.1.0</nio.multipart.parser.version> - <pac4j.version>3.8.3</pac4j.version> + <pac4j.version>4.0.0-RC3</pac4j.version> <poi.version>3.17</poi.version> <powermock.version>2.0.2</powermock.version> <!-- Temporary fix for Multipart upload issues cf