Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/8.0 #177

Merged
merged 5 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading