From 710c9371dc7da9213d6b22cecfabd91d8ca53522 Mon Sep 17 00:00:00 2001
From: Makhtar DIAGNE <makhtar.diagne@teamdlab.com>
Date: Mon, 31 Aug 2020 00:55:37 +0200
Subject: [PATCH] [TECH] Fix oauth authentication : use same token generation
 as CAS

---
 .../client/CasExternalRestClientTest.java     |  2 +-
 .../client/CasInternalRestClientTest.java     |  2 +-
 api/api-iam/iam-internal/pom.xml              | 46 +++++++++++++++
 .../cas/service/CasInternalService.java       |  8 ++-
 .../service/UserInternalServiceIntegTest.java |  2 +-
 .../user/service/UserInternalServiceTest.java |  2 +-
 cas/cas-server/README.md                      |  4 +-
 cas/cas-server/pom.xml                        | 10 ++++
 .../fr/gouv/vitamui/cas/config/AppConfig.java | 59 ++++++++++++++++++-
 pom.xml                                       | 24 ++++++++
 10 files changed, 149 insertions(+), 10 deletions(-)

diff --git a/api/api-iam/iam-external-client/src/test/java/fr/gouv/vitamui/iam/external/client/CasExternalRestClientTest.java b/api/api-iam/iam-external-client/src/test/java/fr/gouv/vitamui/iam/external/client/CasExternalRestClientTest.java
index de997f8f..da5161cc 100644
--- a/api/api-iam/iam-external-client/src/test/java/fr/gouv/vitamui/iam/external/client/CasExternalRestClientTest.java
+++ b/api/api-iam/iam-external-client/src/test/java/fr/gouv/vitamui/iam/external/client/CasExternalRestClientTest.java
@@ -38,7 +38,7 @@ public class CasExternalRestClientTest extends AbstractServerIdentityBuilder {
                 .thenReturn(new ResponseEntity<>(HttpStatus.OK));
 
         final String superUser = "julien@vitamui.com";
-        final String authToken = "TOKyyy";
+        final String authToken = "TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu";
         client.logout(header, authToken, superUser);
         final String path = RestApi.CAS_LOGOUT_PATH + "?authToken=" + authToken + "&superUser=" + superUser;
         assertThat(argumentCaptor.getValue().toString()).endsWith(path.replaceAll(CommonConstants.EMAIL_SEPARATOR, "%40"));
diff --git a/api/api-iam/iam-internal-client/src/test/java/fr/gouv/vitamui/iam/internal/client/CasInternalRestClientTest.java b/api/api-iam/iam-internal-client/src/test/java/fr/gouv/vitamui/iam/internal/client/CasInternalRestClientTest.java
index 1a12242e..c31be6cd 100644
--- a/api/api-iam/iam-internal-client/src/test/java/fr/gouv/vitamui/iam/internal/client/CasInternalRestClientTest.java
+++ b/api/api-iam/iam-internal-client/src/test/java/fr/gouv/vitamui/iam/internal/client/CasInternalRestClientTest.java
@@ -39,7 +39,7 @@ public class CasInternalRestClientTest extends AbstractServerIdentityBuilder {
                 .thenReturn(new ResponseEntity<>(HttpStatus.OK));
 
         final String superUser = "julien@vitamui.com";
-        final String authToken = "TOKxxx";
+        final String authToken = "TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu";
         client.logout(header, authToken, superUser);
         final String path = RestApi.CAS_LOGOUT_PATH + "?authToken=" + authToken + "&superUser=" + superUser;
         assertThat(argumentCaptor.getValue().toString()).endsWith(path.replaceAll(CommonConstants.EMAIL_SEPARATOR, "%40"));
diff --git a/api/api-iam/iam-internal/pom.xml b/api/api-iam/iam-internal/pom.xml
index 707d3f0f..caaf5792 100644
--- a/api/api-iam/iam-internal/pom.xml
+++ b/api/api-iam/iam-internal/pom.xml
@@ -168,6 +168,52 @@
 			<artifactId>common-private</artifactId>
 		</dependency>
 
+		<!-- Apereo CAS Server Core Api Ticket -->
+		<dependency>
+			<groupId>org.apereo.cas</groupId>
+			<artifactId>cas-server-core-api-ticket</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.springframework.cloud</groupId>
+					<artifactId>spring-cloud-context</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.springframework.cloud</groupId>
+					<artifactId>spring-cloud-commons</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.springframework.boot</groupId>
+					<artifactId>spring-boot-actuator</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>com.zaxxer</groupId>
+					<artifactId>*</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+		<dependency>
+			<groupId>org.apereo.cas</groupId>
+			<artifactId>cas-server-core-tickets-api</artifactId>
+			<exclusions>
+				<exclusion>
+					<groupId>org.springframework.cloud</groupId>
+					<artifactId>spring-cloud-context</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.springframework.cloud</groupId>
+					<artifactId>spring-cloud-commons</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>org.springframework.boot</groupId>
+					<artifactId>spring-boot-actuator</artifactId>
+				</exclusion>
+				<exclusion>
+					<groupId>com.zaxxer</groupId>
+					<artifactId>*</artifactId>
+				</exclusion>
+			</exclusions>
+		</dependency>
+
 		<!-- Documentation -->
 		<dependency>
 			<groupId>io.springfox</groupId>
diff --git a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java
index e650a4b5..93c82105 100644
--- a/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java
+++ b/api/api-iam/iam-internal/src/main/java/fr/gouv/vitamui/iam/internal/server/cas/service/CasInternalService.java
@@ -49,6 +49,8 @@ import javax.validation.constraints.NotNull;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.lang.time.DateUtils;
+import org.apereo.cas.util.DefaultUniqueTicketIdGenerator;
+import org.apereo.cas.ticket.UniqueTicketIdGenerator;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.mongodb.core.MongoTemplate;
@@ -162,6 +164,8 @@ public class CasInternalService {
     @SuppressWarnings("unused")
     private static final VitamUILogger LOGGER = VitamUILoggerFactory.getInstance(CasInternalService.class);
 
+    private static final UniqueTicketIdGenerator TICKET_GENERATOR = new DefaultUniqueTicketIdGenerator();
+
     @Transactional
     public void updatePassword(final String email, final String rawPassword) {
         final User user = checkUserInformations(email);
@@ -272,7 +276,7 @@ public class CasInternalService {
     private void createEventsSubrogation(final UserDto surrogate, final boolean isSubrogation) {
         if (isSubrogation) {
             final Subrogation subro = subrogationRepository.findOneBySurrogate(surrogate.getEmail());
-            EventType type;
+            final EventType type;
             if (surrogate.getType().equals(UserTypeEnum.GENERIC)) {
                 type = EventType.EXT_VITAMUI_START_SURROGATE_GENERIC;
             }
@@ -310,7 +314,7 @@ public class CasInternalService {
         }
         final Date nowPlusXMinutes = DateUtils.addMinutes(new Date(), ttlInMinutes);
         token.setUpdatedDate(nowPlusXMinutes);
-        token.setId(TOKEN_PREFIX + tokenRepository.generateSuperId().toUpperCase());
+        token.setId(TICKET_GENERATOR.getNewTicketId(TOKEN_PREFIX));
         token.setSurrogation(isSubrogation);
         tokenRepository.save(token);
         user.setLastConnection(OffsetDateTime.now());
diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceIntegTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceIntegTest.java
index ca6223e8..28723d6e 100644
--- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceIntegTest.java
+++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceIntegTest.java
@@ -85,7 +85,7 @@ import fr.gouv.vitamui.iam.internal.server.utils.IamServerUtilsTest;
         TokenRepository.class }, repositoryBaseClass = VitamUIRepositoryImpl.class)
 public final class UserInternalServiceIntegTest extends AbstractLogbookIntegrationTest {
 
-    private static final String TOKEN_VALUE = "TOK1234567890";
+    private static final String TOKEN_VALUE = "TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu";
 
     private static final String USER_ID = "userId";
 
diff --git a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceTest.java b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceTest.java
index 0c148337..60f0d839 100644
--- a/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceTest.java
+++ b/api/api-iam/iam-internal/src/test/java/fr/gouv/vitamui/iam/internal/server/user/service/UserInternalServiceTest.java
@@ -67,7 +67,7 @@ import fr.gouv.vitamui.iam.security.service.InternalSecurityService;
  */
 public final class UserInternalServiceTest {
 
-    private static final String TOKEN_VALUE = "TOK1234567890";
+    private static final String TOKEN_VALUE = "TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu";
 
     private static final String USER_ID = "userId";
 
diff --git a/cas/cas-server/README.md b/cas/cas-server/README.md
index 9713c7f8..2f0dacfa 100644
--- a/cas/cas-server/README.md
+++ b/cas/cas-server/README.md
@@ -175,8 +175,8 @@ The call must be a POST request:
 
 The result contains the auth token in a plain response:
 
-`access_token=TOK5CE669E223C9741AA6DCD46xxx02C8F6FB9DC98DC25B779&expires_in=28800`
+`access_token=TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu&expires_in=28800`
 
 or in a JSON response:
 
-`{"access_token":"TOK5CE669E223C9741AA6DCD46xxx02C8F6FB9DC98DC25B779","token_type":"bearer","expires_in":28800}`
+`{"access_token":"TOK-1-F8lEhVif0FWjgDF32ov73TtKhE6mflRu","token_type":"bearer","expires_in":28800}`
diff --git a/cas/cas-server/pom.xml b/cas/cas-server/pom.xml
index a779a938..e04775d2 100644
--- a/cas/cas-server/pom.xml
+++ b/cas/cas-server/pom.xml
@@ -111,6 +111,11 @@
             <artifactId>cas-server-support-pac4j-core</artifactId>
             <version>${cas.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.apereo.cas</groupId>
+            <artifactId>cas-server-support-pac4j-api</artifactId>
+            <version>${cas.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.apereo.cas</groupId>
             <artifactId>cas-server-support-pac4j-core-clients</artifactId>
@@ -131,6 +136,11 @@
             <artifactId>pac4j-core</artifactId>
             <version>${pac4j.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.pac4j</groupId>
+            <artifactId>spring-webmvc-pac4j</artifactId>
+            <version>${pac4j.version}</version>
+        </dependency>
         <dependency>
             <groupId>org.pac4j</groupId>
             <artifactId>pac4j-saml-opensamlv3</artifactId>
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 86b862f3..0ea85b96 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
@@ -39,9 +39,17 @@ package fr.gouv.vitamui.cas.config;
 import fr.gouv.vitamui.cas.authentication.*;
 import fr.gouv.vitamui.cas.pm.IamPasswordManagementService;
 import lombok.SneakyThrows;
+import lombok.val;
+import java.util.Collection;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 import org.apereo.cas.CentralAuthenticationService;
 import org.apereo.cas.audit.AuditableExecution;
-import org.apereo.cas.authentication.*;
+import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
+import org.apereo.cas.authentication.AuthenticationHandler;
+import org.apereo.cas.authentication.AuthenticationMetaDataPopulator;
+import org.apereo.cas.authentication.AuthenticationPostProcessor;
 import org.apereo.cas.authentication.principal.PrincipalFactory;
 import org.apereo.cas.authentication.principal.PrincipalResolver;
 import org.apereo.cas.authentication.surrogate.SurrogateAuthenticationService;
@@ -49,13 +57,27 @@ import org.apereo.cas.configuration.CasConfigurationProperties;
 import org.apereo.cas.pm.PasswordHistoryService;
 import org.apereo.cas.pm.PasswordManagementService;
 import org.apereo.cas.services.ServicesManager;
-import org.apereo.cas.ticket.*;
+import org.apereo.cas.support.oauth.authenticator.Authenticators;
+import org.apereo.cas.support.oauth.web.OAuth20HandlerInterceptorAdapter;
+import org.apereo.cas.support.oauth.web.response.accesstoken.ext.AccessTokenGrantRequestExtractor;
+import org.apereo.cas.ticket.BaseTicketCatalogConfigurer;
+import org.apereo.cas.ticket.ExpirationPolicyBuilder;
+import org.apereo.cas.ticket.TicketCatalog;
+import org.apereo.cas.ticket.TicketCatalogConfigurer;
+import org.apereo.cas.ticket.TicketDefinition;
+import org.apereo.cas.ticket.TicketGrantingTicketFactory;
+import org.apereo.cas.ticket.UniqueTicketIdGenerator;
 import org.apereo.cas.ticket.accesstoken.OAuth20AccessTokenFactory;
 import org.apereo.cas.ticket.accesstoken.OAuth20DefaultAccessToken;
 import org.apereo.cas.ticket.registry.TicketRegistry;
 import org.apereo.cas.token.JwtBuilder;
 import org.apereo.cas.util.crypto.CipherExecutor;
+import org.pac4j.core.client.Client;
+import org.pac4j.core.client.DirectClient;
+import org.pac4j.core.config.Config;
 import org.pac4j.core.context.session.SessionStore;
+import org.pac4j.core.http.adapter.JEEHttpActionAdapter;
+import org.pac4j.springframework.web.SecurityInterceptor;
 import org.springframework.beans.factory.ObjectProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -83,6 +105,7 @@ 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 org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.web.servlet.HandlerInterceptor;
 
 /**
  * Configure all beans to customize the CAS server.
@@ -196,6 +219,38 @@ public class AppConfig extends BaseTicketCatalogConfigurer {
     @Value("${vitamui.cas.identity}")
     private String casIdentity;
 
+    @Autowired
+    @Qualifier("oauthSecConfig")
+    private ObjectProvider<Config> oauthSecConfig;
+
+    @Autowired
+    @Qualifier("accessTokenGrantRequestExtractors")
+    private Collection<AccessTokenGrantRequestExtractor> accessTokenGrantRequestExtractors;
+
+    @Bean
+    public SecurityInterceptor requiresAuthenticationAuthorizeInterceptor() {
+        val interceptor = new SecurityInterceptor(oauthSecConfig.getObject(), Authenticators.CAS_OAUTH_CLIENT, JEEHttpActionAdapter.INSTANCE);
+        interceptor.setAuthorizers("none");
+        return interceptor;
+    }
+
+    @Bean
+    public SecurityInterceptor requiresAuthenticationAccessTokenInterceptor() {
+        val secConfig = oauthSecConfig.getObject();
+        val clients =
+                Objects.requireNonNull(secConfig).getClients().findAllClients().stream().filter(client -> client instanceof DirectClient).map(Client::getName)
+                        .collect(Collectors.joining(","));
+        val interceptor = new SecurityInterceptor(oauthSecConfig.getObject(), clients, JEEHttpActionAdapter.INSTANCE);
+        interceptor.setAuthorizers("none");
+        return interceptor;
+    }
+
+    @Bean
+    public HandlerInterceptor oauthHandlerInterceptorAdapter() {
+        return new OAuth20HandlerInterceptorAdapter(requiresAuthenticationAccessTokenInterceptor(), requiresAuthenticationAuthorizeInterceptor(),
+                accessTokenGrantRequestExtractors);
+    }
+
     @Bean
     public UserAuthenticationHandler userAuthenticationHandler() {
         return new UserAuthenticationHandler(servicesManager, principalFactory, casRestClient(), utils(), ipHeaderName);
diff --git a/pom.xml b/pom.xml
index 57943425..eef30fd6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -468,6 +468,30 @@
                 <scope>test</scope>
             </dependency>
 
+            <!-- Apereo CAS Server Core Api Ticket -->
+            <dependency>
+                <groupId>org.apereo.cas</groupId>
+                <artifactId>cas-server-core-api-ticket</artifactId>
+                <version>${cas.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>*</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+            <dependency>
+                <groupId>org.apereo.cas</groupId>
+                <artifactId>cas-server-core-tickets-api</artifactId>
+                <version>${cas.version}</version>
+                <exclusions>
+                    <exclusion>
+                        <groupId>org.springframework</groupId>
+                        <artifactId>*</artifactId>
+                    </exclusion>
+                </exclusions>
+            </dependency>
+
             <!-- Web -->
             <dependency>
                 <groupId>javax.servlet</groupId>
-- 
GitLab