From 99867788bcf7e16e6a812e88979d0e7dd6ed3616 Mon Sep 17 00:00:00 2001 From: Edwin Tan Date: Tue, 26 Nov 2024 13:09:52 -0800 Subject: [PATCH] MAT-7918: support granting and revoking ACLs on library --- pom.xml | 2 +- .../controllers/CqlLibraryController.java | 27 ++-- .../services/CqlLibraryService.java | 31 ++-- .../services/LibrarySetService.java | 79 ++++++---- .../cqllibraryservice/utils/AuthUtils.java | 4 +- .../CqlLibraryControllerMvcTest.java | 5 +- .../controllers/CqlLibraryControllerTest.java | 30 ++++ .../services/CqlLibraryServiceAclTest.java | 95 ++++++------ .../services/LibrarySetServiceTest.java | 146 +++++++++++++++++- .../services/VersionServiceTest.java | 19 +-- 10 files changed, 314 insertions(+), 124 deletions(-) diff --git a/pom.xml b/pom.xml index 24ad6ed..5a5c9b1 100644 --- a/pom.xml +++ b/pom.xml @@ -102,7 +102,7 @@ gov.cms.madie madie-java-models - 0.6.70-SNAPSHOT + 0.6.71-SNAPSHOT gov.cms.madie diff --git a/src/main/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryController.java b/src/main/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryController.java index d8444c6..852eea7 100644 --- a/src/main/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryController.java +++ b/src/main/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryController.java @@ -8,6 +8,8 @@ import gov.cms.madie.cqllibraryservice.services.LibrarySetService; import gov.cms.madie.cqllibraryservice.utils.AuthUtils; import gov.cms.madie.cqllibraryservice.utils.LibraryUtils; +import gov.cms.madie.models.access.AclOperation; +import gov.cms.madie.models.access.AclSpecification; import gov.cms.madie.models.common.ActionType; import gov.cms.madie.models.dto.LibraryUsage; import gov.cms.madie.models.library.CqlLibrary; @@ -218,27 +220,16 @@ public ResponseEntity changeOwnership( return response; } - @PutMapping( - value = "/{id}/grant", - produces = {MediaType.TEXT_PLAIN_VALUE}) + @PutMapping("/{id}/acls") @PreAuthorize("#request.getHeader('api-key') == #apiKey") - public ResponseEntity grantAccess( + public ResponseEntity> updateAccessControl( HttpServletRequest request, - @PathVariable("id") String id, - @RequestParam(required = true, name = "userid") String userid, + @PathVariable String id, + @RequestBody @Validated AclOperation aclOperation, @Value("${admin-api-key}") String apiKey) { - ResponseEntity response = - ResponseEntity.badRequest().body("Cql Library does not exist."); - - log.info("getLibraryId [{}] using apiKey ", id); - - if (cqlLibraryService.grantAccess(id, userid)) { - response = - ResponseEntity.ok() - .body(String.format("%s granted access to Library successfully.", userid)); - actionLogService.logAction(id, ActionType.UPDATED, "apiKey"); - } - return response; + List aclSpecifications = + cqlLibraryService.updateAccessControlList(id, aclOperation); + return ResponseEntity.ok().body(aclSpecifications); } @GetMapping("/sharedWith") diff --git a/src/main/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryService.java b/src/main/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryService.java index 611b425..3140ed7 100644 --- a/src/main/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryService.java +++ b/src/main/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryService.java @@ -4,7 +4,9 @@ import gov.cms.madie.cqllibraryservice.dto.LibraryListDTO; import gov.cms.madie.cqllibraryservice.exceptions.*; import gov.cms.madie.cqllibraryservice.repositories.LibrarySetRepository; -import gov.cms.madie.models.access.RoleEnum; +import gov.cms.madie.models.access.AclOperation; +import gov.cms.madie.models.access.AclSpecification; +import gov.cms.madie.models.common.ActionType; import gov.cms.madie.models.common.Version; import gov.cms.madie.models.dto.LibraryUsage; import gov.cms.madie.models.library.CqlLibrary; @@ -29,6 +31,7 @@ public class CqlLibraryService { private final ElmTranslatorClient elmTranslatorClient; private final LibrarySetRepository librarySetRepository; private CqlLibraryRepository cqlLibraryRepository; + private final ActionLogService actionLogService; private LibrarySetService librarySetService; private MeasureServiceClient measureServiceClient; @@ -98,24 +101,26 @@ public CqlLibrary findCqlLibraryById(String id) { throw new ResourceNotFoundException("CQL Library", id); } - public boolean changeOwnership(String id, String userid) { - boolean result = false; - Optional persistedCqlLibrary = cqlLibraryRepository.findById(id); - if (persistedCqlLibrary.isPresent()) { - CqlLibrary cqlLibrary = persistedCqlLibrary.get(); - librarySetService.updateOwnership(cqlLibrary.getLibrarySetId(), userid); - result = true; + public List updateAccessControlList( + String cqlLibraryId, AclOperation aclOperation) { + Optional persistedLibrary = cqlLibraryRepository.findById(cqlLibraryId); + if (persistedLibrary.isEmpty()) { + throw new ResourceNotFoundException("Library does not exist: " + cqlLibraryId); } - return result; + + CqlLibrary library = persistedLibrary.get(); + LibrarySet librarySet = + librarySetService.updateLibrarySetAcls(library.getLibrarySetId(), aclOperation); + actionLogService.logAction(cqlLibraryId, ActionType.UPDATED, "admin"); + return librarySet.getAcls(); } - public boolean grantAccess(String cqlLibraryId, String userid) { + public boolean changeOwnership(String id, String userid) { boolean result = false; - Optional persistedCqlLibrary = cqlLibraryRepository.findById(cqlLibraryId); + Optional persistedCqlLibrary = cqlLibraryRepository.findById(id); if (persistedCqlLibrary.isPresent()) { CqlLibrary cqlLibrary = persistedCqlLibrary.get(); - librarySetService.updateLibrarySetAcls( - cqlLibrary.getLibrarySetId(), userid, RoleEnum.SHARED_WITH); + librarySetService.updateOwnership(cqlLibrary.getLibrarySetId(), userid); result = true; } return result; diff --git a/src/main/java/gov/cms/madie/cqllibraryservice/services/LibrarySetService.java b/src/main/java/gov/cms/madie/cqllibraryservice/services/LibrarySetService.java index cc5637f..3ca8063 100644 --- a/src/main/java/gov/cms/madie/cqllibraryservice/services/LibrarySetService.java +++ b/src/main/java/gov/cms/madie/cqllibraryservice/services/LibrarySetService.java @@ -2,15 +2,15 @@ import gov.cms.madie.cqllibraryservice.exceptions.ResourceNotFoundException; import gov.cms.madie.cqllibraryservice.repositories.LibrarySetRepository; +import gov.cms.madie.models.access.AclOperation; import gov.cms.madie.models.access.AclSpecification; -import gov.cms.madie.models.access.RoleEnum; import gov.cms.madie.models.common.ActionType; import gov.cms.madie.models.library.LibrarySet; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Service; -import java.util.List; +import java.util.Objects; import java.util.Optional; @Slf4j @@ -36,52 +36,75 @@ public void createLibrarySet( } } - public LibrarySet updateLibrarySetAcls(String librarySetId, String userId, RoleEnum role) { + public LibrarySet updateLibrarySetAcls(String librarySetId, AclOperation aclOperation) { Optional optionalLibrarySet = librarySetRepository.findByLibrarySetId(librarySetId); if (optionalLibrarySet.isPresent()) { LibrarySet librarySet = optionalLibrarySet.get(); - if (CollectionUtils.isEmpty(librarySet.getAcls())) { - librarySet.setAcls(List.of(createAcl(userId, role))); - } else { - Optional aclSpecification = getAclSpecification(userId, librarySet); - if (aclSpecification.isPresent()) { - AclSpecification specification = aclSpecification.get(); - if (!specification.getRoles().contains(role)) { - specification.getRoles().add(role); - } + if (AclOperation.AclAction.GRANT == aclOperation.getAction()) { + if (CollectionUtils.isEmpty(librarySet.getAcls())) { + // if no acl present, add it + librarySet.setAcls(aclOperation.getAcls()); } else { - librarySet.getAcls().add(createAcl(userId, role)); + // update acl + aclOperation + .getAcls() + .forEach( + acl -> { + // check if acl already present for the user + AclSpecification aclSpecification = + findAclSpecificationByUserId(librarySet, acl.getUserId()); + // if acl does not present, add it + if (aclSpecification == null) { + librarySet.getAcls().add(acl); + } else { + aclSpecification.getRoles().addAll(acl.getRoles()); + } + }); } + } else if (AclOperation.AclAction.REVOKE == aclOperation.getAction()) { + aclOperation + .getAcls() + .forEach( + acl -> { + // check if acl already present for the user + AclSpecification aclSpecification = + findAclSpecificationByUserId(librarySet, acl.getUserId()); + if (aclSpecification != null) { + // remove roles from ACL + aclSpecification.getRoles().removeAll(acl.getRoles()); + // after removing the roles if there is no role left, remove acl + if (aclSpecification.getRoles().isEmpty()) { + librarySet.getAcls().remove(aclSpecification); + } + } + }); } + LibrarySet updatedLibrarySet = librarySetRepository.save(librarySet); - log.info("SHARED acl added to library set [{}]", updatedLibrarySet.getId()); + log.info("ACL updated for Library set [{}]", updatedLibrarySet.getId()); return updatedLibrarySet; } else { String error = String.format( "Library with set id `%s` can not be shared. Library set may not exists.", - librarySetId, userId); + librarySetId); log.error(error); throw new ResourceNotFoundException("LibrarySet", "id", librarySetId); } } - private AclSpecification createAcl(String userId, RoleEnum role) { - AclSpecification spec = new AclSpecification(); - spec.setUserId(userId); - spec.setRoles(List.of(role)); - - return spec; + public LibrarySet findByLibrarySetId(final String librarySetId) { + return librarySetRepository.findByLibrarySetId(librarySetId).orElse(null); } - public Optional getAclSpecification(String userId, LibrarySet librarySet) { + private AclSpecification findAclSpecificationByUserId(LibrarySet librarySet, String userId) { + if (CollectionUtils.isEmpty(librarySet.getAcls())) { + return null; + } return librarySet.getAcls().stream() - .filter(acl -> userId.equalsIgnoreCase(acl.getUserId())) - .findFirst(); - } - - public LibrarySet findByLibrarySetId(final String librarySetId) { - return librarySetRepository.findByLibrarySetId(librarySetId).orElse(null); + .filter(existingAcl -> Objects.equals(existingAcl.getUserId(), userId)) + .findFirst() + .orElse(null); } public LibrarySet updateOwnership(String librarySetId, String userId) { diff --git a/src/main/java/gov/cms/madie/cqllibraryservice/utils/AuthUtils.java b/src/main/java/gov/cms/madie/cqllibraryservice/utils/AuthUtils.java index 069f3cd..c4e89c0 100644 --- a/src/main/java/gov/cms/madie/cqllibraryservice/utils/AuthUtils.java +++ b/src/main/java/gov/cms/madie/cqllibraryservice/utils/AuthUtils.java @@ -6,14 +6,14 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.util.CollectionUtils; -import java.util.List; +import java.util.Set; @Slf4j public class AuthUtils { public static void checkAccessPermissions(CqlLibrary cqlLibrary, String username) { // TODO: hardcoded allowed ACLs for now - List allowedRoles = List.of(RoleEnum.SHARED_WITH); + Set allowedRoles = Set.of(RoleEnum.SHARED_WITH); if (!username.equalsIgnoreCase(cqlLibrary.getLibrarySet().getOwner()) && (CollectionUtils.isEmpty(cqlLibrary.getLibrarySet().getAcls()) || cqlLibrary.getLibrarySet().getAcls().stream() diff --git a/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerMvcTest.java b/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerMvcTest.java index 6a47176..feb0a85 100644 --- a/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerMvcTest.java +++ b/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerMvcTest.java @@ -51,6 +51,7 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; +import java.util.Set; import gov.cms.madie.models.library.LibrarySet; @@ -1128,7 +1129,7 @@ public void testAdminMultipleMeasuresGetSharedWith() throws Exception { CqlLibrary lib2 = CqlLibrary.builder().id("6789").build(); AclSpecification acl1 = new AclSpecification(); acl1.setUserId("raoulduke"); - acl1.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl1.setRoles(Set.of(RoleEnum.SHARED_WITH)); List acls = List.of(acl1); LibrarySet librarySet = LibrarySet.builder().acls(acls).build(); @@ -1155,7 +1156,7 @@ public void testAdminMeasureGetSharedWith() throws Exception { CqlLibrary testLibrary = CqlLibrary.builder().id("12345").build(); AclSpecification acl1 = new AclSpecification(); acl1.setUserId("raoulduke"); - acl1.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl1.setRoles(Set.of(RoleEnum.SHARED_WITH)); List acls = List.of(acl1); LibrarySet librarySet = LibrarySet.builder().acls(acls).build(); diff --git a/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerTest.java b/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerTest.java index 335e214..c8e3d20 100644 --- a/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerTest.java +++ b/src/test/java/gov/cms/madie/cqllibraryservice/controllers/CqlLibraryControllerTest.java @@ -20,6 +20,9 @@ import gov.cms.madie.cqllibraryservice.exceptions.ResourceNotFoundException; import gov.cms.madie.cqllibraryservice.services.ActionLogService; import gov.cms.madie.cqllibraryservice.services.LibrarySetService; +import gov.cms.madie.models.access.AclOperation; +import gov.cms.madie.models.access.AclSpecification; +import gov.cms.madie.models.access.RoleEnum; import gov.cms.madie.models.common.ActionType; import gov.cms.madie.models.dto.LibraryUsage; import gov.cms.madie.models.library.CqlLibrary; @@ -34,6 +37,7 @@ import java.time.temporal.ChronoUnit; import java.util.List; import java.util.Optional; +import java.util.Set; import gov.cms.madie.models.library.LibrarySet; import org.junit.jupiter.api.BeforeEach; @@ -528,4 +532,30 @@ void testDeleteLibraryAlongWithVersions() { response.getBody(), is(equalTo("The library and all its associated versions have been removed successfully."))); } + + @Test + public void testUpdateAccessControl() { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader("api-key", "key"); + + AclSpecification aclSpecification = new AclSpecification(); + aclSpecification.setUserId("user_1"); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); + + AclOperation aclOperation = + AclOperation.builder() + .acls(List.of(aclSpecification)) + .action(AclOperation.AclAction.GRANT) + .build(); + + List aclSpecifications = List.of(aclSpecification); + + when(cqlLibraryService.updateAccessControlList(anyString(), any())) + .thenReturn(aclSpecifications); + + ResponseEntity> output = cqlLibraryController.updateAccessControl(request, "1", aclOperation, "key"); + + verify(cqlLibraryService, times(1)).updateAccessControlList(anyString(), any()); + assertThat(output.getBody(), equalTo(aclSpecifications)); + } } diff --git a/src/test/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryServiceAclTest.java b/src/test/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryServiceAclTest.java index 7290ca8..2f3839f 100644 --- a/src/test/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryServiceAclTest.java +++ b/src/test/java/gov/cms/madie/cqllibraryservice/services/CqlLibraryServiceAclTest.java @@ -1,12 +1,12 @@ package gov.cms.madie.cqllibraryservice.services; +import gov.cms.madie.cqllibraryservice.exceptions.ResourceNotFoundException; import gov.cms.madie.cqllibraryservice.repositories.CqlLibraryRepository; -import gov.cms.madie.cqllibraryservice.repositories.LibrarySetRepository; +import gov.cms.madie.models.access.AclOperation; import gov.cms.madie.models.access.AclSpecification; import gov.cms.madie.models.access.RoleEnum; import gov.cms.madie.models.library.CqlLibrary; import gov.cms.madie.models.library.LibrarySet; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -15,71 +15,68 @@ import java.util.List; import java.util.Optional; +import java.util.Set; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.*; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class CqlLibraryServiceAclTest { @Mock private CqlLibraryRepository cqlLibraryRepository; - @Mock private LibrarySetRepository librarySetRepository; - @InjectMocks private LibrarySetService librarySetService; + @Mock private LibrarySetService librarySetService; + @Mock private ActionLogService actionLogService; @InjectMocks private CqlLibraryService cqlLibraryService; - LibrarySet librarySet; - AclSpecification spec; - - @BeforeEach - public void setUp() { - librarySet = LibrarySet.builder().librarySetId("1-2-3").owner("user-1").id("1").build(); - - spec = new AclSpecification(); - spec.setUserId("testUser"); - spec.setRoles(List.of(RoleEnum.SHARED_WITH)); - } @Test - public void testGrantAccessNoCqlLibrary() { - Optional persistedCqlLibrary = Optional.empty(); - when(cqlLibraryRepository.findById(eq("123"))).thenReturn(persistedCqlLibrary); - boolean result = cqlLibraryService.grantAccess("123", "testUser"); + public void testUpdateAccessControlListWithNoLibrary() { + CqlLibrary library = CqlLibrary.builder().id("123").librarySetId("1-2-3").build(); + AclSpecification aclSpecification = new AclSpecification(); + aclSpecification.setUserId("test"); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); - assertFalse(result); - } - - @Test - public void testGrantAccessWhenAclNotExists() { - when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); - LibrarySet updatedLibrarySet = - LibrarySet.builder().librarySetId("1-2-3").owner("owner").acls(List.of(spec)).build(); - when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + AclOperation aclOperation = + AclOperation.builder() + .acls(List.of(aclSpecification)) + .action(AclOperation.AclAction.GRANT) + .build(); + when(cqlLibraryRepository.findById(anyString())).thenReturn(Optional.empty()); - LibrarySet savedLibrarySet = - librarySetService.updateLibrarySetAcls("1-2-3", "testUser", RoleEnum.SHARED_WITH); - assertThat(savedLibrarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); - assertThat(savedLibrarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); - assertThat(savedLibrarySet.getAcls().get(0).getUserId(), is(equalTo("testUser"))); - assertThat( - savedLibrarySet.getAcls().get(0).getRoles(), is(equalTo(List.of(RoleEnum.SHARED_WITH)))); + Exception ex = + assertThrows( + ResourceNotFoundException.class, () -> cqlLibraryService.updateAccessControlList(library.getId(), aclOperation)); + assertEquals(ex.getMessage(), "Library does not exist: " + library.getId()); } @Test - public void testGrantAccessWhenAclExists() { - librarySet.setAcls(List.of(spec)); - when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); - LibrarySet updatedLibrarySet = - LibrarySet.builder().librarySetId("1-2-3").owner("owner").acls(List.of(spec)).build(); - when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + public void testUpdateAccessControlList() { + CqlLibrary library = CqlLibrary.builder().id("123").librarySetId("1-2-3").build(); + AclSpecification aclSpecification = new AclSpecification(); + aclSpecification.setUserId("test"); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); + LibrarySet librarySet = + LibrarySet.builder() + .librarySetId(library.getLibrarySetId()) + .acls(List.of(aclSpecification)) + .build(); + AclOperation aclOperation = + AclOperation.builder() + .acls(List.of(aclSpecification)) + .action(AclOperation.AclAction.GRANT) + .build(); + Optional persistedLibrary = Optional.of(library); + when(cqlLibraryRepository.findById(anyString())).thenReturn(persistedLibrary); + when(librarySetService.updateLibrarySetAcls(any(), any())).thenReturn(librarySet); + when(actionLogService.logAction(any(), any(), any())).thenReturn(true); - LibrarySet savedLibrarySet = - librarySetService.updateLibrarySetAcls("1-2-3", "testUser", RoleEnum.SHARED_WITH); - assertThat(savedLibrarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); - assertThat(savedLibrarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); - assertThat(savedLibrarySet.getAcls().get(0).getUserId(), is(equalTo("testUser"))); - assertThat( - savedLibrarySet.getAcls().get(0).getRoles(), is(equalTo(List.of(RoleEnum.SHARED_WITH)))); + List aclSpecifications = + cqlLibraryService.updateAccessControlList(library.getId(), aclOperation); + assertThat(aclSpecifications.size(), is(equalTo(1))); + assertThat(aclSpecifications.get(0).getUserId(), is(aclSpecification.getUserId())); + assertThat(aclSpecifications.get(0).getRoles(), is(aclSpecification.getRoles())); } } diff --git a/src/test/java/gov/cms/madie/cqllibraryservice/services/LibrarySetServiceTest.java b/src/test/java/gov/cms/madie/cqllibraryservice/services/LibrarySetServiceTest.java index d91cc9b..8d5198a 100644 --- a/src/test/java/gov/cms/madie/cqllibraryservice/services/LibrarySetServiceTest.java +++ b/src/test/java/gov/cms/madie/cqllibraryservice/services/LibrarySetServiceTest.java @@ -1,6 +1,9 @@ package gov.cms.madie.cqllibraryservice.services; import gov.cms.madie.cqllibraryservice.repositories.LibrarySetRepository; +import gov.cms.madie.models.access.AclOperation; +import gov.cms.madie.models.access.AclSpecification; +import gov.cms.madie.models.access.RoleEnum; import gov.cms.madie.models.library.LibrarySet; import org.junit.jupiter.api.Assertions; import gov.cms.madie.cqllibraryservice.exceptions.ResourceNotFoundException; @@ -11,11 +14,12 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import java.util.Optional; +import java.util.*; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -32,7 +36,145 @@ class LibrarySetServiceTest { @BeforeEach public void setUp() { - librarySet = LibrarySet.builder().librarySetId("set-1").owner("user-1").id("1").build(); + AclSpecification aclSpec = new AclSpecification(); + aclSpec.setUserId("john"); + aclSpec.setRoles( + new HashSet<>() { + { + add(RoleEnum.SHARED_WITH); + } + }); + + librarySet = + LibrarySet.builder() + .librarySetId("1") + .owner("user-1") + .acls( + new ArrayList<>() { + { + add(aclSpec); + } + }) + .build(); + } + + @Test + public void testGrantOperationNoLibrarySetFound() { + String librarySetId = "1"; + AclSpecification aclSpec = new AclSpecification(); + aclSpec.setUserId("john"); + aclSpec.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec)).action(AclOperation.AclAction.GRANT).build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.empty()); + + Exception ex = + assertThrows( + ResourceNotFoundException.class, () -> librarySetService.updateLibrarySetAcls(librarySetId, aclOperation)); + assertEquals(ex.getMessage(), "Could not find resource LibrarySet with id: " + librarySetId); + } + + @Test + public void testGrantOperationAsFirstNewAcl() { + AclSpecification aclSpec = new AclSpecification(); + aclSpec.setUserId("john_1"); + aclSpec.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec)).action(AclOperation.AclAction.GRANT).build(); + LibrarySet updatedLibrarySet = + LibrarySet.builder().librarySetId("1").owner("john_1").acls(List.of(aclSpec)).build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); + when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + + LibrarySet librarySet = librarySetService.updateLibrarySetAcls("1", aclOperation); + assertThat(librarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); + assertThat(librarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); + assertThat(librarySet.getAcls().size(), is(equalTo(1))); + } + + @Test + public void testGrantOperationAsFirstNewAclWithNoAclsInLibrarySet() { + LibrarySet librarySetWithNoAcls = LibrarySet.builder() + .librarySetId("1") + .owner("user-1") + .build(); + AclSpecification aclSpec = new AclSpecification(); + aclSpec.setUserId("john_1"); + aclSpec.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec)).action(AclOperation.AclAction.GRANT).build(); + LibrarySet updatedLibrarySet = + LibrarySet.builder().librarySetId("1").owner("john_1").acls(List.of(aclSpec)).build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySetWithNoAcls)); + when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + + LibrarySet librarySet = librarySetService.updateLibrarySetAcls("1", aclOperation); + assertThat(librarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); + assertThat(librarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); + assertThat(librarySet.getAcls().size(), is(equalTo(1))); + } + + @Test + public void testGrantOperationAsSecondNewAcl() { + AclSpecification aclSpec1 = new AclSpecification(); + aclSpec1.setUserId("john"); + aclSpec1.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclSpecification aclSpec2 = new AclSpecification(); + aclSpec2.setUserId("jane"); + aclSpec2.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec2)).action(AclOperation.AclAction.GRANT).build(); + LibrarySet updatedLibrarySet = + LibrarySet.builder() + .librarySetId("1") + .owner("john") + .acls(List.of(aclSpec1, aclSpec2)) + .build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); + when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + + LibrarySet librarySet = librarySetService.updateLibrarySetAcls("1", aclOperation); + assertThat(librarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); + assertThat(librarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); + assertThat(librarySet.getAcls().size(), is(equalTo(2))); + } + + @Test + public void testGrantOperationUpdateAcl() { + AclSpecification aclSpec1 = new AclSpecification(); + aclSpec1.setUserId("john"); + aclSpec1.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclSpecification aclSpec2 = new AclSpecification(); + aclSpec2.setUserId("john"); + aclSpec2.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec2)).action(AclOperation.AclAction.GRANT).build(); + LibrarySet updatedLibrarySet = + LibrarySet.builder().librarySetId("1").owner("john").acls(List.of(aclSpec1)).build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); + when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(updatedLibrarySet); + + LibrarySet librarySet = librarySetService.updateLibrarySetAcls("1", aclOperation); + assertThat(librarySet.getId(), is(equalTo(updatedLibrarySet.getId()))); + assertThat(librarySet.getOwner(), is(equalTo(updatedLibrarySet.getOwner()))); + assertThat(librarySet.getAcls().size(), is(equalTo(1))); + assertThat(librarySet.getAcls().get(0).getUserId(), is(equalTo(aclSpec2.getUserId()))); + } + + @Test + public void testRevokeOperation() { + AclSpecification aclSpec = new AclSpecification(); + aclSpec.setUserId("john"); + aclSpec.setRoles(Set.of(RoleEnum.SHARED_WITH)); + AclOperation aclOperation = + AclOperation.builder().acls(List.of(aclSpec)).action(AclOperation.AclAction.REVOKE).build(); + when(librarySetRepository.findByLibrarySetId(anyString())).thenReturn(Optional.of(librarySet)); + when(librarySetRepository.save(any(LibrarySet.class))).thenReturn(librarySet); + + LibrarySet updatedLibrarySet = librarySetService.updateLibrarySetAcls("1", aclOperation); + assertThat(updatedLibrarySet.getId(), is(equalTo(librarySet.getId()))); + assertThat(updatedLibrarySet.getOwner(), is(equalTo(librarySet.getOwner()))); + assertThat(updatedLibrarySet.getAcls().size(), is(equalTo(0))); } @Test diff --git a/src/test/java/gov/cms/madie/cqllibraryservice/services/VersionServiceTest.java b/src/test/java/gov/cms/madie/cqllibraryservice/services/VersionServiceTest.java index c95cbaa..12551a1 100644 --- a/src/test/java/gov/cms/madie/cqllibraryservice/services/VersionServiceTest.java +++ b/src/test/java/gov/cms/madie/cqllibraryservice/services/VersionServiceTest.java @@ -20,6 +20,7 @@ import gov.cms.madie.cqllibraryservice.repositories.CqlLibraryRepository; import java.util.List; import java.util.Optional; +import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -92,7 +93,7 @@ void testCreateVersionThrowsExceptionWhenCqlHasErrors() { .build(); AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); assertThrows( @@ -155,7 +156,7 @@ void testCreateVersionThrowsRunTimeExceptionIfLibrarySetIDIsNull() { .build(); AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); assertThrows( @@ -183,7 +184,7 @@ void testCreateVersionThrowsElmTranslatorErrorException() { AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); when(cqlLibraryRepository.findMaxVersionByLibrarySetId(anyString())) .thenReturn(Optional.of(Version.parse("1.0.0"))); @@ -212,7 +213,7 @@ void testCreateVersionHandlesHasErrorsException() { .build(); AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); when(cqlLibraryRepository.findMaxVersionByLibrarySetId(anyString())) .thenReturn(Optional.of(Version.parse("1.0.0"))); @@ -244,7 +245,7 @@ void testCreateVersionMajorSuccess() { CqlLibrary updatedCqlLibrary = existingCqlLibrary.toBuilder().build(); AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); when(cqlLibraryRepository.findMaxVersionByLibrarySetId(anyString())) .thenReturn(Optional.of(Version.parse("1.0.0"))); @@ -287,7 +288,7 @@ void testCreateVersionMinorSuccess() { CqlLibrary updatedCqlLibrary = existingCqlLibrary.toBuilder().build(); AclSpecification acl = new AclSpecification(); acl.setUserId("testUser"); - acl.setRoles(List.of(RoleEnum.SHARED_WITH)); + acl.setRoles(Set.of(RoleEnum.SHARED_WITH)); when(cqlLibraryService.findCqlLibraryById(anyString())).thenReturn(existingCqlLibrary); when(cqlLibraryRepository.findMaxMinorVersionByLibrarySetIdAndVersionMajor( anyString(), anyInt())) @@ -326,7 +327,7 @@ void testCreateDraftThrowsExceptionForResourceNotFound() { void testCreateDraftThrowsExceptionForAuthorization() { AclSpecification aclSpecification = new AclSpecification(); aclSpecification.setUserId("sharedUser"); - aclSpecification.setRoles(List.of(RoleEnum.SHARED_WITH)); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); CqlLibrary existingCqlLibrary = CqlLibrary.builder() @@ -356,7 +357,7 @@ void testCreateDraftThrowsExceptionForAuthorization() { void testCreateDraftSuccesfullyDraftsForSharedUser() { AclSpecification aclSpecification = new AclSpecification(); aclSpecification.setUserId("sharedUser"); - aclSpecification.setRoles(List.of(RoleEnum.SHARED_WITH)); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); CqlLibrary existingCqlLibrary = CqlLibrary.builder() @@ -434,7 +435,7 @@ void testCreateDraftSuccess() { void testCreateDraftThrowsExceptionWhenDraftAlreadyExists() { AclSpecification aclSpecification = new AclSpecification(); aclSpecification.setUserId("sharedUser"); - aclSpecification.setRoles(List.of(RoleEnum.SHARED_WITH)); + aclSpecification.setRoles(Set.of(RoleEnum.SHARED_WITH)); CqlLibrary existingCqlLibrary = CqlLibrary.builder()