diff --git a/openapi/openapi-component_catalog-v1.0.0.yaml b/openapi/openapi-component_catalog-v1.0.0.yaml index 54f1b67..c6f750e 100644 --- a/openapi/openapi-component_catalog-v1.0.0.yaml +++ b/openapi/openapi-component_catalog-v1.0.0.yaml @@ -1156,6 +1156,9 @@ components: deletionWorkflowJobId: type: string example: '123456' + canBeDeleted: + type: boolean + example: 'true' parameters: type: array items: diff --git a/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidator.java b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidator.java new file mode 100644 index 0000000..39a4967 --- /dev/null +++ b/src/main/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidator.java @@ -0,0 +1,18 @@ +package org.opendevstack.component_provisioner.server.controllers.validators; + +import lombok.extern.slf4j.Slf4j; +import org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentExtendedInfo; +import org.opendevstack.component_provisioner.server.controllers.exceptions.UserNotAllowedException; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class ProjectComponentDeletePermissionsValidator { + + public void validate(ProjectComponentExtendedInfo projectComponentExtendedInfo) { + log.debug("Validating user has permissions to delete. ProjectComponentExtendedInfo: {}", projectComponentExtendedInfo); + if (!Boolean.TRUE.equals(projectComponentExtendedInfo.getCanBeDeleted())) { + throw new UserNotAllowedException("User has no permissions to delete this project component"); + } + } +} diff --git a/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacade.java b/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacade.java index dec2750..5f2a4e5 100644 --- a/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacade.java +++ b/src/main/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacade.java @@ -15,6 +15,7 @@ import org.opendevstack.component_provisioner.server.controllers.validators.InputParamsValidator; import org.opendevstack.component_provisioner.server.controllers.validators.ParameterType; import org.opendevstack.component_provisioner.server.controllers.validators.WorkflowsValidator; +import org.opendevstack.component_provisioner.server.controllers.validators.ProjectComponentDeletePermissionsValidator; import org.opendevstack.component_provisioner.server.mappers.EntitiesMapper; import org.opendevstack.component_provisioner.server.model.CreateIncidentAction; import org.opendevstack.component_provisioner.server.model.CreateIncidentParameter; @@ -65,6 +66,7 @@ public class ProvisionResultsApiFacade { private final WorkflowsValidator workflowsValidator; private final DeletionSentinelWorkflowValidator deletionSentinelWorkflowValidator; private final InputParamsValidator inputParamsValidator; + private final ProjectComponentDeletePermissionsValidator projectComponentDeletePermissionsValidator; @Value("${component-provisioner.awx.workflows.create-incident-workflow-id}") private String createIncidentWorkflowId; @@ -82,6 +84,8 @@ public AwxResponse requestDeletion( var accessToken = authenticationProvider.getAccessToken(); var projectComponent = componentCatalogService.getProjectComponentById(accessToken, projectKey, componentId); + projectComponentDeletePermissionsValidator.validate(projectComponent); + String deletionWorkflowId = getDeletionWorkflowId(projectComponent); String deletionWorkflowName = getDeletionWorkflowName(projectComponent); String deletionWorkflowTimeoutSeconds = getDeletionWorkflowTimeoutSeconds(projectComponent); diff --git a/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidatorTest.java b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidatorTest.java new file mode 100644 index 0000000..c273342 --- /dev/null +++ b/src/test/java/org/opendevstack/component_provisioner/server/controllers/validators/ProjectComponentDeletePermissionsValidatorTest.java @@ -0,0 +1,32 @@ +package org.opendevstack.component_provisioner.server.controllers.validators; + +import org.junit.jupiter.api.Test; +import org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProjectComponentExtendedInfo; +import org.opendevstack.component_provisioner.server.controllers.exceptions.UserNotAllowedException; +import org.opendevstack.component_provisioner.server.model.ProjectComponentExtendedInfoMother; + +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ProjectComponentDeletePermissionsValidatorTest { + + private final ProjectComponentDeletePermissionsValidator validator = new ProjectComponentDeletePermissionsValidator(); + + @Test + public void givenProjectComponentNotDeletable_whenValidate_thenThrowsUserNotAllowedException() { + // given + ProjectComponentExtendedInfo projectComponentExtendedInfo = ProjectComponentExtendedInfoMother.valid(); + projectComponentExtendedInfo.setCanBeDeleted(false); + + // when / then + assertThrows(UserNotAllowedException.class, () -> validator.validate(projectComponentExtendedInfo)); + } + + @Test + public void givenProjectComponentDeletable_whenValidate_thenDoesNotThrow() { + // given + ProjectComponentExtendedInfo projectComponentExtendedInfo = ProjectComponentExtendedInfoMother.valid(); + + // when / then + validator.validate(projectComponentExtendedInfo); + } +} diff --git a/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacadeTest.java b/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacadeTest.java index 7b1e99f..2ab6e92 100644 --- a/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacadeTest.java +++ b/src/test/java/org/opendevstack/component_provisioner/server/facade/ProvisionResultsApiFacadeTest.java @@ -18,6 +18,7 @@ import org.opendevstack.component_provisioner.server.controllers.exceptions.SlugNotFoundException; import org.opendevstack.component_provisioner.server.controllers.validators.DeletionSentinelWorkflowValidator; import org.opendevstack.component_provisioner.server.controllers.validators.InputParamsValidator; +import org.opendevstack.component_provisioner.server.controllers.validators.ProjectComponentDeletePermissionsValidator; import org.opendevstack.component_provisioner.server.controllers.validators.WorkflowsValidator; import org.opendevstack.component_provisioner.server.mappers.EntitiesMapper; import org.opendevstack.component_provisioner.server.model.*; @@ -86,6 +87,9 @@ class ProvisionResultsApiFacadeTest { @Mock private InputParamsValidator inputParamsValidator; + @Mock + private ProjectComponentDeletePermissionsValidator projectComponentDeletePermissionsValidator; + @InjectMocks private ProvisionResultsApiFacade facade; @@ -1020,6 +1024,7 @@ private static ProjectComponentExtendedInfo buildProjectComponentWithDeletionCon .componentId(componentId) .status(status) .parameters(parameters) + .canBeDeleted(true) .build(); } diff --git a/src/test/java/org/opendevstack/component_provisioner/server/model/ProjectComponentExtendedInfoMother.java b/src/test/java/org/opendevstack/component_provisioner/server/model/ProjectComponentExtendedInfoMother.java index f329a3a..89556a4 100644 --- a/src/test/java/org/opendevstack/component_provisioner/server/model/ProjectComponentExtendedInfoMother.java +++ b/src/test/java/org/opendevstack/component_provisioner/server/model/ProjectComponentExtendedInfoMother.java @@ -18,6 +18,7 @@ public static ProjectComponentExtendedInfo valid() { .componentId("component-id") .catalogItemId("aHR0cDovL2JpdGJ1Y2tldC10ZXN0LmNvbQ") .catalogItemRef("L3JlZmVyZW5jZT9wYXJhbT0xMA") + .canBeDeleted(true) .status(org.opendevstack.component_provisioner.client.component_catalog.v1.model.ProvisioningStatus.CREATED) .componentUrl("https://example.com/component") .parameters(Collections.emptyList())