From 150e02641f6072a1533f2df28352432913416c8a Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Thu, 28 May 2026 11:49:35 +0200 Subject: [PATCH 1/2] Fix: check only project users can get project components --- .../facade/ProjectComponentsFacade.java | 22 ++++- .../facade/ProjectComponentsFacadeTest.java | 84 +++++++++++++++++++ 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java b/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java index 9dd3a62..d1d2945 100644 --- a/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java +++ b/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java @@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.opendevstack.component_catalog.server.controllers.exceptions.ComponentNotFoundException; +import org.opendevstack.component_catalog.server.controllers.exceptions.ForbiddenException; import org.opendevstack.component_catalog.server.mappers.ProjectComponentExtendedInfoMapper; import org.opendevstack.component_catalog.server.mappers.ProjectComponentsInfoMapper; import org.opendevstack.component_catalog.server.model.ProjectComponentExtendedInfo; @@ -15,10 +16,7 @@ import org.opendevstack.component_catalog.server.services.provisioner.ProjectComponents; import org.springframework.stereotype.Component; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Optional; +import java.util.*; @Component @AllArgsConstructor @@ -39,6 +37,10 @@ public List getProjectComponentsInfo(String projectKey, St List userGroups = projectsInfoService.getProjectGroups(accessToken); + if (!userBelongsToProjectGroups(userGroups, projectKey)) { + throw new ForbiddenException("User must belong to the project to get its components"); + } + return projectComponents.getComponents() .values() .stream() @@ -61,6 +63,11 @@ public ProjectComponentExtendedInfo getProjectComponentExtendedInfo(String proje throw new IllegalArgumentException("Valid projectKey, componentId and accessToken are mandatory."); } + List userGroups = projectsInfoService.getProjectGroups(accessToken); + if (!userBelongsToProjectGroups(userGroups, projectKey)) { + throw new ForbiddenException("User must belong to the project to get its components"); + } + return Optional.ofNullable(projectComponents.getComponents()) .orElse(Map.of()) .values() @@ -78,4 +85,11 @@ private boolean notValid(ProjectComponents projectComponents, String projectKey, projectComponents.getComponents().isEmpty() || StringUtils.isBlank(accessToken) || StringUtils.isBlank(projectKey)); } + + private boolean userBelongsToProjectGroups(List groups, String projectKey) { + if (groups == null) return false; + return groups.stream() + .filter(Objects::nonNull) + .anyMatch(g -> g.toUpperCase().contains("BI-AS-ATLASSIAN-P-" + projectKey.toUpperCase())); + } } diff --git a/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java b/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java index 2ff89ee..6a286dd 100644 --- a/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java +++ b/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java @@ -79,6 +79,7 @@ void givenProjectWithTwoComponents_whenAllCatalogFetchOk_thenReturnMappedList() var pc = ProjectComponentsMother.of(comps); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(pc); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(catalogItemsApiFacade.fetchCatalogItem(any())) .thenAnswer(inv -> { var p = (CatalogRequestParams) inv.getArgument(0); @@ -128,6 +129,7 @@ void givenOneComponentFailsWithInvalidIdException_whenGetProjectComponentsInfo_t var pc = ProjectComponentsMother.of(comps); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(pc); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(catalogItemsApiFacade.fetchCatalogItem(any())) .thenAnswer(inv -> { var p = (CatalogRequestParams) inv.getArgument(0); @@ -168,6 +170,7 @@ void givenOneComponentFailsWithInvalidCatalogItemEntityException_whenGetProjectC var pc = ProjectComponentsMother.of(comps); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(pc); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(catalogItemsApiFacade.fetchCatalogItem(any())) .thenAnswer(inv -> { var p = (CatalogRequestParams) inv.getArgument(0); @@ -205,6 +208,7 @@ void givenBlankOrNullImageFileId_whenMapToProjectComponentInfo_thenLogoUrlIsEmpt var pc = ProjectComponentsMother.of(comps); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(pc); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(catalogItemsApiFacade.fetchCatalogItem(any())) .thenAnswer(inv -> { var p = (CatalogRequestParams) inv.getArgument(0); @@ -265,6 +269,7 @@ void givenExistingComponent_whenGetExtendedInfo_thenReturnMappedInfo() { var comps = ProjectComponentsMother.of(Map.of("k1", comp)); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(projectComponentExtendedInfoMapper.mapToProjectComponentExtendedInfo(comp)) .thenReturn(Optional.of(new ProjectComponentExtendedInfo())); @@ -285,6 +290,7 @@ void givenComponentDoesNotExist_whenGetExtendedInfo_thenThrowComponentNotFound() var comps = ProjectComponentsMother.of(Map.of("k1", comp)); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); // when / then assertThatThrownBy(() -> @@ -303,6 +309,7 @@ void givenMapperReturnsEmptyOptional_whenGetExtendedInfo_thenThrowComponentNotFo var comps = ProjectComponentsMother.of(Map.of("k1", comp)); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-" + projectKey)); when(projectComponentExtendedInfoMapper.mapToProjectComponentExtendedInfo(comp)) .thenReturn(Optional.empty()); @@ -311,6 +318,83 @@ void givenMapperReturnsEmptyOptional_whenGetExtendedInfo_thenThrowComponentNotFo projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, componentId, accessToken) ).isInstanceOf(ComponentNotFoundException.class); } + + @Test + void givenUserGroupsDoNotContainProjectGroup_whenGetProjectComponentsInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + + ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("some-unrelated-group")); + + // when / then + assertThatThrownBy(() -> + projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken) + ).isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + + @Test + void givenUserGroupsContainProjectGroup_whenGetProjectComponentsInfo_thenDoNotThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + + ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-PRJ-123")); + + // when + List result = projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken); + + // then + assertThat(result).isNotNull(); + } + + @Test + void givenUserGroupsDoNotContainProjectGroup_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var componentId = "C1"; + + ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("some-unrelated-group")); + + // when / then + assertThatThrownBy(() -> + projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, componentId, accessToken) + ).isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + + @Test + void givenUserGroupsContainProjectGroup_whenGetProjectComponentExtendedInfo_thenDoNotThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var componentId = "C1"; + + ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-PRJ-123")); + when(projectComponentExtendedInfoMapper.mapToProjectComponentExtendedInfo(comp)) + .thenReturn(Optional.of(new ProjectComponentExtendedInfo())); + + // when + ProjectComponentExtendedInfo result = projectComponentsFacade + .getProjectComponentExtendedInfo(projectKey, componentId, accessToken); + + // then + assertThat(result).isNotNull(); + } } From 3f77c7ce8a521cc4df38a79afde953ff4a3a1abd Mon Sep 17 00:00:00 2001 From: "Vila,Jordi (IT EDP)" Date: Thu, 28 May 2026 12:40:00 +0200 Subject: [PATCH 2/2] Use application properties for valid prefixes --- .../ApplicationPropertiesConfiguration.java | 12 ++ .../facade/ProjectComponentsFacade.java | 5 +- src/main/resources/application.yml | 4 + .../facade/ProjectComponentsFacadeTest.java | 133 +++++++++++++++--- 4 files changed, 131 insertions(+), 23 deletions(-) diff --git a/src/main/java/org/opendevstack/component_catalog/config/ApplicationPropertiesConfiguration.java b/src/main/java/org/opendevstack/component_catalog/config/ApplicationPropertiesConfiguration.java index 2e44d1a..b83cca3 100644 --- a/src/main/java/org/opendevstack/component_catalog/config/ApplicationPropertiesConfiguration.java +++ b/src/main/java/org/opendevstack/component_catalog/config/ApplicationPropertiesConfiguration.java @@ -34,6 +34,12 @@ public ExternalServiceProps projectsInfoServiceServiceProps() { return ExternalServiceProps.builder().build(); } + @Bean("catalogProjectComponentsGroupsRestrictionConfig") + @ConfigurationProperties(prefix = "catalog.project-components.groups-restriction") + public CatalogProjectComponentsGroupsRestrictionProps catalogProjectComponentsGroupsRestrictionConfig() { + return CatalogProjectComponentsGroupsRestrictionProps.builder().build(); + } + @Bean("catalogItemGroupsRestrictionConfig") @ConfigurationProperties(prefix = "catalog.user-action.groups-restriction") public CatalogItemUserActionGroupsRestrictionProps catalogItemGroupsRestrictionConfig() { @@ -72,6 +78,12 @@ public static class BitbucketServiceCacheProps { private Duration evictionInterval; } + @Builder + @Data + public static class CatalogProjectComponentsGroupsRestrictionProps { + private List prefix; + } + @Builder @Data public static class CatalogItemUserActionGroupsRestrictionProps { diff --git a/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java b/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java index d1d2945..ce6e544 100644 --- a/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java +++ b/src/main/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacade.java @@ -3,6 +3,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.opendevstack.component_catalog.config.ApplicationPropertiesConfiguration; import org.opendevstack.component_catalog.server.controllers.exceptions.ComponentNotFoundException; import org.opendevstack.component_catalog.server.controllers.exceptions.ForbiddenException; import org.opendevstack.component_catalog.server.mappers.ProjectComponentExtendedInfoMapper; @@ -27,6 +28,7 @@ public class ProjectComponentsFacade { private final ProjectComponentsInfoMapper projectComponentsInfoMapper; private final ProjectsInfoService projectsInfoService; private final ProjectComponentExtendedInfoMapper projectComponentExtendedInfoMapper; + private ApplicationPropertiesConfiguration.CatalogProjectComponentsGroupsRestrictionProps catalogProjectComponentsGroupsRestrictionProps; public List getProjectComponentsInfo(String projectKey, String accessToken) { var projectComponents = provisionerActionsService.getProjectComponents(projectKey); @@ -90,6 +92,7 @@ private boolean userBelongsToProjectGroups(List groups, String projectKe if (groups == null) return false; return groups.stream() .filter(Objects::nonNull) - .anyMatch(g -> g.toUpperCase().contains("BI-AS-ATLASSIAN-P-" + projectKey.toUpperCase())); + .anyMatch(g -> catalogProjectComponentsGroupsRestrictionProps.getPrefix().stream().anyMatch(g.toUpperCase()::startsWith) && + g.toUpperCase().contains(projectKey.toUpperCase())); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0b3a4a8..614aa2a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -53,6 +53,10 @@ component-catalog: base-rest-url: ${PROJECTS_INFO_SERVICE_BASE_REST_URL} catalog: + project-components: + groups-restriction: + prefix: + - BI-AS-ATLASSIAN user-action: groups-restriction: prefix: diff --git a/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java b/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java index 6a286dd..3893d6e 100644 --- a/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java +++ b/src/test/java/org/opendevstack/component_catalog/server/facade/ProjectComponentsFacadeTest.java @@ -54,13 +54,18 @@ class ProjectComponentsFacadeTest { @Mock private ProjectComponentExtendedInfoMapper projectComponentExtendedInfoMapper; + @Mock + private ApplicationPropertiesConfiguration.CatalogProjectComponentsGroupsRestrictionProps catalogGroupsRestrictionProps; + @BeforeEach void setUp() { ProjectComponentsInfoMapper projectComponentsInfoMapper = new ProjectComponentsInfoMapper(catalogItemsApiFacade, catalogItemDefaultProps); - projectComponentsFacade = new ProjectComponentsFacade(provisionerActionsService, projectComponentsInfoMapper, projectsInfoService, projectComponentExtendedInfoMapper); + projectComponentsFacade = new ProjectComponentsFacade(provisionerActionsService, projectComponentsInfoMapper, + projectsInfoService, projectComponentExtendedInfoMapper, catalogGroupsRestrictionProps); lenient().when(authenticationFacade.getAccessToken()).thenReturn("accessToken"); + lenient().when(catalogGroupsRestrictionProps.getPrefix()).thenReturn(List.of("BI-AS-ATLASSIAN-P-")); } @Test @@ -319,32 +324,73 @@ void givenMapperReturnsEmptyOptional_whenGetExtendedInfo_thenThrowComponentNotFo ).isInstanceOf(ComponentNotFoundException.class); } + @Test - void givenUserGroupsDoNotContainProjectGroup_whenGetProjectComponentsInfo_thenThrowForbiddenException() { + void givenNullUserGroups_whenGetProjectComponentsInfo_thenThrowForbiddenException() { // given var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(null); - ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); - var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + // when / then + assertThatThrownBy(() -> projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + @Test + void givenEmptyUserGroups_whenGetProjectComponentsInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); - when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("some-unrelated-group")); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of()); // when / then - assertThatThrownBy(() -> - projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken) - ).isInstanceOf(ForbiddenException.class) + assertThatThrownBy(() -> projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken)) + .isInstanceOf(ForbiddenException.class) .hasMessageContaining("User must belong to the project to get its components"); } @Test - void givenUserGroupsContainProjectGroup_whenGetProjectComponentsInfo_thenDoNotThrowForbiddenException() { + void givenGroupWithNoMatchingPrefix_whenGetProjectComponentsInfo_thenThrowForbiddenException() { // given var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("WRONG-PREFIX-PRJ-123")); - ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); - var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + // when / then + assertThatThrownBy(() -> projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + + @Test + void givenGroupWithMatchingPrefixButWrongProject_whenGetProjectComponentsInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-OTHER")); + + // when / then + assertThatThrownBy(() -> projectComponentsFacade.getProjectComponentsInfo(projectKey, accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + @Test + void givenGroupWithMatchingPrefixAndProject_whenGetProjectComponentsInfo_thenDoNotThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-PRJ-123")); @@ -355,34 +401,77 @@ void givenUserGroupsContainProjectGroup_whenGetProjectComponentsInfo_thenDoNotTh assertThat(result).isNotNull(); } + @Test - void givenUserGroupsDoNotContainProjectGroup_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { + void givenNullUserGroups_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { // given var projectKey = "PRJ-123"; - var componentId = "C1"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(null); - ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); - var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); + // when / then + assertThatThrownBy(() -> + projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, "C1", accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + @Test + void givenEmptyUserGroups_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); - when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("some-unrelated-group")); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of()); // when / then assertThatThrownBy(() -> - projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, componentId, accessToken) - ).isInstanceOf(ForbiddenException.class) + projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, "C1", accessToken)) + .isInstanceOf(ForbiddenException.class) .hasMessageContaining("User must belong to the project to get its components"); } @Test - void givenUserGroupsContainProjectGroup_whenGetProjectComponentExtendedInfo_thenDoNotThrowForbiddenException() { + void givenGroupWithNoMatchingPrefix_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { // given var projectKey = "PRJ-123"; - var componentId = "C1"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("WRONG-PREFIX-PRJ-123")); + // when / then + assertThatThrownBy(() -> + projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, "C1", accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + + @Test + void givenGroupWithMatchingPrefixButWrongProject_whenGetProjectComponentExtendedInfo_thenThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; + var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", + ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED)))); + when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); + when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-OTHER")); + + // when / then + assertThatThrownBy(() -> + projectComponentsFacade.getProjectComponentExtendedInfo(projectKey, "C1", accessToken)) + .isInstanceOf(ForbiddenException.class) + .hasMessageContaining("User must belong to the project to get its components"); + } + + @Test + void givenGroupWithMatchingPrefixAndProject_whenGetProjectComponentExtendedInfo_thenDoNotThrowForbiddenException() { + // given + var projectKey = "PRJ-123"; ProjectComponent comp = ProjectComponentMother.of("C1", "Y2F0LTE", "cmVmLTE", Status.CREATED); var comps = ProjectComponentsMother.of(new LinkedHashMap<>(Map.of("k1", comp))); - when(provisionerActionsService.getProjectComponents(projectKey)).thenReturn(comps); when(projectsInfoService.getProjectGroups(accessToken)).thenReturn(List.of("BI-AS-ATLASSIAN-P-PRJ-123")); when(projectComponentExtendedInfoMapper.mapToProjectComponentExtendedInfo(comp)) @@ -390,7 +479,7 @@ void givenUserGroupsContainProjectGroup_whenGetProjectComponentExtendedInfo_then // when ProjectComponentExtendedInfo result = projectComponentsFacade - .getProjectComponentExtendedInfo(projectKey, componentId, accessToken); + .getProjectComponentExtendedInfo(projectKey, "C1", accessToken); // then assertThat(result).isNotNull();