diff --git a/pom.xml b/pom.xml
index 539758091..fd816d729 100644
--- a/pom.xml
+++ b/pom.xml
@@ -46,7 +46,7 @@
11
0.0.0-MASTER-SNAPSHOT
- 0.0.0-MASTER-SNAPSHOT
+ 0.0.0-SED-4536-SNAPSHOT
5.0.4
diff --git a/step-controller/step-controller-remote-client/src/main/java/step/client/resources/RemoteResourceManager.java b/step-controller/step-controller-remote-client/src/main/java/step/client/resources/RemoteResourceManager.java
index d0d5dcee8..c44b486f2 100644
--- a/step-controller/step-controller-remote-client/src/main/java/step/client/resources/RemoteResourceManager.java
+++ b/step-controller/step-controller-remote-client/src/main/java/step/client/resources/RemoteResourceManager.java
@@ -228,6 +228,11 @@ public String getResourceName() {
return resource.getResourceName();
}
+ @Override
+ public Resource getResource() {
+ return resource;
+ }
+
@Override
public void close() throws IOException {
// TODO Auto-generated method stub
diff --git a/step-controller/step-controller-server/src/main/java/step/core/access/RoleProvider.java b/step-controller/step-controller-server/src/main/java/step/core/access/RoleProvider.java
index 3d9c5a279..2afd45e8e 100644
--- a/step-controller/step-controller-server/src/main/java/step/core/access/RoleProvider.java
+++ b/step-controller/step-controller-server/src/main/java/step/core/access/RoleProvider.java
@@ -19,8 +19,11 @@
package step.core.access;
import java.util.List;
+import java.util.Set;
public interface RoleProvider {
- public List getRoles();
+ List getRoles();
+
+ Set getAllRights();
}
diff --git a/step-controller/step-controller-server/src/main/java/step/core/deployment/AbstractStepServices.java b/step-controller/step-controller-server/src/main/java/step/core/deployment/AbstractStepServices.java
index 1b2189649..cc347c457 100644
--- a/step-controller/step-controller-server/src/main/java/step/core/deployment/AbstractStepServices.java
+++ b/step-controller/step-controller-server/src/main/java/step/core/deployment/AbstractStepServices.java
@@ -40,6 +40,12 @@
public abstract class AbstractStepServices extends AbstractServices {
public static final String SESSION = "session";
+ public static final String RIGHT_SEPARATOR = "-";
+ public static final String READ_RIGHT = "read";
+ public static final String WRITE_RIGHT = "write";
+ public static final String DELETE_RIGHT = "delete";
+ public static final String EXECUTE_RIGHT = "execute";
+
protected Configuration configuration;
@@ -118,6 +124,19 @@ protected void checkRightsOnBehalfOf(String right, String userOnBehalfOf) {
}
}
+ protected void checkRightIfDefined(String right) {
+ Session session = getSession();
+ try {
+ if (!getAuthorizationManager().checkRightInContextIfDefined(session, right)) {
+ User user = session.getUser();
+ throw new AuthorizationException("User " + (user == null ? "" : user.getUsername()) + " has no permission on '" + right + "'");
+ }
+ } catch (NotMemberOfProjectException ex) {
+ // if user is not a member of the project, we want to return 'access denied' error
+ throw new AuthorizationException(ex.getMessage());
+ }
+ }
+
/**
* The ObjectHookInterceptor.aroundReadFrom can only be used for POST request passing an entity as request BODY
* This method can be used as helper for all other cases where checking if the entity is editable in given context (i.e. DELETE request...(
diff --git a/step-controller/step-controller-server/src/main/java/step/resources/ResourceServices.java b/step-controller/step-controller-server/src/main/java/step/resources/ResourceServices.java
index 91dd0d45e..247d8c2ab 100644
--- a/step-controller/step-controller-server/src/main/java/step/resources/ResourceServices.java
+++ b/step-controller/step-controller-server/src/main/java/step/resources/ResourceServices.java
@@ -45,11 +45,16 @@
import java.io.InputStream;
import java.util.*;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
@Path("/resources")
@Tag(name = "Resources")
public class ResourceServices extends AbstractStepAsyncServices {
+ private static final String RESOURCE_RIGHT_NAME = "resource";
+ public static final String INVALID_ARGUMENTS_A_NON_NULL_RESOURCE_MUST_BE_PROVIDED = "Invalid arguments: a non null resource must be provided.";
+ public static final String INVALID_RESOURCE_THE_RESOURCE_HAS_NO_RESOURCE_TYPE_SET = "Invalid resource: the resource has no resourceType set.";
+
protected ResourceManager resourceManager;
private TableService tableService;
@@ -74,10 +79,25 @@ private void auditLog(String operation, Resource resource) {
AuditLogger.logEntityModification(getSession(), operation, "resources", resource.getId().toHexString(), entityName, attributes);
}
+ private void checkResourceTypeRight(String resourceType, String right) {
+ if (resourceType == null || resourceType.isEmpty()) {
+ throw new ControllerServiceException(INVALID_RESOURCE_THE_RESOURCE_HAS_NO_RESOURCE_TYPE_SET);
+ }
+ checkRightIfDefined(RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + resourceType + RIGHT_SEPARATOR + right);
+ }
+
+ private void checkResourceTypeRight(Resource resource, String right) {
+ //If a resource is null, right is granted too, it's not the role of this function to perform integrity check
+ if (resource == null) {
+ throw new ControllerServiceException(INVALID_ARGUMENTS_A_NON_NULL_RESOURCE_MUST_BE_PROVIDED);
+ } else {
+ checkResourceTypeRight(resource.getResourceType(), right);
+ }
+ }
@POST
@Path("/content")
- @Secured(right = "resource-write")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + WRITE_RIGHT)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public ResourceUploadResponse createResource(@FormDataParam("file") InputStream uploadedInputStream,
@@ -91,9 +111,10 @@ public ResourceUploadResponse createResource(@FormDataParam("file") InputStream
if (uploadedInputStream == null || fileDetail == null)
throw new RuntimeException("Invalid arguments");
- if (resourceType == null || resourceType.length() == 0)
+ if (resourceType == null || resourceType.isEmpty())
throw new RuntimeException("Missing resource type query parameter 'type'");
+ checkResourceTypeRight(resourceType, WRITE_RIGHT);
try {
Resource resource = resourceManager.createTrackedResource(
resourceType, isDirectory, uploadedInputStream, fileDetail.getFileName(), objectEnricher,
@@ -113,17 +134,27 @@ private ControllerServiceException uploadFileNotAnArchive() {
}
@POST
- @Secured(right = "resource-write")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + WRITE_RIGHT)
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Resource saveResource(Resource resource) throws IOException {
+ // Always check that we have write access to the new resource type, this automatically ensure the resource and its resourceType are not null
+ checkResourceTypeRight(resource, WRITE_RIGHT);
+ // When updating a resource, we need write access to both the original and new types
+ String resourceId = resource.getId().toHexString();
+ if (resourceManager.resourceExists(resourceId)) {
+ String originalResourceType = resourceManager.getResource(resourceId).getResourceType();
+ if (!resource.getResourceType().equals(originalResourceType)) {
+ checkResourceTypeRight(originalResourceType, WRITE_RIGHT);
+ }
+ }
auditLog("save", resource);
return resourceManager.saveResource(resource);
}
@POST
@Path("/{id}/content")
- @Secured(right = "resource-write")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + WRITE_RIGHT)
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
public ResourceUploadResponse saveResourceContent(@PathParam("id") String resourceId, @FormDataParam("file") InputStream uploadedInputStream,
@@ -131,6 +162,7 @@ public ResourceUploadResponse saveResourceContent(@PathParam("id") String resour
if (uploadedInputStream == null || fileDetail == null)
throw new RuntimeException("Invalid arguments");
+ checkResourceTypeRight(resourceManager.getResource(resourceId), WRITE_RIGHT);
try {
Resource resource = resourceManager.saveResourceContent(resourceId, uploadedInputStream, fileDetail.getFileName(), null, getSession().getUser().getUsername());
auditLog("save-content", resource);
@@ -143,11 +175,13 @@ public ResourceUploadResponse saveResourceContent(@PathParam("id") String resour
@GET
@Secured
@Path("/{id}")
- @Secured(right = "resource-read")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + READ_RIGHT)
@Produces(MediaType.APPLICATION_JSON)
public Resource getResource(@PathParam("id") String resourceId) throws IOException {
try {
- return resourceManager.getResource(resourceId);
+ Resource resource = resourceManager.getResource(resourceId);
+ checkResourceTypeRight(resource, READ_RIGHT);
+ return resource;
} catch (ResourceMissingException e) {
throw new ControllerServiceException(404, e.getMessage());
}
@@ -155,19 +189,21 @@ public Resource getResource(@PathParam("id") String resourceId) throws IOExcepti
@GET
@Path("/{id}/content")
- @Secured(right = "resource-read")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + READ_RIGHT)
@Produces(MediaType.APPLICATION_JSON)
public Response getResourceContent(@PathParam("id") String resourceId, @QueryParam("inline") boolean inline) throws IOException {
ResourceRevisionContent resourceContent = resourceManager.getResourceContent(resourceId);
+ checkResourceTypeRight(resourceContent.getResource(), READ_RIGHT);
return getResponseForResourceRevisionContent(resourceContent, inline);
}
@DELETE
@Secured
@Path("/{id}")
- @Secured(right = "resource-delete")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + DELETE_RIGHT)
public void deleteResource(@PathParam("id") String resourceId) {
Resource resource = resourceManager.getResource(resourceId);
+ checkResourceTypeRight(resource, DELETE_RIGHT);
assertEntityIsEditableInContext(resource);
auditLog("delete", resource);
resourceManager.deleteResource(resourceId);
@@ -176,9 +212,10 @@ public void deleteResource(@PathParam("id") String resourceId) {
@DELETE
@Secured
@Path("/{id}/revisions")
- @Secured(right = "resource-delete")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + DELETE_RIGHT)
public void deleteResourceRevisions(@PathParam("id") String resourceId) {
Resource resource = resourceManager.getResource(resourceId);
+ checkResourceTypeRight(resource, DELETE_RIGHT);
assertEntityIsEditableInContext(resource);
auditLog("delete-revisions", resource);
resourceManager.deleteResourceRevisionContent(resourceId);
@@ -207,9 +244,10 @@ public AsyncTaskStatus bulkDelete(TableBulkOperationRe
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/revision/{id}/content")
- @Secured(right = "resource-read")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + READ_RIGHT)
public Response getResourceRevisionContent(@PathParam("id") String resourceRevisionId, @QueryParam("inline") boolean inline) throws IOException {
ResourceRevisionContentImpl resourceContent = resourceManager.getResourceRevisionContent(resourceRevisionId);
+ checkResourceTypeRight(resourceContent.getResource(), READ_RIGHT);
return getResponseForResourceRevisionContent(resourceContent, inline);
}
@@ -217,9 +255,17 @@ public Response getResourceRevisionContent(@PathParam("id") String resourceRevis
@Path("/find")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
- @Secured(right = "resource-read")
+ @Secured(right = RESOURCE_RIGHT_NAME + RIGHT_SEPARATOR + READ_RIGHT)
public List findManyByCriteria(Map criteria) {
- return resourceManager.findManyByCriteria(criteria);
+ return resourceManager.findManyByCriteria(criteria).stream().filter(r -> {
+ try {
+ checkResourceTypeRight(r, READ_RIGHT);
+ return true;
+ } catch (Exception e) {
+ //we filter out the resources for which the user has no access
+ return false;
+ }
+ }).collect(Collectors.toList());
}
protected Response getResponseForResourceRevisionContent(ResourceRevisionContent resourceContent, boolean inline) {
diff --git a/step-core/src/main/java/step/resources/ResourceRevisionContent.java b/step-core/src/main/java/step/resources/ResourceRevisionContent.java
index 55f219b8a..c8873afde 100644
--- a/step-core/src/main/java/step/resources/ResourceRevisionContent.java
+++ b/step-core/src/main/java/step/resources/ResourceRevisionContent.java
@@ -27,6 +27,8 @@ public interface ResourceRevisionContent {
String getResourceName();
+ Resource getResource();
+
void close() throws IOException;
}
diff --git a/step-core/src/main/java/step/resources/ResourceRevisionContentImpl.java b/step-core/src/main/java/step/resources/ResourceRevisionContentImpl.java
index c3aece366..d2c2a1053 100644
--- a/step-core/src/main/java/step/resources/ResourceRevisionContentImpl.java
+++ b/step-core/src/main/java/step/resources/ResourceRevisionContentImpl.java
@@ -48,6 +48,10 @@ public String getResourceName() {
return resourceName;
}
+ public Resource getResource() {
+ return resource;
+ }
+
@Override
public void close() throws IOException {
resourceStream.close();