From 4c8f24928caea155e7cb327b9bd146451bdbac51 Mon Sep 17 00:00:00 2001 From: linxiaoxin Date: Wed, 17 Jul 2024 18:35:28 +0800 Subject: [PATCH] Added IsAuthorisedUser function to check whether user session is authorised to access path and method --- .github/workflows/cicd.yml | 1 + .../controller/AuthenticationController.java | 5 + .../auth_ms/model/IsAuthorisedRequest.java | 1 + .../auth_ms/repository/RoleRepository.java | 5 + .../service/AuthenticationService.java | 2 + .../service/AuthenticationServiceImpl.java | 29 ++++- .../AuthenticationControllerTest.java | 23 +++- .../AuthenticationServiceImplTest.java | 100 +++++++++++++++--- 8 files changed, 150 insertions(+), 16 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 828d641..2f1aa04 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -162,6 +162,7 @@ jobs: build/reports/ zap_scan: + if: false permissions: write-all needs: deploy_to_ecs runs-on: ubuntu-latest diff --git a/src/main/java/com/quemistry/auth_ms/controller/AuthenticationController.java b/src/main/java/com/quemistry/auth_ms/controller/AuthenticationController.java index 622d83a..1daa331 100644 --- a/src/main/java/com/quemistry/auth_ms/controller/AuthenticationController.java +++ b/src/main/java/com/quemistry/auth_ms/controller/AuthenticationController.java @@ -65,4 +65,9 @@ public ResponseEntity isAuthorised(@RequestBody IsAuthorisedRequest req var result =authenticationService.checkAccess(request.getRole(), request.getPath(), request.getMethod()); return ResponseEntity.status(HttpStatus.OK).body(result); } + @PostMapping("isauthoriseduser") + public ResponseEntity isAuthorisedUser(@RequestBody IsAuthorisedRequest request){ + var result =authenticationService.checkUserSessionAccess(request.getSessionId(), request.getPath(), request.getMethod()); + return ResponseEntity.status(HttpStatus.OK).body(result); + } } diff --git a/src/main/java/com/quemistry/auth_ms/model/IsAuthorisedRequest.java b/src/main/java/com/quemistry/auth_ms/model/IsAuthorisedRequest.java index e0402c1..c05fb8c 100644 --- a/src/main/java/com/quemistry/auth_ms/model/IsAuthorisedRequest.java +++ b/src/main/java/com/quemistry/auth_ms/model/IsAuthorisedRequest.java @@ -12,4 +12,5 @@ public class IsAuthorisedRequest { private String role; private String path; private String method; + private String sessionId; } diff --git a/src/main/java/com/quemistry/auth_ms/repository/RoleRepository.java b/src/main/java/com/quemistry/auth_ms/repository/RoleRepository.java index ada975b..7d222ac 100644 --- a/src/main/java/com/quemistry/auth_ms/repository/RoleRepository.java +++ b/src/main/java/com/quemistry/auth_ms/repository/RoleRepository.java @@ -5,10 +5,15 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import java.util.List; import java.util.Optional; public interface RoleRepository extends JpaRepository { @Query("select r from Role r left join fetch r.grantedWith where r.name = :name") Optional findByName(@Param("name") String name); + + @Query("select r from Role r left join fetch r.grantedWith where r.name in :names") + List findByNames(@Param("name") String[] names); } + diff --git a/src/main/java/com/quemistry/auth_ms/service/AuthenticationService.java b/src/main/java/com/quemistry/auth_ms/service/AuthenticationService.java index 9e91871..5adc761 100644 --- a/src/main/java/com/quemistry/auth_ms/service/AuthenticationService.java +++ b/src/main/java/com/quemistry/auth_ms/service/AuthenticationService.java @@ -9,4 +9,6 @@ public interface AuthenticationService { void signOut(String sessionId, String clientId); Boolean checkAccess(String roleName, String path, String method); + + Boolean checkUserSessionAccess(String sessionId, String path, String method); } diff --git a/src/main/java/com/quemistry/auth_ms/service/AuthenticationServiceImpl.java b/src/main/java/com/quemistry/auth_ms/service/AuthenticationServiceImpl.java index 8915850..a3a706c 100644 --- a/src/main/java/com/quemistry/auth_ms/service/AuthenticationServiceImpl.java +++ b/src/main/java/com/quemistry/auth_ms/service/AuthenticationServiceImpl.java @@ -16,6 +16,7 @@ import org.springframework.web.client.RestTemplate; import java.time.Duration; +import java.util.ArrayList; import java.util.Base64; import java.util.Map; import java.util.UUID; @@ -69,10 +70,16 @@ public UserProfile getAccessToken(TokenRequest request) { ObjectMapper mapper = new ObjectMapper(); try { Map map = mapper.readValue(payload, new TypeReference<>() {}); + //creates session id as key and store user profile and access tokens info in redis user = new UserProfile(); user.setSessionId(UUID.randomUUID().toString()); user.setEmail((String)map.get("email")); + var useRoles = (ArrayList)map.get("cognito:groups"); + if(useRoles != null) { + var roles = useRoles.toArray(new String[useRoles.size()]); + user.setRoles(roles); + } redisTemplate.opsForValue().set(user.getSessionId()+"_profile", user, Duration.ofSeconds(SESSION_TIMEOUT)); redisTemplate.opsForValue().set(user.getSessionId()+"_tokens", response.getBody(), Duration.ofSeconds(SESSION_TIMEOUT)); @@ -112,9 +119,9 @@ public void signOut(String sessionId, String clientId) { @Override public Boolean checkAccess(String roleName, String path, String method) { //get role - var teacher = roleRepository.findByName(roleName); - if(teacher.isPresent()){ - var grantedWith = teacher.get().getGrantedWith(); + var role = roleRepository.findByName(roleName); + if(role.isPresent()){ + var grantedWith = role.get().getGrantedWith(); if(grantedWith.stream().anyMatch(granted -> granted.getPath().compareToIgnoreCase(path) == 0 && granted.getMethod().compareToIgnoreCase(method) ==0)) return true; @@ -122,4 +129,20 @@ public Boolean checkAccess(String roleName, String path, String method) { return false; } + @Override + public Boolean checkUserSessionAccess(String sessionId, String path, String method) { + //get user profile role + var profile = ((UserProfile) redisTemplate.opsForValue().get(sessionId + "_profile")); + if(profile == null) + return false; + + //get role + var roles = roleRepository.findByNames(profile.getRoles()); + if(roles.stream().anyMatch(role -> + role.getGrantedWith().stream().anyMatch(granted -> granted.getPath().compareToIgnoreCase(path) == 0 + && granted.getMethod().compareToIgnoreCase(method) == 0))){ + return true; + } + return false; + } } diff --git a/src/test/java/com/quemistry/auth_ms/controller/AuthenticationControllerTest.java b/src/test/java/com/quemistry/auth_ms/controller/AuthenticationControllerTest.java index 575d3d9..e8fca3c 100644 --- a/src/test/java/com/quemistry/auth_ms/controller/AuthenticationControllerTest.java +++ b/src/test/java/com/quemistry/auth_ms/controller/AuthenticationControllerTest.java @@ -108,7 +108,7 @@ void givenSignOut_Success() throws Exception{ @Test void givenisAuthorised_Success() throws Exception{ var request = new IsAuthorisedRequest(); - request.setRole("teacher"); + request.setRole("tutor"); request.setPath("/questions"); request.setMethod("GET"); @@ -125,4 +125,25 @@ void givenisAuthorised_Success() throws Exception{ Assertions.assertEquals("true", result.getResponse().getContentAsString()); } + + @Test + void givenisAuthorisedUser_Success() throws Exception{ + var request = new IsAuthorisedRequest(); + request.setRole("tutor"); + request.setPath("/questions"); + request.setMethod("GET"); + + ObjectMapper mapper = new ObjectMapper(); + + given(authenticationService.checkUserSessionAccess(request.getSessionId(), request.getPath(), request.getMethod())) + .willReturn(true); + + var result = mockMvc.perform(post("/v1/auth/isauthoriseduser") + .contentType(MediaType.APPLICATION_JSON) + .content(mapper.writeValueAsString(request))) + .andExpect(status().isOk()) + .andReturn(); + + Assertions.assertEquals("true", result.getResponse().getContentAsString()); + } } diff --git a/src/test/java/com/quemistry/auth_ms/service/AuthenticationServiceImplTest.java b/src/test/java/com/quemistry/auth_ms/service/AuthenticationServiceImplTest.java index c78ad1a..c48cee0 100644 --- a/src/test/java/com/quemistry/auth_ms/service/AuthenticationServiceImplTest.java +++ b/src/test/java/com/quemistry/auth_ms/service/AuthenticationServiceImplTest.java @@ -23,10 +23,7 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import java.util.*; //@WebMvcTest(AuthenticationService.class) @ContextConfiguration(classes = {RestClientConfig.class, RedisConfig.class}) @@ -64,10 +61,11 @@ void init() throws NoSuchFieldException, IllegalAccessException { user = new UserProfile(); user.setEmail("testUser@email.com"); + user.setRoles(new String[]{"tutor", "admin"}); user.setSessionId(UUID.randomUUID().toString()); //idtoken with email set as testUser@email.com - idToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJlbWFpbCI6InRlc3RVc2VyQGVtYWlsLmNvbSJ9.cI8ybuRu1FP7_jR9-mHuS2w9EBVueQRMR5DeF2C3pWc"; + idToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdF9oYXNoIjoidThRdnNKdFY1TXRETFdodF9xQmFpZyIsInN1YiI6ImM5YWFkNTRjLTYwZTEtNzA0NS1lNzEyLTlhZDFkYTczZjg3YSIsImNvZ25pdG86Z3JvdXBzIjpbInR1dG9yIiwiYWRtaW4iXSwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJpc3MiOiJodHRwczovL2NvZ25pdG8taWRwLmFwLXNvdXRoZWFzdC0xLmFtYXpvbmF3cy5jb20vYXAtc291dGhlYXN0LTFfWmIwSmwwN1dzIiwiY29nbml0bzp1c2VybmFtZSI6Imdvb2dsZV8xMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJvcmlnaW5fanRpIjoiYTY1M2ExMDEtNTUyMi00ZDIyLTk1NzctZGZkZjA4ZDM5NDc4IiwiYXVkIjoiMXEzMHZtZDB2Y2VlNmsxbHJwMmluMTA2MjMiLCJpZGVudGl0aWVzIjpbeyJkYXRlQ3JlYXRlZCI6IjE3MTcyNTI0Mzk3MDMiLCJ1c2VySWQiOiIxMDQwMjk0OTE2Njc5NjE1ODg4NDAiLCJwcm92aWRlck5hbWUiOiJHb29nbGUiLCJwcm92aWRlclR5cGUiOiJHb29nbGUiLCJpc3N1ZXIiOm51bGwsInByaW1hcnkiOiJ0cnVlIn1dLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTcyMTE0MDE3NywiZXhwIjoxNzIxMTQxMDc3LCJpYXQiOjE3MjExNDAxNzcsImp0aSI6IjU5YjlkZmZlLWFkMjItNDMyZC05ZWIxLTRiZmVhYjFhOGY4MyIsImVtYWlsIjoidGVzdFVzZXJAZW1haWwuY29tIn0.HabZEsulPCsu-IYRE_G42RUWo0k5jMJqYSxJx_QgtuY"; tokenResponse = new TokenResponse(); tokenResponse.setIdToken(idToken); tokenResponse.setAccessToken("testAccessToken"); @@ -105,8 +103,11 @@ void givenGetAccessToken_Success(){ var result = authenticationService.getAccessToken(tokenRequest); tokenResponse.setEmail("testUser@email.com"); + Assertions.assertEquals(user.getEmail(), result.getEmail() ); - } + Assertions.assertEquals(user.getRoles().length, result.getRoles().length ); + + } @Test void givenSignOut_Success(){ @@ -136,11 +137,11 @@ void givenCheckAccess_Success(){ grantedWith.add(apiResource); var role = new Role(); - role.setName("teacher"); + role.setName("tutor"); role.setGrantedWith(grantedWith); - Mockito.when(roleRepository.findByName("teacher")).thenReturn(Optional.of(role)); - var result = authenticationService.checkAccess("teacher", "/questions", "GET"); + Mockito.when(roleRepository.findByName("tutor")).thenReturn(Optional.of(role)); + var result = authenticationService.checkAccess("tutor", "/questions", "GET"); Assertions.assertEquals(result, true); } @@ -154,11 +155,86 @@ void givenCheckAccess_DenyAccess(){ grantedWith.add(apiResource); var role = new Role(); - role.setName("teacher"); + role.setName("tutor"); + role.setGrantedWith(grantedWith); + + Mockito.when(roleRepository.findByName(role.getName())).thenReturn(Optional.of(role)); + var result = authenticationService.checkAccess("tutor", "/questions", "POST"); + + Assertions.assertEquals(result, false); + } + + @Test + void givencheckUserSessionAccess_Success(){ + Set grantedWith = new HashSet<>(); + var apiResource = new ApiResource(); + apiResource.setPath("/questions"); + apiResource.setMethod("GET"); + + grantedWith.add(apiResource); + var role = new Role(); + role.setName("tutor"); + role.setGrantedWith(grantedWith); + + var roles = new ArrayList(); + roles.add(role); + + Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations); + Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile")) + .thenReturn(user); + + Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles); + var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "GET"); + + Assertions.assertEquals(result, true); + } + + @Test + void givencheckUserSessionAccess_InValidSession(){ + Set grantedWith = new HashSet<>(); + var apiResource = new ApiResource(); + apiResource.setPath("/questions"); + apiResource.setMethod("GET"); + + grantedWith.add(apiResource); + var role = new Role(); + role.setName("tutor"); role.setGrantedWith(grantedWith); - Mockito.when(roleRepository.findByName("teacher")).thenReturn(Optional.of(role)); - var result = authenticationService.checkAccess("teacher", "/questions", "POST"); + var roles = new ArrayList(); + roles.add(role); + + Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations); + Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile")) + .thenReturn(null); + + Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles); + var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "GET"); + + Assertions.assertEquals(result, false); + } + + @Test + void givencheckUserSessionAccess_NoAccess(){ + Set grantedWith = new HashSet<>(); + var apiResource = new ApiResource(); + apiResource.setPath("/questions"); + apiResource.setMethod("GET"); + + grantedWith.add(apiResource); + var role = new Role(); + role.setName("tutor"); + role.setGrantedWith(grantedWith); + + var roles = new ArrayList(); + roles.add(role); + + Mockito.when(redisTemplate.opsForValue()).thenReturn(valueOperations); + Mockito.when(redisTemplate.opsForValue().get(user.getSessionId()+"_profile")) + .thenReturn(null); + + Mockito.when(roleRepository.findByNames(user.getRoles())).thenReturn(roles); + var result = authenticationService.checkUserSessionAccess(user.getSessionId(), "/questions", "POST"); Assertions.assertEquals(result, false); }