Skip to content

Commit

Permalink
Merge pull request #177 from cabinetoffice/release/8.0
Browse files Browse the repository at this point in the history
Release/8.0
  • Loading branch information
iaincooper-tco authored Feb 7, 2024
2 parents 768c4b4 + 10386d8 commit b36d0e6
Show file tree
Hide file tree
Showing 14 changed files with 251 additions and 73 deletions.
11 changes: 7 additions & 4 deletions .github/workflows/promoteToProd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 15 additions & 9 deletions .github/workflows/pushImage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }}

Expand All @@ -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
Expand Down Expand Up @@ -118,6 +121,9 @@ jobs:
deploy:
environment: AWS
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
needs: [build, test]

steps:
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gov.cabinetofice.gapuserservice.dto;

import lombok.Builder;

@Builder
public record CreateTechSupportUserDto(String userSub, String departmentName) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gov.cabinetofice.gapuserservice.dto;

import lombok.Builder;

import java.util.List;

@Builder
public record UpdateUserRolesRequestDto(Integer departmentId, List<Integer> newUserRoles) {
}
20 changes: 14 additions & 6 deletions src/main/java/gov/cabinetofice/gapuserservice/model/RoleEnum.java
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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, Integer> {
SpotlightOAuthAudit findFirstByOrderByIdDesc();

SpotlightOAuthAudit findFirstByStatusInOrderByIdDesc(Collection<SpotlightOAuthAuditStatus> statuses);

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +27,7 @@
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;

@RequiredArgsConstructor
@Service
Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -194,16 +195,19 @@ public User updateDepartment(Integer id, Integer departmentId, String jwt) {
return userRepository.save(user);
}

public User updateRoles(Integer id, List<Integer> 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);
}
Expand All @@ -216,6 +220,25 @@ public User updateRoles(Integer id, List<Integer> 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(
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -408,7 +458,8 @@ public List<UserEmailDto> getUserEmailsBySubs(List<String> 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')")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public ResponseEntity<SpotlightIntegrationAuditDto> 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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public ResponseEntity<UserAndRelationsDto> getUserByUserSub(@RequestParam("userS
return ResponseEntity.ok(new UserAndRelationsDto(oneLoginUserService.getUserByUserSub(userSub)));
}

@GetMapping("/user/{userSub}/email")
public ResponseEntity<String> getEmailFromSub(HttpServletRequest httpRequest, @PathVariable("userSub") String userSub) {
return ResponseEntity.ok(oneLoginUserService.getUserBySub(userSub).getEmailAddress());
}

@PatchMapping("/user/{userId}/department")
public ResponseEntity<User> updateDepartment(HttpServletRequest httpRequest, @PathVariable("userId") Integer userId,
@Validated @RequestBody ChangeDepartmentDto changeDepartmentDto) {
Expand Down Expand Up @@ -113,13 +118,16 @@ public ResponseEntity<ChangeDepartmentPageDto> getChangeDepartmentPage(HttpServl
}

@PatchMapping("/user/{id}/role")
public ResponseEntity<String> updateRoles(HttpServletRequest httpRequest, @RequestBody() List<Integer> roleIds,
public ResponseEntity<String> 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> user = jwtService.getUserFromJwt(httpRequest);

if (user.isEmpty()) {
Expand All @@ -129,7 +137,7 @@ public ResponseEntity<String> 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");
}

Expand Down Expand Up @@ -166,7 +174,5 @@ public ResponseEntity<UserDto> getUserByEmail(@PathVariable("email") String emai
.orElseGet(() -> new UserDto(oneLoginUserService.getUserByEmail(email)))
);
}


}

Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {

Expand Down
Loading

0 comments on commit b36d0e6

Please sign in to comment.