-
Couldn't load subscription status.
- Fork 0
S28-3980 What's this UUID #1114
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
base: master
Are you sure you want to change the base?
Changes from 12 commits
4febcea
002a7f6
bc3eeaf
93b9855
d889a74
33c8fe1
4cac144
68ea121
de93c53
fd6a3d4
c5f6f0d
c62f209
6772af9
437faf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| package uk.gov.hmcts.reform.preapi.controllers; | ||
|
|
||
| import com.fasterxml.jackson.core.JsonProcessingException; | ||
| import org.junit.jupiter.api.DisplayName; | ||
| import org.junit.jupiter.api.Test; | ||
| import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; | ||
| import uk.gov.hmcts.reform.preapi.dto.CreateUserDTO; | ||
| import uk.gov.hmcts.reform.preapi.util.FunctionalTestBase; | ||
|
|
||
| import java.util.Set; | ||
| import java.util.UUID; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| public class AdminControllerFT extends FunctionalTestBase { | ||
|
|
||
| protected static final String ADMIN_ENDPOINT = "/admin"; | ||
|
|
||
| @DisplayName("Should get back table type UUID belongs to") | ||
| @Test | ||
| void checkUuidExists() throws JsonProcessingException { | ||
| UUID randomUuid = UUID.randomUUID(); | ||
| var user = createUserDto(randomUuid); | ||
| putUser(user); | ||
|
|
||
| var response = | ||
| doGetRequest(ADMIN_ENDPOINT + "/" + user.getId(), TestingSupportRoles.SUPER_USER); | ||
|
|
||
| assertThat(response.body().prettyPrint()) | ||
| .isEqualTo("Uuid relates to a USER"); | ||
| assertResponseCode(response, 200); | ||
| } | ||
|
|
||
| @DisplayName("Should return bad request when UUID not present in relevant tables") | ||
| @Test | ||
| void checkRequestFails() throws JsonProcessingException { | ||
|
|
||
| UUID randomUuid = UUID.randomUUID(); | ||
|
|
||
| var response = | ||
| doGetRequest(ADMIN_ENDPOINT + "/" + randomUuid, TestingSupportRoles.SUPER_USER); | ||
|
|
||
| assertThat(response.body().jsonPath().getString("message")) | ||
| .isEqualTo("Not found: " + randomUuid + " does not exist in any relevant table"); | ||
| assertResponseCode(response, 404); | ||
| } | ||
|
|
||
| @DisplayName("Should return not authorised due to role") | ||
| @Test | ||
| void checkIfUserAuthorised() throws JsonProcessingException { | ||
| UUID randomUuid = UUID.randomUUID(); | ||
| var user = createUserDto(randomUuid); | ||
| putUser(user); | ||
|
|
||
| var response = | ||
| doGetRequest(ADMIN_ENDPOINT + "/" + user.getId(), TestingSupportRoles.LEVEL_1); | ||
|
|
||
| assertThat(response.body().jsonPath().getString("error")) | ||
| .isEqualTo("Forbidden"); | ||
| assertResponseCode(response, 403); | ||
| } | ||
|
|
||
| private CreateUserDTO createUserDto(UUID uuid) { | ||
| var dto = new CreateUserDTO(); | ||
| dto.setId(uuid); | ||
| dto.setEmail("[email protected]"); | ||
| dto.setFirstName("Example"); | ||
| dto.setLastName("User"); | ||
| dto.setPortalAccess(Set.of()); | ||
| dto.setAppAccess(Set.of()); | ||
| dto.setOrganisation("Example Organisation"); | ||
| dto.setPhoneNumber("1234567890"); | ||
| return dto; | ||
| } | ||
|
|
||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| package uk.gov.hmcts.reform.preapi.services.admin; | ||
|
|
||
| import jakarta.persistence.EntityManager; | ||
| import org.junit.jupiter.api.Test; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
| import uk.gov.hmcts.reform.preapi.entities.Booking; | ||
| import uk.gov.hmcts.reform.preapi.entities.CaptureSession; | ||
| import uk.gov.hmcts.reform.preapi.entities.Case; | ||
| import uk.gov.hmcts.reform.preapi.entities.Court; | ||
| import uk.gov.hmcts.reform.preapi.entities.Recording; | ||
| import uk.gov.hmcts.reform.preapi.entities.Role; | ||
| import uk.gov.hmcts.reform.preapi.entities.User; | ||
| import uk.gov.hmcts.reform.preapi.enums.CourtType; | ||
| import uk.gov.hmcts.reform.preapi.enums.RecordingOrigin; | ||
| import uk.gov.hmcts.reform.preapi.exception.NotFoundException; | ||
| import uk.gov.hmcts.reform.preapi.repositories.admin.AdminRepository; | ||
| import uk.gov.hmcts.reform.preapi.util.HelperFactory; | ||
| import uk.gov.hmcts.reform.preapi.utils.IntegrationTestBase; | ||
|
|
||
| import java.sql.Timestamp; | ||
| import java.util.UUID; | ||
|
|
||
| import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
| import static org.assertj.core.api.AssertionsForClassTypes.assertThat; | ||
|
|
||
| public class AdminServiceIT extends IntegrationTestBase { | ||
|
|
||
| @Autowired | ||
| AdminService adminService; | ||
|
|
||
| @Autowired | ||
| AdminRepository adminRepository; | ||
|
|
||
| @Autowired | ||
| protected EntityManager entityManager; | ||
|
|
||
| @Test | ||
| @Transactional | ||
| public void shouldCheckUuidExists() { | ||
| Timestamp now = new Timestamp(System.currentTimeMillis()); | ||
| //Put a user in the database to test UUID against | ||
| User user = HelperFactory.createUser("Example", "One", "[email protected]", null, null, null); | ||
| entityManager.persist(user); | ||
| Court court = HelperFactory.createCourt(CourtType.CROWN, "Test Court", "Test123"); | ||
| entityManager.persist(court); | ||
| Case case1 = HelperFactory.createCase(court, "null", true, now); | ||
| entityManager.persist(case1); | ||
| Booking booking = HelperFactory.createBooking(case1, now, now); | ||
| entityManager.persist(booking); | ||
| CaptureSession captureSession = HelperFactory.createCaptureSession( | ||
| booking, RecordingOrigin.PRE, | ||
| null, null, null, null, null, | ||
| null, null, null | ||
| ); | ||
| entityManager.persist(captureSession); | ||
| Recording recording = HelperFactory.createRecording(captureSession, null, 1, "filename", null); | ||
| entityManager.persist(recording); | ||
|
|
||
| entityManager.flush(); | ||
| UUID userGeneratedId = user.getId(); | ||
| UUID bookingGeneratedId = booking.getId(); | ||
| UUID caseGeneratedId = case1.getId(); | ||
| UUID courtGeneratedId = court.getId(); | ||
| UUID captureSessionGeneratedId = captureSession.getId(); | ||
| UUID recordingGeneratedId = recording.getId(); | ||
|
|
||
| assertThat(adminService.findUuidType(userGeneratedId)).isEqualTo(AdminService.UuidTableType.USER); | ||
| assertThat(adminService.findUuidType(bookingGeneratedId)).isEqualTo(AdminService.UuidTableType.BOOKING); | ||
| assertThat(adminService.findUuidType(caseGeneratedId)).isEqualTo(AdminService.UuidTableType.CASE); | ||
| assertThat(adminService.findUuidType(courtGeneratedId)).isEqualTo(AdminService.UuidTableType.COURT); | ||
| assertThat(adminService.findUuidType(captureSessionGeneratedId)) | ||
| .isEqualTo(AdminService.UuidTableType.CAPTURE_SESSION); | ||
| assertThat(adminService.findUuidType(recordingGeneratedId)).isEqualTo(AdminService.UuidTableType.RECORDING); | ||
|
|
||
| } | ||
|
|
||
| @Test | ||
| @Transactional | ||
| public void shouldThrowExceptionWhenUuidNotFound() { | ||
|
|
||
| UUID randomUuid = UUID.randomUUID(); | ||
|
|
||
| assertThatThrownBy(() -> adminService.findUuidType(randomUuid)) | ||
| .isInstanceOf(NotFoundException.class); | ||
|
|
||
| } | ||
|
|
||
| @Test | ||
| @Transactional | ||
| public void shouldThrowExceptionWhenUuidIsNotInRelevantTable() { | ||
|
|
||
| Role role = HelperFactory.createRole("role"); | ||
| entityManager.persist(role); | ||
|
|
||
| entityManager.flush(); | ||
|
|
||
| UUID roleGeneratedId = role.getId(); | ||
|
|
||
| assertThatThrownBy(() -> adminService.findUuidType(roleGeneratedId)) | ||
| .isInstanceOf(NotFoundException.class); | ||
|
|
||
| } | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| package uk.gov.hmcts.reform.preapi.controllers.admin; | ||
|
|
||
| import io.swagger.v3.oas.annotations.Operation; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.access.prepost.PreAuthorize; | ||
| import org.springframework.web.bind.annotation.GetMapping; | ||
| import org.springframework.web.bind.annotation.PathVariable; | ||
| import org.springframework.web.bind.annotation.RequestMapping; | ||
| import org.springframework.web.bind.annotation.RestController; | ||
| import uk.gov.hmcts.reform.preapi.services.admin.AdminService; | ||
|
|
||
| import java.util.UUID; | ||
|
|
||
| /** | ||
| * Helper endpoints intended for developers to use in troubleshooting/investigating | ||
| * incidents. | ||
| * Your user will need to have the role ROLE_SUPER_USER to access these endpoints. | ||
| */ | ||
| @RestController | ||
| @RequestMapping("/admin") | ||
| @PreAuthorize("hasRole('ROLE_SUPER_USER')") | ||
| public class AdminController { | ||
|
|
||
| private final AdminService adminService; | ||
|
|
||
| @Autowired | ||
| public AdminController(AdminService adminService) { | ||
| this.adminService = adminService; | ||
| } | ||
|
|
||
| /** | ||
| * Endpoint for getting back what type of item a UUID relates to | ||
| * @param id UUID to search for | ||
| * @return returns a string | ||
| */ | ||
| @GetMapping("/{id}") | ||
| @Operation(operationId = "checkUuid", summary = "Check if a UUID exists in the system", | ||
| description = "Checks if a UUID exists in any of the tables: User, Recording, CaptureSession, " | ||
| + "Booking, Case, Court.") | ||
| public ResponseEntity<String> checkUuidExists(@PathVariable UUID id) { | ||
| return ResponseEntity.ok("Uuid relates to a " + adminService.findUuidType(id)); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We send back a pure string don't know if you want us to change to a valid json? |
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| package uk.gov.hmcts.reform.preapi.repositories.admin; | ||
|
|
||
| import org.springframework.jdbc.core.JdbcTemplate; | ||
| import org.springframework.stereotype.Repository; | ||
|
|
||
| import java.util.Optional; | ||
| import java.util.UUID; | ||
|
|
||
| @Repository | ||
| public class AdminRepository { | ||
|
|
||
| private final JdbcTemplate jdbcTemplate; | ||
|
|
||
| public AdminRepository(JdbcTemplate jdbcTemplate) { | ||
| this.jdbcTemplate = jdbcTemplate; | ||
| } | ||
|
|
||
| /** | ||
| * Searches tables to find what the given UUID is an ID for. | ||
| * Takes the given UUID id and passes it to JDBC Template to replace the placeholders ('?') in the query. | ||
| * The query uses SQL union all to select items across multiple tables (User, Recording, CaptureSession, Booking, | ||
| * Case, Court) that could contain the UUID. If found in that table, it returns a string | ||
| * e.g. if found in the users table, it will return "user". It will return the first match it finds. | ||
| * @param id the UUID to check for | ||
| * @return if found, an Optional containing the type of item the UUID relates to, otherwise empty Optional | ||
| */ | ||
| public Optional<String> findUuidType(UUID id) { | ||
| String query = """ | ||
| (SELECT 'user' AS table_name FROM users WHERE id = ? | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We might need to optimise this if it gets slow, but we can do that at a later date |
||
| UNION ALL | ||
| SELECT 'recording' FROM recordings WHERE id = ? | ||
| UNION ALL | ||
| SELECT 'capture_session' FROM capture_sessions WHERE id = ? | ||
| UNION ALL | ||
| SELECT 'booking' FROM bookings WHERE id = ? | ||
| UNION ALL | ||
| SELECT 'case' FROM cases WHERE id = ? | ||
| UNION ALL | ||
| SELECT 'court' FROM courts WHERE id = ?) | ||
| LIMIT 1 | ||
| """; | ||
|
|
||
| try { | ||
| String type = jdbcTemplate.queryForObject(query, String.class, id, id, id, id, id, id); | ||
| return Optional.ofNullable(type); | ||
| } catch (org.springframework.dao.EmptyResultDataAccessException e) { | ||
| return Optional.empty(); | ||
| } | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| package uk.gov.hmcts.reform.preapi.services.admin; | ||
|
|
||
| import lombok.Getter; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.stereotype.Service; | ||
| import uk.gov.hmcts.reform.preapi.exception.NotFoundException; | ||
| import uk.gov.hmcts.reform.preapi.repositories.admin.AdminRepository; | ||
|
|
||
| import java.util.Objects; | ||
| import java.util.UUID; | ||
|
|
||
| @Service | ||
| public class AdminService { | ||
|
|
||
| private final AdminRepository adminRepository; | ||
|
|
||
| @Autowired | ||
| public AdminService(AdminRepository adminRepository) { | ||
| this.adminRepository = adminRepository; | ||
| } | ||
|
|
||
| /** | ||
| * Checks if a UUID exists in relevant tables. | ||
| * Uses the AdminRepository to search the database for the UUID. The String returned from the repository | ||
| * is then converted to an enum value. | ||
| * @param id the UUID to check the tables for | ||
| * @return a string indicating the type of table related to the UUID | ||
| * @throws NotFoundException if the UUID does not exist in the tables being checked | ||
| */ | ||
| public UuidTableType findUuidType(UUID id) { | ||
|
|
||
| String tableName = adminRepository.findUuidType(Objects.requireNonNull(id, "UUID should not be null")) | ||
| .orElseThrow(() -> new NotFoundException(id + " does not exist in any relevant table")); | ||
|
|
||
| return UuidTableType.valueOf(tableName.toUpperCase()); | ||
| } | ||
|
|
||
| /** | ||
| * Enum representing the tables the UUID could belong to. Restricts to relevant tables only. | ||
| */ | ||
| @Getter | ||
| public enum UuidTableType { | ||
| USER, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. app_access and portal_access would be useful too, as these are the X-User-Ids that sometimes pop up in e.g. the audit table |
||
| RECORDING, | ||
| CAPTURE_SESSION, | ||
| BOOKING, | ||
| CASE, | ||
| COURT | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
beware: user ID and X-User-Id are not quite the same. User ID = ID from users table. X-User-ID = ID from app_access or portal_access table. Or at least should be in theory, they do get a little bit interchanged.