diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/RecordingRepository.java b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/RecordingRepository.java index e4b05c4f4..72d99f76b 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/repositories/RecordingRepository.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/repositories/RecordingRepository.java @@ -113,6 +113,8 @@ Page searchAllBy( List findAllByParentRecordingIsNull(); + boolean existsByParentRecordingIdIsAndReencodeIs(UUID id, boolean reencode); + boolean existsByCaptureSessionAndDeletedAtIsNull(CaptureSession captureSession); Optional findFirstByCaptureSessionAndDeletedAtIsNull(CaptureSession captureSession); diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/security/AuthorisationService.java b/src/main/java/uk/gov/hmcts/reform/preapi/security/AuthorisationService.java index 8edcd9abb..5bd353ac4 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/security/AuthorisationService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/security/AuthorisationService.java @@ -1,6 +1,7 @@ package uk.gov.hmcts.reform.preapi.security; import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import uk.gov.hmcts.reform.preapi.dto.CreateBookingDTO; import uk.gov.hmcts.reform.preapi.dto.CreateCaptureSessionDTO; @@ -30,7 +31,7 @@ import java.util.UUID; @Service -@SuppressWarnings("PMD.CouplingBetweenObjects") +@SuppressWarnings({"PMD.CouplingBetweenObjects", "PMD.GodClass", "PMD.TooManyMethods"}) public class AuthorisationService { private final BookingRepository bookingRepository; private final CaseRepository caseRepository; @@ -120,10 +121,10 @@ public boolean hasRecordingAccess(UserAuthentication authentication, UUID record } if (enableMigratedData && authentication.isAdmin()) { - return canViewReencodedRecording(authentication, entity); + return canViewRecording(authentication, entity); } - return canViewReencodedRecording(authentication, entity) + return canViewRecording(authentication, entity) && hasCaptureSessionAccess(authentication, entity.getCaptureSession().getId()); } @@ -214,10 +215,23 @@ public boolean canViewVodafoneData(UserAuthentication authentication) { return enableMigratedData || authentication.hasRole(ROLE_SUPER_USER); } - public boolean canViewReencodedRecording(UserAuthentication authentication, Recording recording) { - return !hideReencodedRecordings - || !recording.isReencode() - || authentication.hasRole(ROLE_SUPER_USER); + public boolean canViewRecording(UserAuthentication authentication, Recording recording) { + if (authentication != null && authentication.hasRole(ROLE_SUPER_USER) + || recording.getCaptureSession().getOrigin() != RecordingOrigin.VODAFONE) { + return true; + } + + if (!hideReencodedRecordings && !recording.isReencode()) { + // Show original recording if no re-encoded version exists + return !recordingRepository.existsByParentRecordingIdIsAndReencodeIs(recording.getId(), true); + } + + return hideReencodedRecordings ^ recording.isReencode(); + } + + public boolean canViewReencodedRecordings() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); + return !hideReencodedRecordings || auth != null && auth.hasRole(ROLE_SUPER_USER); } public boolean isVodafoneData(Case caseEntity) { diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/EditRequestService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/EditRequestService.java index 62e5f1a85..2d406a93c 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/EditRequestService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/EditRequestService.java @@ -5,7 +5,6 @@ import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.util.Pair; @@ -28,6 +27,7 @@ import uk.gov.hmcts.reform.preapi.exception.ResourceInWrongStateException; import uk.gov.hmcts.reform.preapi.exception.UnknownServerException; import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import uk.gov.hmcts.reform.preapi.services.edit.EditRequestCrudService; import uk.gov.hmcts.reform.preapi.utils.InputSanitizerUtils; @@ -43,40 +43,37 @@ @Slf4j @Service +@SuppressWarnings("PMD.CouplingBetweenObjects") public class EditRequestService { private final EditRequestCrudService editRequestCrudService; private final RecordingRepository recordingRepository; private final RecordingService recordingService; private final EditNotificationService editNotificationService; - private final boolean hideReencodedRecordings; - - private static final String ROLE_SUPER_USER = "ROLE_SUPER_USER"; + private final AuthorisationService authorisationService; @Autowired public EditRequestService(final EditRequestCrudService editRequestCrudService, final RecordingRepository recordingRepository, final RecordingService recordingService, final EditNotificationService editNotificationService, - @Value("${feature-flags.hide-reencoded-recordings:true}") - final boolean hideReencodedRecordings) { + final AuthorisationService authorisationService) { this.editRequestCrudService = editRequestCrudService; this.recordingRepository = recordingRepository; this.recordingService = recordingService; this.editNotificationService = editNotificationService; - this.hideReencodedRecordings = hideReencodedRecordings; + this.authorisationService = authorisationService; } @Transactional @PreAuthorize("@authorisationService.hasEditRequestAccess(authentication, #id)") public EditRequestDTO findById(UUID id) { - boolean includeReencodedRecordings = canViewReencodedRecordings(); - return editRequestCrudService.findById(id, includeReencodedRecordings); + return editRequestCrudService.findById(id); } @Transactional public Page findAll(@NotNull SearchEditRequests params, Pageable pageable) { UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); - boolean includeReencodedRecordings = canViewReencodedRecordings(auth); + boolean includeReencodedRecordings = authorisationService.canViewReencodedRecordings(); params.setAuthorisedBookings(auth.isAdmin() || auth.isAppUser() ? null : auth.getSharedBookings()); params.setAuthorisedCourt(auth.isPortalUser() || auth.isAdmin() ? null : auth.getCourtId()); @@ -148,7 +145,7 @@ public EditRequestDTO upsert(UUID sourceRecordingId, MultipartFile file) { upsert(dto); - return editRequestCrudService.findById(id, canViewReencodedRecordings()); + return editRequestCrudService.findById(id); } private Recording getSourceRecording(UUID sourceRecordingId) { @@ -179,15 +176,6 @@ private void notifyOnUpdatedRequest(CreateEditRequestDTO dto, Pair parseCsv(MultipartFile file) { try { @Cleanup BufferedReader reader = new BufferedReader(new InputStreamReader( diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java index df6abbee4..ccd3c95f4 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/LegacyReportService.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.reform.preapi.services; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -29,6 +28,7 @@ import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; import uk.gov.hmcts.reform.preapi.repositories.UserRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import java.util.Comparator; @@ -47,9 +47,7 @@ public class LegacyReportService { private final UserRepository userRepository; private final AppAccessRepository appAccessRepository; private final PortalAccessRepository portalAccessRepository; - private final boolean hideReencodedRecordings; - - private static final String ROLE_SUPER_USER = "ROLE_SUPER_USER"; + private final AuthorisationService authorisationService; @Autowired public LegacyReportService(CaptureSessionRepository captureSessionRepository, @@ -59,8 +57,7 @@ public LegacyReportService(CaptureSessionRepository captureSessionRepository, UserRepository userRepository, AppAccessRepository appAccessRepository, PortalAccessRepository portalAccessRepository, - @Value("${feature-flags.hide-reencoded-recordings:true}") - boolean hideReencodedRecordings) { + AuthorisationService authorisationService) { this.captureSessionRepository = captureSessionRepository; this.recordingRepository = recordingRepository; this.shareBookingRepository = shareBookingRepository; @@ -68,7 +65,7 @@ public LegacyReportService(CaptureSessionRepository captureSessionRepository, this.userRepository = userRepository; this.appAccessRepository = appAccessRepository; this.portalAccessRepository = portalAccessRepository; - this.hideReencodedRecordings = hideReencodedRecordings; + this.authorisationService = authorisationService; } @Transactional @@ -82,8 +79,9 @@ public List reportCaptureSessions() { @Transactional public List reportRecordingsPerCase() { + boolean includeReencodedRecordings = authorisationService.canViewReencodedRecordings(); return recordingRepository - .countRecordingsPerCase(canViewReencodedRecordings()) + .countRecordingsPerCase(includeReencodedRecordings) .stream() .map(data -> new RecordingsPerCaseReportDTO((Case) data[0], ((Long) data[1]).intValue())) .toList(); @@ -91,10 +89,11 @@ public List reportRecordingsPerCase() { @Transactional public List reportEdits() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); return recordingRepository .findAllByParentRecordingIsNotNull() .stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .sorted(Comparator.comparing(Recording::getCreatedAt)) .map(EditReportDTO::new) .collect(Collectors.toList()); @@ -156,10 +155,11 @@ public List reportPlayback(AuditLogSource source) { @Transactional public List reportCompletedCaptureSessions() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); return recordingRepository .findAllByParentRecordingIsNull() .stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .sorted(Comparator.comparing(r -> r.getCaptureSession().getBooking().getScheduledFor())) .map(CompletedCaptureSessionReportDTO::new) .collect(Collectors.toList()); @@ -187,6 +187,8 @@ private PlaybackReportDTO toPlaybackReport(Audit audit) { } } + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); + return new PlaybackReportDTO( audit, audit.getCreatedBy() != null @@ -199,24 +201,11 @@ private PlaybackReportDTO toPlaybackReport(Audit audit) { .orElse(null))) : null, recordingId != null - ? recordingRepository.findById(recordingId).filter(this::canViewRecording).orElse(null) + ? recordingRepository.findById(recordingId) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) + .orElse(null) : null ); } - private boolean canViewRecording(Recording recording) { - return recording == null - || !hideReencodedRecordings - || !recording.isReencode() - || getAuthentication() != null && getAuthentication().hasRole(ROLE_SUPER_USER); - } - - private boolean canViewReencodedRecordings() { - UserAuthentication auth = getAuthentication(); - return !hideReencodedRecordings || auth != null && auth.hasRole(ROLE_SUPER_USER); - } - - private UserAuthentication getAuthentication() { - return SecurityContextHolder.getContext().getAuthentication() instanceof UserAuthentication auth ? auth : null; - } } diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/RecordingService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/RecordingService.java index c40cc6316..04a00b631 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/RecordingService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/RecordingService.java @@ -28,6 +28,7 @@ import uk.gov.hmcts.reform.preapi.media.storage.AzureFinalStorageService; import uk.gov.hmcts.reform.preapi.repositories.CaptureSessionRepository; import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import java.sql.Timestamp; @@ -51,15 +52,13 @@ public class RecordingService { private final CaptureSessionRepository captureSessionRepository; private final CaptureSessionService captureSessionService; private final AzureFinalStorageService azureFinalStorageService; + private final AuthorisationService authorisationService; @Setter private boolean enableMigratedData; private final boolean rtmpsSuffixEnabled; - @Setter - private boolean hideReencodedRecordings; - @Autowired public RecordingService(RecordingRepository recordingRepository, CaptureSessionRepository captureSessionRepository, @@ -68,21 +67,20 @@ public RecordingService(RecordingRepository recordingRepository, @Value("${migration.enableMigratedData:false}") boolean enableMigratedData, @Value("${mediakind.rtmpsSuffixEnabled:false}") boolean rtmpsSuffixEnabled, - @Value("${feature-flags.hide-reencoded-recordings:true}") - boolean hideReencodedRecordings) { + AuthorisationService authorisationService) { this.recordingRepository = recordingRepository; this.captureSessionRepository = captureSessionRepository; this.captureSessionService = captureSessionService; this.azureFinalStorageService = azureFinalStorageService; this.enableMigratedData = enableMigratedData; this.rtmpsSuffixEnabled = rtmpsSuffixEnabled; - this.hideReencodedRecordings = hideReencodedRecordings; + this.authorisationService = authorisationService; } @Transactional @PreAuthorize("@authorisationService.hasRecordingAccess(authentication, #recordingId)") public RecordingDTO findById(UUID recordingId) { - boolean includeReencodedRecordings = canViewReencodedRecordings(); + boolean includeReencodedRecordings = authorisationService.canViewReencodedRecordings(); return recordingRepository.findByIdAndDeletedAtIsNull(recordingId, includeReencodedRecordings) .map(recording -> new RecordingDTO(recording, includeReencodedRecordings, rtmpsSuffixEnabled)) .orElseThrow(() -> new NotFoundException("RecordingDTO: " + recordingId)); @@ -114,7 +112,7 @@ public Page findAll( ); UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); - boolean includeReencodedRecordings = canViewReencodedRecordings(auth); + boolean includeReencodedRecordings = authorisationService.canViewReencodedRecordings(); params.setAuthorisedBookings( auth.isAdmin() || auth.isAppUser() ? null : auth.getSharedBookings() ); @@ -199,15 +197,6 @@ private boolean isReencodedRecording(String editInstructions) { } } - private boolean canViewReencodedRecordings() { - UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); - return canViewReencodedRecordings(auth); - } - - private boolean canViewReencodedRecordings(UserAuthentication auth) { - return !hideReencodedRecordings || auth != null && auth.hasRole(ROLE_SUPER_USER); - } - @Transactional public UpsertResult forceUpsert(CreateRecordingDTO createRecordingDTO) { // ignores deleted_at and case state diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java index f88b53490..9f64ee5b9 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/ReportService.java @@ -1,7 +1,6 @@ package uk.gov.hmcts.reform.preapi.services; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -35,12 +34,14 @@ import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; import uk.gov.hmcts.reform.preapi.repositories.UserRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -56,9 +57,7 @@ public class ReportService { private final UserRepository userRepository; private final AppAccessRepository appAccessRepository; private final PortalAccessRepository portalAccessRepository; - private final boolean hideReencodedRecordings; - - private static final String ROLE_SUPER_USER = "ROLE_SUPER_USER"; + private final AuthorisationService authorisationService; @Autowired public ReportService(CaptureSessionRepository captureSessionRepository, @@ -68,8 +67,7 @@ public ReportService(CaptureSessionRepository captureSessionRepository, UserRepository userRepository, AppAccessRepository appAccessRepository, PortalAccessRepository portalAccessRepository, - @Value("${feature-flags.hide-reencoded-recordings:true}") - boolean hideReencodedRecordings) { + AuthorisationService authorisationService) { this.captureSessionRepository = captureSessionRepository; this.recordingRepository = recordingRepository; this.shareBookingRepository = shareBookingRepository; @@ -77,7 +75,7 @@ public ReportService(CaptureSessionRepository captureSessionRepository, this.userRepository = userRepository; this.appAccessRepository = appAccessRepository; this.portalAccessRepository = portalAccessRepository; - this.hideReencodedRecordings = hideReencodedRecordings; + this.authorisationService = authorisationService; } @Transactional @@ -91,8 +89,9 @@ public List reportCaptureSessions() { @Transactional public List reportRecordingsPerCase() { + boolean includeReencodedRecordings = authorisationService.canViewReencodedRecordings(); return recordingRepository - .countRecordingsPerCase(canViewReencodedRecordings()) + .countRecordingsPerCase(includeReencodedRecordings) .stream() .map(data -> new RecordingsPerCaseReportDTOV2((Case) data[0], ((Long) data[1]).intValue())) .toList(); @@ -100,10 +99,11 @@ public List reportRecordingsPerCase() { @Transactional public List reportEdits() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); return recordingRepository .findAllByParentRecordingIsNotNull() .stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .sorted(Comparator.comparing(Recording::getCreatedAt)) .map(EditReportDTOV2::new) .collect(Collectors.toList()); @@ -149,6 +149,7 @@ public List userRecordingPlaybackReport() { @Transactional public List reportPlayback(AuditLogSource source) { if (source == AuditLogSource.PORTAL || source == AuditLogSource.APPLICATION) { + final UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); final String activityPlay = "Play"; final String functionalAreaVideoPlayer = "Video Player"; final String functionalAreaViewRecordings = "View Recordings"; @@ -209,7 +210,7 @@ public List reportPlayback(AuditLogSource source) { final List recordings = recordingRepository.findAllById(recordingIds); final Map recordingMap = recordings.stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .collect(Collectors.toMap(Recording::getId, r -> r)); return audits.stream() @@ -233,10 +234,11 @@ public List reportPlayback(AuditLogSource source) { @Transactional public List reportCompletedCaptureSessions() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); return recordingRepository .findAllCompletedCaptureSessionsWithRecordings() .stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .sorted(Comparator.comparing(r -> r.getCaptureSession().getBooking().getScheduledFor())) .map(CompletedCaptureSessionReportDTOV2::new) .collect(Collectors.toList()); @@ -254,10 +256,11 @@ public List reportAccessRemoved() { @Transactional public List reportRecordingParticipants() { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); return recordingRepository .findAllByParentRecordingIsNull() .stream() - .filter(this::canViewRecording) + .filter(rec -> authorisationService.canViewRecording(auth, rec)) .map(this::getParticipantsForRecording) .flatMap(List::stream) .toList(); @@ -285,11 +288,18 @@ private PlaybackReportArgsRecord toPlaybackReport(Audit audit) { .orElse(null))) : null; - Recording recording = recordingId != null - ? recordingRepository.findById(recordingId).orElse(null) - : null; + if (recordingId != null) { + Optional recording = recordingRepository.findById(recordingId); + if (recording.isPresent()) { + UserAuthentication auth = (UserAuthentication) SecurityContextHolder.getContext().getAuthentication(); + boolean canView = authorisationService.canViewRecording(auth, recording.get()); + if (canView) { + return new PlaybackReportArgsRecord(audit, user, recording.get()); + } + } + } - return new PlaybackReportArgsRecord(audit, user, canViewRecording(recording) ? recording : null); + return new PlaybackReportArgsRecord(audit, user, null); } private PlaybackReportArgsRecord toPlaybackReport( @@ -335,22 +345,6 @@ private UUID getRecordingIDForAudit(Audit audit) { return null; } - private boolean canViewRecording(Recording recording) { - return recording == null - || !hideReencodedRecordings - || !recording.isReencode() - || getAuthentication() != null && getAuthentication().hasRole(ROLE_SUPER_USER); - } - - private boolean canViewReencodedRecordings() { - UserAuthentication auth = getAuthentication(); - return !hideReencodedRecordings || auth != null && auth.hasRole(ROLE_SUPER_USER); - } - - private UserAuthentication getAuthentication() { - return SecurityContextHolder.getContext().getAuthentication() instanceof UserAuthentication auth ? auth : null; - } - private User getCreatedByUserForAudit( Audit audit, Set userIdSet, diff --git a/src/main/java/uk/gov/hmcts/reform/preapi/services/edit/EditRequestCrudService.java b/src/main/java/uk/gov/hmcts/reform/preapi/services/edit/EditRequestCrudService.java index d2cf70e87..9e4f5c2dc 100644 --- a/src/main/java/uk/gov/hmcts/reform/preapi/services/edit/EditRequestCrudService.java +++ b/src/main/java/uk/gov/hmcts/reform/preapi/services/edit/EditRequestCrudService.java @@ -46,13 +46,9 @@ public Optional findByIdIfExists(UUID id) { } public EditRequestDTO findById(UUID id) { - return findById(id, true); - } - - public EditRequestDTO findById(UUID id, boolean includeReencodedRecordings) { return editRequestRepository .findByIdNotLocked(id) - .map(editRequest -> new EditRequestDTO(editRequest, true, includeReencodedRecordings)) + .map(editRequest -> new EditRequestDTO(editRequest, true, true)) .orElseThrow(() -> new NotFoundException("Edit Request: " + id)); } @@ -142,7 +138,7 @@ public Set findRecordingIdsWithForceReencodeRequests(Set sourceRecor return editRequestRepository.findSourceRecordingIdsWithForceReencodeRequests(sourceRecordingIds); } - + private UpsertResult handleEmptyInstructions(CreateEditRequestDTO dto, Optional existingEditRequest, boolean isUpdate) { diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/security/AuthorisationServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/security/AuthorisationServiceTest.java index b5e39af93..a0ba899c2 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/security/AuthorisationServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/security/AuthorisationServiceTest.java @@ -3,10 +3,15 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; import uk.gov.hmcts.reform.preapi.dto.CreateBookingDTO; import uk.gov.hmcts.reform.preapi.dto.CreateCaptureSessionDTO; import uk.gov.hmcts.reform.preapi.dto.CreateCaseDTO; @@ -35,6 +40,7 @@ import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -385,6 +391,7 @@ void hasRecordingAccessCaptureSessionAccessGranted() { var recording = new Recording(); recording.setId(recordingId); var captureSession = new CaptureSession(); + captureSession.setOrigin(RecordingOrigin.PRE); recording.setCaptureSession(captureSession); when(authenticationUser.isAdmin()).thenReturn(false); @@ -393,38 +400,133 @@ void hasRecordingAccessCaptureSessionAccessGranted() { assertTrue(authorisationService.hasRecordingAccess(authenticationUser, recordingId)); } - @DisplayName("Should not grant access to hidden re-encoded recording when user is not super user") - @Test - void hasRecordingAccessHiddenReencodedRecordingNotSuperUser() { - var recordingId = UUID.randomUUID(); - var recording = new Recording(); + @ParameterizedTest + @MethodSource("provideArgsForTestingRecording") + @DisplayName("Should (not) grant access to hidden re-encoded recording depending on user role and config flag") + void hasRecordingAccessHiddenReencodedRecordingNotSuperUser( + boolean shouldBeVisible, + RecordingOrigin recordingOrigin, + TestingSupportRoles userRole, + boolean recordingWasReEncoded, + boolean hideReencodedRecordingsFlag) { + UUID recordingId = UUID.randomUUID(); + Recording recording = new Recording(); recording.setId(recordingId); - recording.setReencode(true); + recording.setReencode(recordingWasReEncoded); - when(authenticationUser.isAdmin()).thenReturn(false); - when(authenticationUser.hasRole("ROLE_SUPER_USER")).thenReturn(false); + CaptureSession captureSession = new CaptureSession(); + captureSession.setOrigin(recordingOrigin); + recording.setCaptureSession(captureSession); + + boolean userIsSuperUser = userRole.equals(TestingSupportRoles.SUPER_USER); + when(authenticationUser.isAdmin()).thenReturn(userIsSuperUser); + when(authenticationUser.hasRole("ROLE_SUPER_USER")).thenReturn(userIsSuperUser); when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recording)); - assertFalse(authorisationService.hasRecordingAccess(authenticationUser, recordingId)); + AuthorisationService authorisationServiceHideReencodedRecordings = createAuthServiceWithHiddenFlag( + hideReencodedRecordingsFlag); + + if (shouldBeVisible) { + assertTrue(authorisationServiceHideReencodedRecordings.hasRecordingAccess(authenticationUser, recordingId)); + } else { + assertFalse(authorisationServiceHideReencodedRecordings.hasRecordingAccess( + authenticationUser, + recordingId + )); + } } - @DisplayName("Should grant access to hidden re-encoded recording when user is super user") - @Test - void hasRecordingAccessHiddenReencodedRecordingSuperUser() { - var recordingId = UUID.randomUUID(); - var recording = new Recording(); - recording.setId(recordingId); - recording.setReencode(true); - var captureSession = new CaptureSession(); - recording.setCaptureSession(captureSession); + private static Stream provideArgsForTestingRecording() { + // Arg 1: boolean shouldBeVisible + // Arg 4: boolean recordingWasReEncoded + // Arg 5: boolean reencodedHidden - when(authenticationUser.isAdmin()).thenReturn(false); - when(authenticationUser.hasRole("ROLE_SUPER_USER")).thenReturn(true); - when(recordingRepository.findById(recordingId)).thenReturn(Optional.of(recording)); + return Stream.of( + // PRE origin, any user, any recording: should be visible because PRE + Arguments.of(true, RecordingOrigin.PRE, TestingSupportRoles.LEVEL_1, false, false), - assertTrue(authorisationService.hasRecordingAccess(authenticationUser, recordingId)); + // PRE origin, normal user, re-encoded recording, hidden flag --> visible for PRE, or invisible for VF + Arguments.of(true, RecordingOrigin.PRE, TestingSupportRoles.LEVEL_1, true, true), + Arguments.of(false, RecordingOrigin.VODAFONE, TestingSupportRoles.LEVEL_1, true, true), + + // Vodafone, re-encoded recording, hidden flag is true --> visible for superuser only + Arguments.of(true, RecordingOrigin.VODAFONE, TestingSupportRoles.SUPER_USER, true, true), + + // Vodafone, *original* recording, hidden flag is true --> visible for Level 1 user + Arguments.of(true, RecordingOrigin.VODAFONE, TestingSupportRoles.LEVEL_1, false, true), + + // Vodafone, re-encoded recording, hidden flag is false --> visible for Level 1 user + Arguments.of(true, RecordingOrigin.VODAFONE, TestingSupportRoles.LEVEL_1, true, false) + + // For original VF recording when hidden flag is false --> see separate test below + ); } + @DisplayName("When hidden flag is false, original recordings should be visible " + + "when a re-encoded alternative does not exist") + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void originalRecordingIsVisibleWhenAReencodedRecordingDoesNotExist(boolean hideReencodedRecordingsFlag) { + CaptureSession captureSession = new CaptureSession(); + captureSession.setOrigin(RecordingOrigin.VODAFONE); + + UUID originalRecordingId = UUID.randomUUID(); + Recording originalRecording = new Recording(); + originalRecording.setId(originalRecordingId); + originalRecording.setReencode(false); + originalRecording.setCaptureSession(captureSession); + + UUID reencodedRecordingId = UUID.randomUUID(); + Recording reencodedRecording = new Recording(); + reencodedRecording.setId(reencodedRecordingId); + reencodedRecording.setReencode(true); + reencodedRecording.setCaptureSession(captureSession); + reencodedRecording.setParentRecording(originalRecording); + + CaptureSession secondCaptureSession = new CaptureSession(); + secondCaptureSession.setOrigin(RecordingOrigin.VODAFONE); + + UUID anotherOriginalRecordingId = UUID.randomUUID(); + Recording anotherOriginalRec = new Recording(); + anotherOriginalRec.setId(anotherOriginalRecordingId); + anotherOriginalRec.setReencode(false); + anotherOriginalRec.setCaptureSession(secondCaptureSession); + + when(authenticationUser.isAdmin()).thenReturn(false); + when(authenticationUser.hasRole("ROLE_SUPER_USER")).thenReturn(false); + when(recordingRepository.findById(originalRecordingId)).thenReturn(Optional.of(originalRecording)); + when(recordingRepository.findById(reencodedRecordingId)).thenReturn(Optional.of(reencodedRecording)); + when(recordingRepository.findById(anotherOriginalRecordingId)).thenReturn(Optional.of(anotherOriginalRec)); + + when(recordingRepository.existsByParentRecordingIdIsAndReencodeIs(originalRecordingId, true)) + .thenReturn(true); + when(recordingRepository.existsByParentRecordingIdIsAndReencodeIs(reencodedRecordingId, true)) + .thenReturn(false); + when(recordingRepository.existsByParentRecordingIdIsAndReencodeIs(anotherOriginalRecordingId, true)) + .thenReturn(false); + + AuthorisationService authServiceWithFlag = createAuthServiceWithHiddenFlag(false); + + if (hideReencodedRecordingsFlag) { + // show originals only + assertTrue(authServiceWithFlag + .hasRecordingAccess(authenticationUser, originalRecordingId)); + assertFalse(authServiceWithFlag + .hasRecordingAccess(authenticationUser, reencodedRecordingId)); + assertTrue(authServiceWithFlag + .hasRecordingAccess(authenticationUser, anotherOriginalRecordingId)); + } else { + // show re-encoded, and originals where re-encoded does not exist + assertFalse(authServiceWithFlag + .hasRecordingAccess(authenticationUser, originalRecordingId)); + assertTrue(authServiceWithFlag + .hasRecordingAccess(authenticationUser, reencodedRecordingId)); + assertTrue(authServiceWithFlag + .hasRecordingAccess(authenticationUser, anotherOriginalRecordingId)); + } + } + + @DisplayName("Should not grant access to recording when capture session access is not granted") @Test void hasRecordingAccessCaptureSessionAccessNotGranted() { @@ -1080,4 +1182,18 @@ private AuthorisationService createAuthorisationServiceWithToggle(boolean enable true ); } + + private AuthorisationService createAuthServiceWithHiddenFlag(boolean hideReencodedRecordingsFlag) { + return new AuthorisationService( + bookingRepository, + caseRepository, + participantRepository, + captureSessionRepository, + recordingRepository, + editRequestRepository, + true, + hideReencodedRecordingsFlag + ); + } + } diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/EditRequestServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/EditRequestServiceTest.java index 216ebe4e7..808869557 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/EditRequestServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/EditRequestServiceTest.java @@ -32,6 +32,7 @@ import uk.gov.hmcts.reform.preapi.exception.NotFoundException; import uk.gov.hmcts.reform.preapi.exception.ResourceInWrongStateException; import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import uk.gov.hmcts.reform.preapi.services.edit.EditRequestCrudService; import uk.gov.hmcts.reform.preapi.util.HelperFactory; @@ -70,6 +71,9 @@ class EditRequestServiceTest { @MockitoBean private EditNotificationService editNotificationService; + @MockitoBean + private AuthorisationService authorisationService; + @MockitoBean private Recording mockRecording; @@ -176,7 +180,7 @@ void setup() throws InterruptedException { when(editRequestCrudService.upsert(dto, mockRecording, courtClerkUser)) .thenReturn(Pair.of(UpsertResult.CREATED, mockEditRequest)); - when(editRequestCrudService.findById(mockEditRequestId, false)).thenReturn(editRequestDTO); + when(editRequestCrudService.findById(mockEditRequestId)).thenReturn(editRequestDTO); when(editRequestDTO.getId()).thenReturn(mockEditRequestId); when(editRequestDTO.getStatus()).thenReturn(EditRequestStatus.PENDING); when(editRequestDTO.getCreatedBy()).thenReturn(courtClerkUser.getId().toString()); @@ -341,7 +345,7 @@ void upsertEditInstructionsWithCSVFile() { when(editRequestCrudService.upsert(any(), any(), any())) .thenReturn(Pair.of(UpsertResult.CREATED, mockEditRequest)); when(editRequestCrudService.findByIdIfExists(any())).thenReturn(Optional.of(returnedByDb)); - when(editRequestCrudService.findById(any(UUID.class), anyBoolean())).thenReturn(editRequestDTO); + when(editRequestCrudService.findById(any(UUID.class))).thenReturn(editRequestDTO); EditRequestDTO upsert = underTest.upsert(mockRecordingId, file); assertThat(upsert).isEqualTo(editRequestDTO); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java index 17b8ef6e6..f4cba6cfd 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/LegacyReportServiceTest.java @@ -9,6 +9,7 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.test.context.bean.override.mockito.MockitoBean; +import uk.gov.hmcts.reform.preapi.controllers.params.TestingSupportRoles; import uk.gov.hmcts.reform.preapi.entities.AppAccess; import uk.gov.hmcts.reform.preapi.entities.Audit; import uk.gov.hmcts.reform.preapi.entities.Booking; @@ -32,6 +33,7 @@ import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; import uk.gov.hmcts.reform.preapi.repositories.UserRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import java.sql.Timestamp; @@ -60,6 +62,7 @@ public class LegacyReportServiceTest { private static Case caseEntity; private static Booking bookingEntity; private static Audit auditEntity; + private static UserAuthentication mockAuth; @MockitoBean private CaptureSessionRepository captureSessionRepository; @@ -82,6 +85,9 @@ public class LegacyReportServiceTest { @MockitoBean private PortalAccessRepository portalAccessRepository; + @MockitoBean + private AuthorisationService authorisationService; + @Autowired private LegacyReportService reportService; @@ -138,6 +144,12 @@ void reset() { auditEntity.setCreatedBy(null); auditEntity.setAuditDetails(null); bookingEntity.setParticipants(Set.of()); + + mockAuth = mock(UserAuthentication.class); + when(mockAuth.hasRole(TestingSupportRoles.LEVEL_1.name())).thenReturn(true); + SecurityContextHolder.getContext().setAuthentication(mockAuth); + + when(authorisationService.canViewRecording(mockAuth, recordingEntity)).thenReturn(true); } @DisplayName("Find all capture sessions and return a list of models as a report when capture session is incomplete") @@ -231,6 +243,7 @@ void reportEditsSuccess() { recording2.setCreatedAt(Timestamp.from(Instant.MIN)); recording2.setCaptureSession(captureSessionEntity); + when(authorisationService.canViewRecording(any(), any())).thenReturn(true); when(recordingRepository.findAllByParentRecordingIsNotNull()).thenReturn(List.of(recording2, recordingEntity)); var report = reportService.reportEdits(); @@ -277,14 +290,15 @@ void reportEditsHidesReencodedRecordingsForNonSuperUser() { @Test @DisplayName("Find all edited recordings includes re-encoded recordings for super users") void reportEditsIncludesReencodedRecordingsForSuperUser() { - var mockAuth = mock(UserAuthentication.class); - when(mockAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); - SecurityContextHolder.getContext().setAuthentication(mockAuth); + var superUserAuth = mock(UserAuthentication.class); + when(superUserAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); + SecurityContextHolder.getContext().setAuthentication(superUserAuth); recordingEntity.setVersion(2); recordingEntity.setReencode(true); when(recordingRepository.findAllByParentRecordingIsNotNull()).thenReturn(List.of(recordingEntity)); + when(authorisationService.canViewRecording(superUserAuth, recordingEntity)).thenReturn(true); var report = reportService.reportEdits(); @@ -299,6 +313,7 @@ void reportRecordingsPerCaseIncludesReencodedForSuperUser() { when(mockAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); SecurityContextHolder.getContext().setAuthentication(mockAuth); + when(authorisationService.canViewReencodedRecordings()).thenReturn(true); when(recordingRepository.countRecordingsPerCase(true)).thenReturn(List.of()); var report = reportService.reportRecordingsPerCase(); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/RecordingServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/RecordingServiceTest.java index ff45c5061..6d940c337 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/RecordingServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/RecordingServiceTest.java @@ -29,6 +29,7 @@ import uk.gov.hmcts.reform.preapi.media.storage.AzureFinalStorageService; import uk.gov.hmcts.reform.preapi.repositories.CaptureSessionRepository; import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import uk.gov.hmcts.reform.preapi.util.HelperFactory; @@ -69,6 +70,9 @@ class RecordingServiceTest { @MockitoBean private CaptureSessionService captureSessionService; + @MockitoBean + private AuthorisationService authorisationService; + @MockitoBean private GovNotify govNotify; @@ -135,6 +139,7 @@ void findRecordingByIdIncludesReencodedForSuperUser() { recordingEntity.setReencode(true); when(recordingRepository.findByIdAndDeletedAtIsNull(recordingEntity.getId(), true)) .thenReturn(Optional.of(recordingEntity)); + when(authorisationService.canViewReencodedRecordings()).thenReturn(true); var model = recordingService.findById(recordingEntity.getId()); @@ -204,6 +209,8 @@ void findAllRecordingsIncludesReencodedForSuperUser() { when(mockAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); SecurityContextHolder.getContext().setAuthentication(mockAuth); + when(authorisationService.canViewReencodedRecordings()).thenReturn(true); + recordingService.findAll(params, false, null); verify(recordingRepository, times(1)).searchAllBy(params, false, true, true, null); diff --git a/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java b/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java index be3799721..b083cce1d 100644 --- a/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java +++ b/src/test/java/uk/gov/hmcts/reform/preapi/services/ReportServiceTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.security.core.context.SecurityContextHolder; @@ -35,6 +37,7 @@ import uk.gov.hmcts.reform.preapi.repositories.RecordingRepository; import uk.gov.hmcts.reform.preapi.repositories.ShareBookingRepository; import uk.gov.hmcts.reform.preapi.repositories.UserRepository; +import uk.gov.hmcts.reform.preapi.security.AuthorisationService; import uk.gov.hmcts.reform.preapi.security.authentication.UserAuthentication; import uk.gov.hmcts.reform.preapi.util.HelperFactory; import uk.gov.hmcts.reform.preapi.utils.DateTimeUtils; @@ -91,6 +94,11 @@ public class ReportServiceTest { @Autowired private ReportService reportService; + @MockitoBean + private AuthorisationService authorisationService; + + private UserAuthentication userAuthentication; + @BeforeAll static void setUp() { regionEntity = new Region(); @@ -145,6 +153,10 @@ void reset() { auditEntity.setCreatedBy(null); auditEntity.setAuditDetails(null); bookingEntity.setParticipants(Set.of()); + + userAuthentication = mock(UserAuthentication.class); + SecurityContextHolder.getContext().setAuthentication(userAuthentication); + when(authorisationService.canViewRecording(userAuthentication, recordingEntity)).thenReturn(true); } @DisplayName("Find all capture sessions and return a list of models as a report when capture session is incomplete") @@ -234,6 +246,7 @@ void reportEditsSuccess() { recording2.setCaptureSession(captureSessionEntity); when(recordingRepository.findAllByParentRecordingIsNotNull()).thenReturn(List.of(recording2, recordingEntity)); + when(authorisationService.canViewRecording(userAuthentication, recording2)).thenReturn(true); var report = reportService.reportEdits(); @@ -250,7 +263,7 @@ void reportEditsSuccess() { } @Test - @DisplayName("Find all edited recordings hides re-encoded recordings for non super users") + @DisplayName("Find all edited recordings hides re-encoded recordings when auth service disallows it") void reportEditsHidesReencodedRecordingsForNonSuperUser() { recordingEntity.setVersion(2); var reencodedRecording = new Recording(); @@ -263,6 +276,9 @@ void reportEditsHidesReencodedRecordingsForNonSuperUser() { when(recordingRepository.findAllByParentRecordingIsNotNull()) .thenReturn(List.of(recordingEntity, reencodedRecording)); + when(authorisationService.canViewRecording(userAuthentication, recordingEntity)).thenReturn(true); + when(authorisationService.canViewRecording(userAuthentication, reencodedRecording)).thenReturn(false); + var report = reportService.reportEdits(); assertThat(report.size()).isEqualTo(1); @@ -270,16 +286,13 @@ void reportEditsHidesReencodedRecordingsForNonSuperUser() { } @Test - @DisplayName("Find all edited recordings includes re-encoded recordings for super users") + @DisplayName("Find all edited recordings includes re-encoded recordings when auth service allows it") void reportEditsIncludesReencodedRecordingsForSuperUser() { - var mockAuth = mock(UserAuthentication.class); - when(mockAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); - SecurityContextHolder.getContext().setAuthentication(mockAuth); - recordingEntity.setVersion(2); recordingEntity.setReencode(true); when(recordingRepository.findAllByParentRecordingIsNotNull()).thenReturn(List.of(recordingEntity)); + when(authorisationService.canViewRecording(userAuthentication, recordingEntity)).thenReturn(true); var report = reportService.reportEdits(); @@ -287,19 +300,17 @@ void reportEditsIncludesReencodedRecordingsForSuperUser() { assertThat(report.getFirst().getVersion()).isEqualTo(recordingEntity.getVersion()); } - @Test - @DisplayName("Find counts for recordings includes re-encoded recordings for super users") - void reportRecordingsPerCaseIncludesReencodedForSuperUser() { - var mockAuth = mock(UserAuthentication.class); - when(mockAuth.hasRole("ROLE_SUPER_USER")).thenReturn(true); - SecurityContextHolder.getContext().setAuthentication(mockAuth); - - when(recordingRepository.countRecordingsPerCase(true)).thenReturn(List.of()); + @ParameterizedTest + @ValueSource(booleans = {true, false}) + @DisplayName("Find counts for recordings includes re-encoded recordings if authorisation service enables it") + void reportRecordingsPerCaseIncludesReencodedForSuperUser(boolean includeReencodedRecordings) { + when(authorisationService.canViewReencodedRecordings()).thenReturn(includeReencodedRecordings); - var report = reportService.reportRecordingsPerCase(); + reportService.reportRecordingsPerCase(); - assertThat(report.size()).isEqualTo(0); - verify(recordingRepository, times(1)).countRecordingsPerCase(true); + // Should match whatever is returned from auth service + verify(recordingRepository, times(1)) + .countRecordingsPerCase(includeReencodedRecordings); } @DisplayName("Find shared bookings and return report list")