diff --git a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java index 4fdee04b83..b3f1fbaae1 100644 --- a/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java +++ b/libraries/datahandler/src/main/java/org/eclipse/sw360/datahandler/permissions/PermissionUtils.java @@ -9,8 +9,7 @@ */ package org.eclipse.sw360.datahandler.permissions; -import java.util.Properties; -import java.util.Set; +import java.util.*; import org.eclipse.sw360.datahandler.common.CommonUtils; import org.eclipse.sw360.datahandler.common.DatabaseSettings; @@ -18,6 +17,7 @@ import org.eclipse.sw360.datahandler.thrift.components.Release; import org.eclipse.sw360.datahandler.thrift.licenses.License; import org.eclipse.sw360.datahandler.thrift.projects.Project; +import org.eclipse.sw360.datahandler.thrift.projects.ProjectClearingState; import org.eclipse.sw360.datahandler.thrift.users.User; import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; @@ -174,4 +174,44 @@ public static DocumentPermissions makePermission(T document, User user) { } } + public static Set editableParmsSet = new HashSet<>(); + static { + editableParmsSet.add("enableSvm"); + editableParmsSet.add("enableVulnerabilitiesDisplay"); + editableParmsSet.add("projectManager"); + editableParmsSet.add("projectOwner"); + editableParmsSet.add("securityResponsibles"); + editableParmsSet.add("externalIds"); + editableParmsSet.add("state"); + editableParmsSet.add("phaseOutSince"); + } + + public static boolean checkEditablePermission(String name, User user, Map reqBodyMap, Project sw360Project) { + if (!name.equals(ProjectClearingState.CLOSED.name()) || PermissionUtils.isAdmin(user)) { + return true; + } else { + if ((reqBodyMap.containsKey("attachments") || reqBodyMap.containsKey("obligationsText") + || reqBodyMap.containsKey("linkedObligationId")) && !PermissionUtils.isAdmin(user)) { + return false; + } + String createdBy = sw360Project.getCreatedBy(); + String projectResponsible = sw360Project.getProjectResponsible(); + Set projModerators = sw360Project.getModerators(); + Set projContributors = sw360Project.getContributors(); + String leadArchitect = sw360Project.getLeadArchitect(); + Optional match = editableParmsSet.stream() + .filter(reqBodyMap::containsKey) + .findAny(); + if (match.isPresent() && (PermissionUtils.isAdmin(user) + || PermissionUtils.isClearingAdmin(user) + || user.getUserGroup().name().equalsIgnoreCase(UserGroup.CLEARING_EXPERT.name()) + || PermissionUtils.isClearingExpert(user)) || user.getEmail().equals(createdBy) + || user.getEmail().equals(projectResponsible) || user.getEmail().equals(leadArchitect) + || projModerators.contains(user.getEmail()) || projContributors.contains(user.getEmail())) { + return true; + } + } + return false; + } + } diff --git a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java index 904778dbb4..432695f495 100644 --- a/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java +++ b/rest/resource-server/src/main/java/org/eclipse/sw360/rest/resourceserver/project/ProjectController.java @@ -90,6 +90,7 @@ import org.eclipse.sw360.datahandler.thrift.projects.ProjectDTO; import org.eclipse.sw360.datahandler.thrift.projects.ClearingRequest; import org.eclipse.sw360.datahandler.thrift.users.User; +import org.eclipse.sw360.datahandler.thrift.users.UserGroup; import org.eclipse.sw360.datahandler.thrift.vendors.Vendor; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.ProjectVulnerabilityRating; import org.eclipse.sw360.datahandler.thrift.vulnerabilities.VulnerabilityCheckStatus; @@ -125,6 +126,7 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.FileCopyUtils; import org.springframework.web.bind.annotation.PathVariable; @@ -1573,21 +1575,31 @@ public ResponseEntity> patchProject( @Parameter(description = "Updated values", schema = @Schema(implementation = Project.class)) @RequestBody Map reqBodyMap ) throws TException { - User user = restControllerHelper.getSw360UserFromAuthentication(); - Project sw360Project = projectService.getProjectForUserById(id, user); - Project updateProject = convertToProject(reqBodyMap); - updateProject.unsetReleaseRelationNetwork(); - sw360Project = this.restControllerHelper.updateProject(sw360Project, updateProject, reqBodyMap, mapOfProjectFieldsToRequestBody); - if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP && updateProject.getReleaseIdToUsage() != null) { - sw360Project.unsetReleaseRelationNetwork(); - projectService.syncReleaseRelationNetworkAndReleaseIdToUsage(sw360Project, user); + try{ + User user = restControllerHelper.getSw360UserFromAuthentication(); + Project sw360Project = projectService.getProjectForUserById(id, user); + boolean editPermitted = PermissionUtils.checkEditablePermission(sw360Project.getClearingState().name(),user,reqBodyMap, sw360Project); + if (!editPermitted) { + throw new AccessDeniedException("No write permission for project"); + } + Project updateProject = convertToProject(reqBodyMap); + updateProject.unsetReleaseRelationNetwork(); + sw360Project = this.restControllerHelper.updateProject(sw360Project, updateProject, reqBodyMap, mapOfProjectFieldsToRequestBody); + if (SW360Constants.ENABLE_FLEXIBLE_PROJECT_RELEASE_RELATIONSHIP && updateProject.getReleaseIdToUsage() != null) { + sw360Project.unsetReleaseRelationNetwork(); + projectService.syncReleaseRelationNetworkAndReleaseIdToUsage(sw360Project, user); + } + RequestStatus updateProjectStatus = projectService.updateProject(sw360Project, user); + HalResource userHalResource = createHalProject(sw360Project, user); + if (updateProjectStatus == RequestStatus.SENT_TO_MODERATOR) { + return new ResponseEntity(RESPONSE_BODY_FOR_MODERATION_REQUEST, HttpStatus.ACCEPTED); + } + return new ResponseEntity<>(userHalResource, HttpStatus.OK); } - RequestStatus updateProjectStatus = projectService.updateProject(sw360Project, user); - HalResource userHalResource = createHalProject(sw360Project, user); - if (updateProjectStatus == RequestStatus.SENT_TO_MODERATOR) { - return new ResponseEntity(RESPONSE_BODY_FOR_MODERATION_REQUEST, HttpStatus.ACCEPTED); + catch (Exception sw360Exception) { + log.error(sw360Exception.getMessage()); + throw new AccessDeniedException("No write permission for project"); } - return new ResponseEntity<>(userHalResource, HttpStatus.OK); } @Operation( diff --git a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java index 025bd33d05..d1e5cd6a42 100644 --- a/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java +++ b/rest/resource-server/src/test/java/org/eclipse/sw360/rest/resourceserver/restdocs/ProjectSpecTest.java @@ -1957,6 +1957,7 @@ public void should_document_create_duplicate_project() throws Exception { @Test public void should_document_update_project() throws Exception { + project.setClearingState(ProjectClearingState.OPEN); Project updateProject = new Project(); updateProject.setName("updated project"); updateProject.setDescription("Project description updated"); @@ -1964,7 +1965,7 @@ public void should_document_update_project() throws Exception { updateProject.setState(ProjectState.PHASE_OUT); updateProject.setPhaseOutSince("2020-06-24"); this.mockMvc - .perform(patch("/api/projects/376576").contentType(MediaTypes.HAL_JSON) + .perform(patch("/api/projects/"+project.getId()).contentType(MediaTypes.HAL_JSON) .content(this.objectMapper.writeValueAsString(updateProject)) .header("Authorization", TestHelper.generateAuthHeader(testUserId, testUserPassword)).accept(MediaTypes.HAL_JSON)) .andExpect(status().isOk()) @@ -2040,6 +2041,7 @@ public void should_document_update_project() throws Exception { subsectionWithPath("externalUrls").description("A place to store additional data used by external URLs"), fieldWithPath("considerReleasesFromExternalList").description("Consider list of releases from existing external list"), fieldWithPath("enableVulnerabilitiesDisplay").description("Displaying vulnerabilities flag."), + fieldWithPath("clearingState").description("The clearingState of the project"), subsectionWithPath("_embedded.sw360:moderators").description("An array of moderators"), subsectionWithPath("_embedded.sw360:projects").description("An array of <>"), subsectionWithPath("_embedded.sw360:releases").description("An array of <>"),