diff --git a/.github/workflows/promoteToProd.yml b/.github/workflows/promoteToProd.yml index 780345f6..daf0a24c 100644 --- a/.github/workflows/promoteToProd.yml +++ b/.github/workflows/promoteToProd.yml @@ -14,13 +14,16 @@ jobs: promoteToProd: environment: AWS runs-on: ubuntu-latest + permissions: + id-token: write + contents: read steps: - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v3 + name: Configure AWS credentials with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: github-gap-automated-tests aws-region: ${{ env.AWS_REGION }} - name: Login to AWS ECR diff --git a/.github/workflows/pushImage.yml b/.github/workflows/pushImage.yml index 21616392..a19a68fb 100644 --- a/.github/workflows/pushImage.yml +++ b/.github/workflows/pushImage.yml @@ -55,7 +55,10 @@ jobs: if: github.ref == 'refs/heads/develop' || startsWith(github.ref, 'refs/heads/release') runs-on: ubuntu-latest - + permissions: + id-token: write + contents: read + outputs: docker-image-name: ${{ steps.docker-image-name.outputs.name }} @@ -72,11 +75,11 @@ jobs: distribution: "temurin" cache: maven - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v3 + name: Configure AWS credentials with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: github-gap-automated-tests aws-region: ${{ env.AWS_REGION }} - name: Login to AWS ECR @@ -118,6 +121,9 @@ jobs: deploy: environment: AWS runs-on: ubuntu-latest + permissions: + id-token: write + contents: read needs: [build, test] steps: @@ -126,11 +132,11 @@ jobs: # Fetch all commits since we use the total commit count to determine the build version fetch-depth: 0 - - name: Setup AWS credentials - uses: aws-actions/configure-aws-credentials@v1-node16 + - uses: aws-actions/configure-aws-credentials@v3 + name: Configure AWS credentials with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: github-gap-automated-tests aws-region: ${{ env.AWS_REGION }} - name: Login to AWS ECR diff --git a/src/main/java/gov/cabinetofice/gapuserservice/dto/CreateTechSupportUserDto.java b/src/main/java/gov/cabinetofice/gapuserservice/dto/CreateTechSupportUserDto.java new file mode 100644 index 00000000..8f775eff --- /dev/null +++ b/src/main/java/gov/cabinetofice/gapuserservice/dto/CreateTechSupportUserDto.java @@ -0,0 +1,7 @@ +package gov.cabinetofice.gapuserservice.dto; + +import lombok.Builder; + +@Builder +public record CreateTechSupportUserDto(String userSub, String departmentName) { +} diff --git a/src/main/java/gov/cabinetofice/gapuserservice/dto/UpdateUserRolesRequestDto.java b/src/main/java/gov/cabinetofice/gapuserservice/dto/UpdateUserRolesRequestDto.java new file mode 100644 index 00000000..5df63bc5 --- /dev/null +++ b/src/main/java/gov/cabinetofice/gapuserservice/dto/UpdateUserRolesRequestDto.java @@ -0,0 +1,9 @@ +package gov.cabinetofice.gapuserservice.dto; + +import lombok.Builder; + +import java.util.List; + +@Builder +public record UpdateUserRolesRequestDto(Integer departmentId, List newUserRoles) { +} diff --git a/src/main/java/gov/cabinetofice/gapuserservice/model/RoleEnum.java b/src/main/java/gov/cabinetofice/gapuserservice/model/RoleEnum.java index 14183710..376f7a62 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/model/RoleEnum.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/model/RoleEnum.java @@ -1,9 +1,17 @@ package gov.cabinetofice.gapuserservice.model; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor public enum RoleEnum { - SUPER_ADMIN, - ADMIN, - APPLICANT, - FIND, - TECHNICAL_SUPPORT, -} + SUPER_ADMIN(4), + ADMIN(3), + APPLICANT(2), + FIND(1), + TECHNICAL_SUPPORT(5), + ; + + final int roleId; +} \ No newline at end of file diff --git a/src/main/java/gov/cabinetofice/gapuserservice/repository/SpotlightOAuthAuditRepository.java b/src/main/java/gov/cabinetofice/gapuserservice/repository/SpotlightOAuthAuditRepository.java index b98138e3..35af9144 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/repository/SpotlightOAuthAuditRepository.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/repository/SpotlightOAuthAuditRepository.java @@ -1,10 +1,15 @@ package gov.cabinetofice.gapuserservice.repository; +import gov.cabinetofice.gapuserservice.enums.SpotlightOAuthAuditStatus; import gov.cabinetofice.gapuserservice.model.SpotlightOAuthAudit; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.Collection; + @Repository public interface SpotlightOAuthAuditRepository extends JpaRepository { - SpotlightOAuthAudit findFirstByOrderByIdDesc(); + + SpotlightOAuthAudit findFirstByStatusInOrderByIdDesc(Collection statuses); + } \ No newline at end of file diff --git a/src/main/java/gov/cabinetofice/gapuserservice/service/SpotlightService.java b/src/main/java/gov/cabinetofice/gapuserservice/service/SpotlightService.java index 4b885940..2c988cb5 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/service/SpotlightService.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/service/SpotlightService.java @@ -3,6 +3,7 @@ import com.nimbusds.jose.shaded.gson.JsonObject; import com.nimbusds.jose.shaded.gson.JsonParser; import gov.cabinetofice.gapuserservice.config.SpotlightConfig; +import gov.cabinetofice.gapuserservice.enums.SpotlightOAuthAuditStatus; import gov.cabinetofice.gapuserservice.exceptions.InvalidRequestException; import gov.cabinetofice.gapuserservice.exceptions.SpotlightInvalidStateException; import gov.cabinetofice.gapuserservice.model.SpotlightOAuthAudit; @@ -26,6 +27,7 @@ import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; +import java.util.List; @RequiredArgsConstructor @Service @@ -47,8 +49,8 @@ public class SpotlightService { private final static String ACCESS_TOKEN_NAME = "access_token"; private final static String REFRESH_TOKEN_NAME = "refresh_token"; - public SpotlightOAuthAudit getLatestAudit() { - return spotlightOAuthAuditRepository.findFirstByOrderByIdDesc(); + public SpotlightOAuthAudit getLatestSuccessOrFailureAudit() { + return spotlightOAuthAuditRepository.findFirstByStatusInOrderByIdDesc(List.of(SpotlightOAuthAuditStatus.SUCCESS, SpotlightOAuthAuditStatus.FAILURE)); } public void saveAudit(SpotlightOAuthAudit spotlightOAuthAudit) { diff --git a/src/main/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserService.java b/src/main/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserService.java index cf054042..e47814b8 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserService.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserService.java @@ -31,6 +31,7 @@ import org.springframework.http.MediaType; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; @@ -194,16 +195,19 @@ public User updateDepartment(Integer id, Integer departmentId, String jwt) { return userRepository.save(user); } - public User updateRoles(Integer id, List newRoles) { + public User updateRoles(Integer id, UpdateUserRolesRequestDto updateUserRolesRequestDto, String jwt) { User user = userRepository.findById(id).orElseThrow(() -> new RuntimeException("User not found")); + + handleTechSupportRoleChange(user, updateUserRolesRequestDto, jwt); + user.removeAllRoles(); - if (newRoles == null || newRoles.isEmpty()) { + if (updateUserRolesRequestDto.newUserRoles().isEmpty()) { userRepository.save(user); return user; } - for (Integer roleId : newRoles) { + for (Integer roleId : updateUserRolesRequestDto.newUserRoles()) { Role role = roleRepository.findById(roleId).orElseThrow(); user.addRole(role); } @@ -216,6 +220,25 @@ public User updateRoles(Integer id, List newRoles) { return user; } + private void handleTechSupportRoleChange(User user, UpdateUserRolesRequestDto updateUserRolesRequestDto, String jwt) { + if (updateUserRolesRequestDto.newUserRoles().contains(RoleEnum.TECHNICAL_SUPPORT.getRoleId()) + && !user.isTechnicalSupport()) { + + Integer departmentId = updateUserRolesRequestDto.departmentId() == null + ? user.getDepartment().getId() : updateUserRolesRequestDto.departmentId(); + + Department department = departmentRepository.findById(departmentId) + .orElseThrow(() -> new DepartmentNotFoundException + ("Department not found with id: " + updateUserRolesRequestDto.departmentId())); + addTechSupportUserToApply(user, department.getName(), jwt); + } else { + if (user.isTechnicalSupport() && !updateUserRolesRequestDto.newUserRoles() + .contains(RoleEnum.TECHNICAL_SUPPORT.getRoleId())) { + deleteTechSupportUserFromApply(user.getSub(), jwt); + } + } + } + private void addRoleIfNotPresent(User user, RoleEnum roleName) { if (user.getRoles().stream().noneMatch(role -> role.getName().equals(roleName))) { Role role = roleRepository.findByName(roleName).orElseThrow(() -> new RoleNotFoundException( @@ -250,6 +273,33 @@ public void deleteUser(Integer id, String jwt) { userRepository.deleteById(id); } + public void addTechSupportUserToApply(User user, String departmentName, String jwt) { + webClientBuilder.build() + .post() + .uri(adminBackend.concat("/users/tech-support-user")) + .contentType(MediaType.APPLICATION_JSON) + .body(BodyInserters.fromValue(CreateTechSupportUserDto.builder() + .userSub(user.getSub()).departmentName(departmentName).build())) + .header(AUTHORIZATION_HEADER_NAME, BEARER_HEADER_PREFIX + jwt) + .retrieve() + .bodyToMono(Void.class) + .block(); + } + + public void deleteTechSupportUserFromApply(String sub, String jwt) { + webClientBuilder.build() + .delete() + .uri(adminBackend.concat("/users/tech-support-user/".concat(sub))) + .header(AUTHORIZATION_HEADER_NAME, BEARER_HEADER_PREFIX + jwt) + .retrieve() + .onStatus(httpStatus -> !httpStatus.equals(HttpStatus.OK), clientResponse -> { + log.error("Unable to delete tech support user with sub {}, HTTP status code {}", sub, clientResponse.statusCode()); + return Mono.empty(); + }) + .bodyToMono(Void.class) + .block(); + } + private void deleteUserFromApply(String jwt, User user) { String query = user.hasSub() ? "?oneLoginSub=" + user.getSub() : "?colaSub=" + user.getColaSub(); @@ -408,7 +458,8 @@ public List getUserEmailsBySubs(List subs) { return users.stream().map(user -> UserEmailDto.builder() .emailAddress(awsEncryptionService.encryptField(user.getEmailAddress())) .sub(user.getSub()) - .build()).collect(Collectors.toList()); + .build()) + .toList(); } @PreAuthorize("hasRole('SUPER_ADMIN')") diff --git a/src/main/java/gov/cabinetofice/gapuserservice/web/SpotlightController.java b/src/main/java/gov/cabinetofice/gapuserservice/web/SpotlightController.java index 182e9520..ec4b7066 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/web/SpotlightController.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/web/SpotlightController.java @@ -47,7 +47,7 @@ public ResponseEntity getIntegrations(final HttpSe if (!roleService.isSuperAdmin(httpRequest)) { throw new ForbiddenException(); } - SpotlightOAuthAudit audit = spotlightService.getLatestAudit(); + SpotlightOAuthAudit audit = spotlightService.getLatestSuccessOrFailureAudit(); if (Objects.equals(null, audit)) { throw new InvalidRequestException("No audit found"); } diff --git a/src/main/java/gov/cabinetofice/gapuserservice/web/UserController.java b/src/main/java/gov/cabinetofice/gapuserservice/web/UserController.java index 85c3e75f..2e954bdd 100644 --- a/src/main/java/gov/cabinetofice/gapuserservice/web/UserController.java +++ b/src/main/java/gov/cabinetofice/gapuserservice/web/UserController.java @@ -80,6 +80,11 @@ public ResponseEntity getUserByUserSub(@RequestParam("userS return ResponseEntity.ok(new UserAndRelationsDto(oneLoginUserService.getUserByUserSub(userSub))); } + @GetMapping("/user/{userSub}/email") + public ResponseEntity getEmailFromSub(HttpServletRequest httpRequest, @PathVariable("userSub") String userSub) { + return ResponseEntity.ok(oneLoginUserService.getUserBySub(userSub).getEmailAddress()); + } + @PatchMapping("/user/{userId}/department") public ResponseEntity updateDepartment(HttpServletRequest httpRequest, @PathVariable("userId") Integer userId, @Validated @RequestBody ChangeDepartmentDto changeDepartmentDto) { @@ -113,13 +118,16 @@ public ResponseEntity getChangeDepartmentPage(HttpServl } @PatchMapping("/user/{id}/role") - public ResponseEntity updateRoles(HttpServletRequest httpRequest, @RequestBody() List roleIds, + public ResponseEntity updateRoles(HttpServletRequest httpRequest, + @RequestBody() UpdateUserRolesRequestDto updateUserRolesRequestDto, @PathVariable("id") Integer id) { if (!roleService.isSuperAdmin(httpRequest)) { throw new ForbiddenException(); } - boolean isARequestToBlockUser = roleIds.isEmpty(); + final Cookie customJWTCookie = getCustomJwtCookieFromRequest(httpRequest, userServiceCookieName); + + boolean isARequestToBlockUser = updateUserRolesRequestDto.newUserRoles().isEmpty(); Optional user = jwtService.getUserFromJwt(httpRequest); if (user.isEmpty()) { @@ -129,7 +137,7 @@ public ResponseEntity updateRoles(HttpServletRequest httpRequest, @Reque throw new UnsupportedOperationException("You can't block yourself"); } - oneLoginUserService.updateRoles(id, roleIds); + oneLoginUserService.updateRoles(id, updateUserRolesRequestDto, customJWTCookie.getValue()); return ResponseEntity.ok("success"); } @@ -166,7 +174,5 @@ public ResponseEntity getUserByEmail(@PathVariable("email") String emai .orElseGet(() -> new UserDto(oneLoginUserService.getUserByEmail(email))) ); } - - } diff --git a/src/test/java/gov/cabinetofice/gapuserservice/service/SpotlightServiceTest.java b/src/test/java/gov/cabinetofice/gapuserservice/service/SpotlightServiceTest.java index 03f169d2..f4bcfdcf 100644 --- a/src/test/java/gov/cabinetofice/gapuserservice/service/SpotlightServiceTest.java +++ b/src/test/java/gov/cabinetofice/gapuserservice/service/SpotlightServiceTest.java @@ -1,11 +1,8 @@ package gov.cabinetofice.gapuserservice.service; import gov.cabinetofice.gapuserservice.config.SpotlightConfig; -import gov.cabinetofice.gapuserservice.enums.SpotlightOAuthAuditEvent; -import gov.cabinetofice.gapuserservice.enums.SpotlightOAuthAuditStatus; import gov.cabinetofice.gapuserservice.exceptions.InvalidRequestException; import gov.cabinetofice.gapuserservice.exceptions.SpotlightInvalidStateException; -import gov.cabinetofice.gapuserservice.model.SpotlightOAuthAudit; import gov.cabinetofice.gapuserservice.repository.SpotlightOAuthAuditRepository; import gov.cabinetofice.gapuserservice.util.RestUtils; import org.apache.http.impl.client.HttpClients; @@ -135,16 +132,6 @@ void shouldThrowExceptionWhenSateValueDoesNotMatchTest() { } - @Test - void shouldGetLatestAudit() { - SpotlightOAuthAudit spotlightOAuthAudit = SpotlightOAuthAudit.builder().id(1).status( - SpotlightOAuthAuditStatus.FAILURE).event(SpotlightOAuthAuditEvent.AUTHORISE).build(); - when(spotlightOAuthAuditRepository.findFirstByOrderByIdDesc()).thenReturn(spotlightOAuthAudit); - - assertEquals(spotlightOAuthAudit, spotlightService.getLatestAudit()); - } - - @Test void shouldRefreshTokenTest() throws Exception { diff --git a/src/test/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserServiceTest.java b/src/test/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserServiceTest.java index 472618c3..09c20339 100644 --- a/src/test/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserServiceTest.java +++ b/src/test/java/gov/cabinetofice/gapuserservice/service/user/OneLoginUserServiceTest.java @@ -71,11 +71,12 @@ void setUp() { @Test void shouldReturnUpdatedUserWhenValidIdAndRolesAreGiven() { Integer userId = 1; - List newRoles = List.of(1, 2); List currentUserRoles = List.of(Role.builder().name(RoleEnum.FIND).id(1).build()); User user = spy(User.builder().gapUserId(1).sub("sub").roles(currentUserRoles).build()); Role role1 = Role.builder().id(1).name(RoleEnum.FIND).description("a desc").build(); Role role2 = Role.builder().id(2).name(RoleEnum.APPLICANT).description("a desc 2").build(); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(1, 2)).build(); doNothing().when(user).removeAllRoles(); doNothing().when(user).addRole(role2); @@ -86,7 +87,7 @@ void shouldReturnUpdatedUserWhenValidIdAndRolesAreGiven() { when(roleRepository.findById(2)).thenReturn(Optional.of(role2)); when(roleRepository.findByName(RoleEnum.APPLICANT)).thenReturn(Optional.of(role2)); - User updatedUser = oneLoginUserService.updateRoles(1, newRoles); + User updatedUser = oneLoginUserService.updateRoles(1, updateUserRolesRequestDto, "jwt"); Mockito.verify(roleRepository, times(2)).findById(anyInt()); Mockito.verify(roleRepository, times(1)).findByName(any(RoleEnum.class)); @@ -446,13 +447,15 @@ void testDeleteUser_DeletesUser() { void updateRolesShouldAddApplicantAndFindRolesWhenNoRolesPresent() { Integer userId = 1; User user = User.builder().gapUserId(userId).build(); - List newRoles = List.of(3, 4); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(3, 4)).build(); + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(roleRepository.findById(3)).thenReturn(Optional.of(Role.builder().name(RoleEnum.ADMIN).build())); when(roleRepository.findById(4)).thenReturn(Optional.of(Role.builder().name(RoleEnum.SUPER_ADMIN).build())); when(roleRepository.findByName(RoleEnum.APPLICANT)).thenReturn(Optional.of(Role.builder().name(RoleEnum.APPLICANT).build())); when(roleRepository.findByName(RoleEnum.FIND)).thenReturn(Optional.of(Role.builder().name(RoleEnum.FIND).build())); - User updatedUser = oneLoginUserService.updateRoles(userId, newRoles); + User updatedUser = oneLoginUserService.updateRoles(userId, updateUserRolesRequestDto, "jwt"); assertThat(updatedUser.getRoles()).hasSize(4); assertThat(updatedUser.getRoles().stream().anyMatch(role -> role.getName().equals(RoleEnum.APPLICANT))).isTrue(); @@ -514,12 +517,14 @@ void testValidateSessionsRolesThrowsInvalidExceptionWithEmptyUser() { @Test void updateRolesShouldSetDepartmentToNullIfNotSuperAdminOrAdmin() { Integer userId = 1; - List newRoles = List.of(1, 2); User user = User.builder().gapUserId(userId).department(Department.builder().name("test").build()).build(); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(1, 2)).build(); + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(roleRepository.findById(1)).thenReturn(Optional.of(Role.builder().name(RoleEnum.APPLICANT).build())); when(roleRepository.findById(2)).thenReturn(Optional.of(Role.builder().name(RoleEnum.FIND).build())); - User updatedUser = oneLoginUserService.updateRoles(1, newRoles); + User updatedUser = oneLoginUserService.updateRoles(1, updateUserRolesRequestDto, "jwt"); assertThat(updatedUser.getDepartment()).isNull(); } @@ -527,13 +532,15 @@ void updateRolesShouldSetDepartmentToNullIfNotSuperAdminOrAdmin() { @Test void updateRolesShouldNotSetDepartmentToNullIfUserHasMoreThanTwoRoles() { Integer userId = 1; - List newRoles = List.of(1, 2, 3); User user = User.builder().gapUserId(userId).department(Department.builder().name("test").build()).build(); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(1, 2, 3)).build(); + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); when(roleRepository.findById(1)).thenReturn(Optional.of(Role.builder().name(RoleEnum.APPLICANT).build())); when(roleRepository.findById(2)).thenReturn(Optional.of(Role.builder().name(RoleEnum.FIND).build())); when(roleRepository.findById(3)).thenReturn(Optional.of(Role.builder().name(RoleEnum.ADMIN).build())); - User updatedUser = oneLoginUserService.updateRoles(1, newRoles); + User updatedUser = oneLoginUserService.updateRoles(1, updateUserRolesRequestDto, "jwt"); assertThat(updatedUser.getDepartment()).isNotNull(); } @@ -541,14 +548,33 @@ void updateRolesShouldNotSetDepartmentToNullIfUserHasMoreThanTwoRoles() { @Test void updateRolesShouldNotSetDepartmentToNullIfUserIsAdminOrSuperAdmin() { Integer userId = 1; - List newRoles = List.of(3, 4); - User user = User.builder().gapUserId(userId).department(Department.builder().name("test").build()).build(); + Department department = Department.builder().id(1).name("test").build(); + User user = User.builder().gapUserId(userId).department(department).build(); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(1, 2, 3, 4, 5)).build(); + + final WebClient webClient = mock(WebClient.class); + final WebClient.RequestHeadersSpec requestHeadersSpec = mock(WebClient.RequestHeadersSpec.class); + final WebClient.RequestBodyUriSpec requestBodyUriSpec = mock(WebClient.RequestBodyUriSpec.class); + final WebClient.ResponseSpec responseSpec = mock(WebClient.ResponseSpec.class); + + when(webClientBuilder.build()).thenReturn(webClient); + when(webClient.post()).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.uri(anyString())).thenReturn(requestBodyUriSpec); + when(requestHeadersSpec.header(anyString(), anyString())).thenReturn(requestHeadersSpec); + when(requestBodyUriSpec.contentType(any())).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.body(any())).thenReturn(requestHeadersSpec); + when(requestHeadersSpec.retrieve()).thenReturn(responseSpec); + when(responseSpec.bodyToMono(Void.class)).thenReturn(Mono.empty()); + when(userRepository.findById(userId)).thenReturn(Optional.of(user)); + when(roleRepository.findById(1)).thenReturn(Optional.of(Role.builder().name(RoleEnum.FIND).build())); + when(departmentRepository.findById(1)).thenReturn(Optional.of(department)); + when(roleRepository.findById(2)).thenReturn(Optional.of(Role.builder().name(RoleEnum.APPLICANT).build())); when(roleRepository.findById(3)).thenReturn(Optional.of(Role.builder().name(RoleEnum.ADMIN).build())); when(roleRepository.findById(4)).thenReturn(Optional.of(Role.builder().name(RoleEnum.SUPER_ADMIN).build())); - when(roleRepository.findByName(RoleEnum.FIND)).thenReturn(Optional.of(Role.builder().name(RoleEnum.FIND).id(1).build())); - when(roleRepository.findByName(RoleEnum.APPLICANT)).thenReturn(Optional.of(Role.builder().name(RoleEnum.APPLICANT).id(2).build())); - User updatedUser = oneLoginUserService.updateRoles(1, newRoles); + when(roleRepository.findById(5)).thenReturn(Optional.of(Role.builder().name(RoleEnum.TECHNICAL_SUPPORT).build())); + User updatedUser = oneLoginUserService.updateRoles(1, updateUserRolesRequestDto, "jwt"); assertThat(updatedUser.getDepartment()).isNotNull(); } diff --git a/src/test/java/gov/cabinetofice/gapuserservice/web/SpotlightControllerTest.java b/src/test/java/gov/cabinetofice/gapuserservice/web/SpotlightControllerTest.java index 2bff0a72..c39fdd5d 100644 --- a/src/test/java/gov/cabinetofice/gapuserservice/web/SpotlightControllerTest.java +++ b/src/test/java/gov/cabinetofice/gapuserservice/web/SpotlightControllerTest.java @@ -172,7 +172,7 @@ void shouldThrowExceptionIfTokenExchangeFails() throws IOException { @Nested class IntegrationTest { @Test - void shouldReturnIntegrationAuditDto() throws Exception { + void shouldReturnIntegrationAuditDto() { HttpServletRequest httpRequest = mock(HttpServletRequest.class); Date date = new Date(); SpotlightOAuthAudit audit = SpotlightOAuthAudit.builder().event(SpotlightOAuthAuditEvent.AUTHORISE) @@ -181,7 +181,7 @@ void shouldReturnIntegrationAuditDto() throws Exception { SpotlightOAuthAuditEvent.AUTHORISE, SpotlightOAuthAuditStatus.FAILURE, date); when(roleService.isSuperAdmin(any(HttpServletRequest.class))).thenReturn(true); - when(spotlightService.getLatestAudit()).thenReturn(audit); + when(spotlightService.getLatestSuccessOrFailureAudit()).thenReturn(audit); assertEquals(SpotlightController.getIntegrations(httpRequest), ResponseEntity.ok(auditDto)); } @@ -189,10 +189,18 @@ void shouldReturnIntegrationAuditDto() throws Exception { void shouldThrowInvalidRequestWhenNoIntegrationAuditFound() { HttpServletRequest httpRequest = mock(HttpServletRequest.class); when(roleService.isSuperAdmin(any(HttpServletRequest.class))).thenReturn(true); - when(spotlightService.getLatestAudit()).thenReturn(null); + when(spotlightService.getLatestSuccessOrFailureAudit()).thenReturn(null); assertThrows(InvalidRequestException.class, () -> SpotlightController.getIntegrations(httpRequest)); } + + @Test + void shouldThrowForbiddenExceptionWhenUserIsNotSuperAdmin() { + HttpServletRequest httpRequest = mock(HttpServletRequest.class); + when(roleService.isSuperAdmin(any(HttpServletRequest.class))).thenReturn(false); + + assertThrows(ForbiddenException.class, () -> SpotlightController.getIntegrations(httpRequest)); + } } @Nested diff --git a/src/test/java/gov/cabinetofice/gapuserservice/web/UserControllerTest.java b/src/test/java/gov/cabinetofice/gapuserservice/web/UserControllerTest.java index 303003cb..2e98d46d 100644 --- a/src/test/java/gov/cabinetofice/gapuserservice/web/UserControllerTest.java +++ b/src/test/java/gov/cabinetofice/gapuserservice/web/UserControllerTest.java @@ -3,6 +3,7 @@ import gov.cabinetofice.gapuserservice.dto.*; import gov.cabinetofice.gapuserservice.exceptions.ForbiddenException; import gov.cabinetofice.gapuserservice.exceptions.InvalidRequestException; +import gov.cabinetofice.gapuserservice.exceptions.UserNotFoundException; import gov.cabinetofice.gapuserservice.model.Role; import gov.cabinetofice.gapuserservice.model.RoleEnum; import gov.cabinetofice.gapuserservice.model.User; @@ -13,20 +14,20 @@ import gov.cabinetofice.gapuserservice.service.user.OneLoginUserService; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; +import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.http.ResponseEntity; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.util.WebUtils; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -51,17 +52,30 @@ class UserControllerTest { @Mock private SecretAuthService secretAuthService; + private static MockedStatic mockedWebUtils; + @BeforeEach void setUp() { + mockedWebUtils = mockStatic(WebUtils.class); ReflectionTestUtils.setField(controller, "userServiceCookieName", "userServiceCookieName"); } + @AfterEach + public void close() { + mockedWebUtils.close(); + } + @Test void updateRolesForUserId() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(Arrays.asList(1, 2, 3, 4, 5)).build(); + + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.of(User.builder().gapUserId(2).build())); - final ResponseEntity methodResponse = controller.updateRoles(httpRequest, List.of(1, 2), 1); + final ResponseEntity methodResponse = controller.updateRoles(httpRequest,updateUserRolesRequestDto, 1); assertThat(methodResponse).isEqualTo(ResponseEntity.ok("success")); } @@ -70,20 +84,32 @@ void updateRolesForUserId() { void testSuperAdminCannotBlockThemselves() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); - final ArrayList noRoles = new ArrayList<>(); + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); + + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(new ArrayList<>()).build(); + when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.of(User.builder().gapUserId(1).build())); - assertThrows(UnsupportedOperationException.class, () -> controller.updateRoles(httpRequest, noRoles, 1)); + assertThrows(UnsupportedOperationException.class, () -> controller.updateRoles(httpRequest, + updateUserRolesRequestDto, 1)); } @Test void testSuperAdminThrowsInvalidRequestWithNoUser() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); - final ArrayList noRoles = new ArrayList<>(); + UpdateUserRolesRequestDto updateUserRolesRequestDto = UpdateUserRolesRequestDto.builder() + .newUserRoles(new ArrayList<>()).departmentId(1).build(); + + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); + when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.empty()); - assertThrows(InvalidRequestException.class, () -> controller.updateRoles(httpRequest, noRoles, 1)); + assertThrows(InvalidRequestException.class, () -> controller.updateRoles(httpRequest, + updateUserRolesRequestDto, 1)); } @Test @@ -124,7 +150,10 @@ void shouldReturnChangeDepartmentPageDtoWhenValidIdIsGiven() { @Test void shouldDeleteUserWhenValidIdIsGiven() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); - when(httpRequest.getCookies()).thenReturn(new Cookie[]{new Cookie("userServiceCookieName", "1")}); + + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); + when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.of(User.builder().gapUserId(2).build())); final ResponseEntity methodResponse = controller.deleteUser(httpRequest, 1); @@ -135,7 +164,10 @@ void shouldDeleteUserWhenValidIdIsGiven() { @Test void shouldThrowErrorWhenAdminTriesToDeleteThemselves() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); - when(httpRequest.getCookies()).thenReturn(new Cookie[]{new Cookie("userServiceCookieName", "1")}); + + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); + when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.of(User.builder().gapUserId(1).build())); @@ -145,7 +177,10 @@ void shouldThrowErrorWhenAdminTriesToDeleteThemselves() { @Test void shouldThrowErrorWhenUserIsEmpty() { final HttpServletRequest httpRequest = mock(HttpServletRequest.class); - when(httpRequest.getCookies()).thenReturn(new Cookie[]{new Cookie("userServiceCookieName", "1")}); + + mockedWebUtils.when(() -> WebUtils.getCookie(httpRequest, "userServiceCookieName")) + .thenReturn(new Cookie("userServiceCookieName", "jwt")); + when(roleService.isSuperAdmin(httpRequest)).thenReturn(true); when(customJwtService.getUserFromJwt(httpRequest)).thenReturn(Optional.empty()); @@ -252,4 +287,29 @@ void testGetUserByEmailWhenRoleIsAbsent() { assertThat(methodResponse.getBody()).isEqualTo(mockUserDto); } + @Test + void testGetEmailFromSub() { + String userSub = "1"; + String userEmail = "test@test.com"; + User mockUser = User.builder().sub(userSub).gapUserId(1) + .emailAddress(userEmail).build(); + when(oneLoginUserService.getUserBySub(userSub)).thenReturn(mockUser); + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + final ResponseEntity methodResponse = controller.getEmailFromSub(mockRequest, userSub); + + assertThat(methodResponse.getBody()).isEqualTo(userEmail); + verify(oneLoginUserService, times(1)).getUserBySub(userSub); + } + + @Test + void testGetEmailFromSub_UserNotFound() { + String userSub = "2"; + when(oneLoginUserService.getUserBySub(userSub)).thenThrow(UserNotFoundException.class); + HttpServletRequest mockRequest = mock(HttpServletRequest.class); + + assertThrows(UserNotFoundException.class, () -> { + controller.getEmailFromSub(mockRequest, userSub); + }); + verify(oneLoginUserService, times(1)).getUserBySub(userSub); + } } \ No newline at end of file