diff --git a/pom.xml b/pom.xml
index f072cf8..18cea2f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,11 @@
spring-boot-starter-oauth2-client
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
org.springframework.boot
spring-boot-starter-data-jpa
--
2.51.2
From b6e3ceb45e3bb782373210d2e7afed025bb95bbc Mon Sep 17 00:00:00 2001
From: Julien WITTOUCK
Date: Mon, 28 Aug 2023 16:45:09 +0200
Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=94=92=20:=20add=20id=5Ftoken=20secur?=
=?UTF-8?q?ity=20configuration?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../GitlabIDTokenSecurityConfiguration.java | 24 +++++++++++++++++++
1 file changed, 24 insertions(+)
create mode 100644 src/main/java/fr/univ_lille/gitlab/classrooms/security/GitlabIDTokenSecurityConfiguration.java
diff --git a/src/main/java/fr/univ_lille/gitlab/classrooms/security/GitlabIDTokenSecurityConfiguration.java b/src/main/java/fr/univ_lille/gitlab/classrooms/security/GitlabIDTokenSecurityConfiguration.java
new file mode 100644
index 0000000..5d735df
--- /dev/null
+++ b/src/main/java/fr/univ_lille/gitlab/classrooms/security/GitlabIDTokenSecurityConfiguration.java
@@ -0,0 +1,24 @@
+package fr.univ_lille.gitlab.classrooms.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.web.SecurityFilterChain;
+
+/**
+ * Specific configuration to accept Gitlab ID_TOKENS as an authentication mecanism
+ */
+@Configuration
+public class GitlabIDTokenSecurityConfiguration {
+
+ @Bean
+ SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception {
+ return http
+ .securityMatcher("/api/**")
+ .authorizeHttpRequests(authorize -> authorize.requestMatchers("/api/**").authenticated())
+ .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
+ .build();
+ }
+
+}
--
2.51.2
From 839fa515e1e6da0cd317e2564e679f4046639f40 Mon Sep 17 00:00:00 2001
From: Julien WITTOUCK
Date: Mon, 28 Aug 2023 17:53:02 +0200
Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=94=A7=20:=20configure=20jwt=20decode?=
=?UTF-8?q?r=20issuer=20uri?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/resources/application.properties | 2 ++
src/test/resources/application.properties | 2 ++
2 files changed, 4 insertions(+)
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 932acd3..53db70b 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -21,3 +21,5 @@ spring.datasource.password=${POSTGRESQL_ADDON_PASSWORD}
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=validate
+
+spring.security.oauth2.resourceserver.jwt.issuer-uri=${gitlab.url}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
index bef1c43..2836134 100644
--- a/src/test/resources/application.properties
+++ b/src/test/resources/application.properties
@@ -15,3 +15,5 @@ spring.security.oauth2.client.registration.gitlab.redirect-uri={baseUrl}/login/o
spring.security.oauth2.client.registration.gitlab.scope[0]=api
spring.flyway.enabled=false
+
+spring.security.oauth2.resourceserver.jwt.issuer-uri=${gitlab.url}
--
2.51.2
From 7bd69f8c85864bb36b2e5157959d09bb4ca54a85 Mon Sep 17 00:00:00 2001
From: Julien WITTOUCK
Date: Mon, 28 Aug 2023 18:06:37 +0200
Subject: [PATCH 4/4] =?UTF-8?q?=E2=9C=A8=20:=20add=20JWTAuthenticationToke?=
=?UTF-8?q?n=20support,=20for=20Gitlab=20ID=20Tokens?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../users/UserControllerAdvice.java | 5 ++
.../users/UserControllerAdviceTest.java | 81 +++++++++++++++++++
2 files changed, 86 insertions(+)
create mode 100644 src/test/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdviceTest.java
diff --git a/src/main/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdvice.java b/src/main/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdvice.java
index 4448eae..693e81e 100644
--- a/src/main/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdvice.java
+++ b/src/main/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdvice.java
@@ -1,5 +1,6 @@
package fr.univ_lille.gitlab.classrooms.users;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@@ -23,6 +24,10 @@ public class UserControllerAdvice {
@ModelAttribute("user")
ClassroomUser classroomUser(Principal authenticationPrincipal){
try{
+ // if user is a JWTAuthenticationToken, (gitlab id token), load the user using the "user_login" claim
+ if(authenticationPrincipal instanceof JwtAuthenticationToken jwtToken){
+ return this.classroomUserService.getClassroomUser(jwtToken.getTokenAttributes().get("user_login").toString());
+ }
return this.classroomUserService.getClassroomUser(authenticationPrincipal.getName());
}
catch(NoSuchElementException e){
diff --git a/src/test/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdviceTest.java b/src/test/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdviceTest.java
new file mode 100644
index 0000000..12b3c91
--- /dev/null
+++ b/src/test/java/fr/univ_lille/gitlab/classrooms/users/UserControllerAdviceTest.java
@@ -0,0 +1,81 @@
+package fr.univ_lille.gitlab.classrooms.users;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
+import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
+
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import static org.assertj.core.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+@ExtendWith(MockitoExtension.class)
+class UserControllerAdviceTest {
+
+ @InjectMocks
+ private UserControllerAdvice userControllerAdvice;
+
+ @Mock
+ private ClassroomUserService classroomUserService;
+
+ @Test
+ void classroomUser_for_OauthAuthenticationToken() {
+ var vader = new ClassroomUser("darth.vader", List.of(ClassroomRole.STUDENT));
+ when(classroomUserService.getClassroomUser("darth.vader")).thenReturn(vader);
+
+ var oauth2User = new DefaultOAuth2User(null, Map.of("nameAttr", "darth.vader"), "nameAttr");
+ var token = new OAuth2AuthenticationToken(oauth2User, null, "oauth");
+
+ var user = this.userControllerAdvice.classroomUser(token);
+
+ assertThat(user)
+ .isNotNull()
+ .isEqualTo(vader);
+
+ verify(classroomUserService).getClassroomUser("darth.vader");
+ }
+
+ @Test
+ void classroomUser_for_JWTAuthenticationToken() {
+ var vader = new ClassroomUser("darth.vader", List.of(ClassroomRole.STUDENT));
+ when(classroomUserService.getClassroomUser("darth.vader")).thenReturn(vader);
+
+ var jwt = Jwt.withTokenValue("token")
+ .header("alg", "none")
+ .claim("sub", "user")
+ .claim("user_login", "darth.vader")
+ .build();
+ var token = new JwtAuthenticationToken(jwt);
+
+ var user = this.userControllerAdvice.classroomUser(token);
+
+ assertThat(user)
+ .isNotNull()
+ .isEqualTo(vader);
+
+ verify(classroomUserService).getClassroomUser("darth.vader");
+ }
+
+ @Test
+ void classroomUser_returnsNull_whenUserIsNotFound() {
+ when(classroomUserService.getClassroomUser(anyString())).thenThrow(new NoSuchElementException());
+
+ var oauth2User = new DefaultOAuth2User(null, Map.of("nameAttr", "darth.vader"), "nameAttr");
+ var token = new OAuth2AuthenticationToken(oauth2User, null, "oauth");
+
+ var user = this.userControllerAdvice.classroomUser(token);
+
+ assertThat(user).isNull();
+
+ verify(classroomUserService).getClassroomUser("darth.vader");
+ }
+}