From 77e87f7cc92142f0d78a7b761cf3bf6cc5599604 Mon Sep 17 00:00:00 2001 From: carlosjepard Date: Thu, 7 Dec 2023 12:04:22 +0000 Subject: [PATCH] Added permissions configuration for newly created AIPs --- .../org/roda/core/model/ModelService.java | 12 +- .../org/roda/core/plugins/PluginHelper.java | 21 ++- .../plugins/base/ingest/BagitToAIPPlugin.java | 4 +- .../base/ingest/BagitToAIPPluginUtils.java | 14 +- .../base/ingest/EARKSIP2ToAIPPlugin.java | 6 +- .../base/ingest/EARKSIP2ToAIPPluginUtils.java | 24 +-- .../base/ingest/EARKSIPToAIPPlugin.java | 6 +- .../base/ingest/EARKSIPToAIPPluginUtils.java | 26 +-- .../plugins/base/ingest/PermissionUtils.java | 176 ++++++++++++------ .../TransferredResourceToAIPPlugin.java | 17 +- .../resources/config/roda-core.properties | 48 +++++ roda-ui/roda-wui/pom.xml | 4 + .../org/roda/wui/api/controllers/Browser.java | 18 +- 13 files changed, 255 insertions(+), 121 deletions(-) diff --git a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java index d91c55b218..87f6e0f6c5 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/model/ModelService.java @@ -108,7 +108,6 @@ import org.roda.core.data.v2.ip.metadata.PreservationMetadata; import org.roda.core.data.v2.ip.metadata.PreservationMetadata.PreservationMetadataType; import org.roda.core.data.v2.jobs.Job; -import org.roda.core.data.v2.jobs.PluginInfo; import org.roda.core.data.v2.jobs.PluginState; import org.roda.core.data.v2.jobs.Report; import org.roda.core.data.v2.log.LogEntry; @@ -131,8 +130,6 @@ import org.roda.core.model.utils.ResourceListUtils; import org.roda.core.model.utils.ResourceParseUtils; import org.roda.core.model.utils.UserUtility; -import org.roda.core.plugins.Plugin; -import org.roda.core.plugins.PluginHelper; import org.roda.core.storage.Binary; import org.roda.core.storage.BinaryVersion; import org.roda.core.storage.ContentPayload; @@ -352,9 +349,8 @@ public AIP createAIP(String parentId, String type, Permissions permissions, List AIPState state = AIPState.ACTIVE; Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP)); String id = directory.getStoragePath().getName(); - Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId); - AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy); + AIP aip = new AIP(id, parentId, type, state, permissions, createdBy); aip.setGhost(isGhost); aip.setIngestSIPIds(ingestSIPIds); @@ -394,9 +390,8 @@ public AIP createAIP(AIPState state, String parentId, String type, Permissions p Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP)); String id = directory.getStoragePath().getName(); - Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId); - AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy); + AIP aip = new AIP(id, parentId, type, state, permissions, createdBy); // Instance Id Management aip.setInstanceId(RODAInstanceUtils.getLocalInstanceIdentifier()); createAIPMetadata(aip); @@ -415,9 +410,8 @@ public AIP createAIP(AIPState state, String parentId, String type, Permissions p Directory directory = storage.createRandomDirectory(DefaultStoragePath.parse(RodaConstants.STORAGE_CONTAINER_AIP)); String id = directory.getStoragePath().getName(); - Permissions inheritedPermissions = this.addParentPermissions(permissions, parentId); - AIP aip = new AIP(id, parentId, type, state, inheritedPermissions, createdBy).setIngestSIPIds(ingestSIPIds) + AIP aip = new AIP(id, parentId, type, state, permissions, createdBy).setIngestSIPIds(ingestSIPIds) .setIngestJobId(ingestJobId).setIngestSIPUUID(ingestSIPUUID); // Instance Id Management aip.setInstanceId(RODAInstanceUtils.getLocalInstanceIdentifier()); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java index 3334c392ba..13aa7bebed 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/PluginHelper.java @@ -19,7 +19,6 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -85,12 +84,14 @@ import org.roda.core.data.v2.risks.Risk; import org.roda.core.data.v2.risks.RiskIncidence; import org.roda.core.data.v2.user.RODAMember; +import org.roda.core.data.v2.user.User; import org.roda.core.data.v2.validation.ValidationException; import org.roda.core.index.IndexService; import org.roda.core.index.utils.IterableIndexResult; import org.roda.core.model.LiteRODAObjectFactory; import org.roda.core.model.ModelService; import org.roda.core.model.utils.ModelUtils; +import org.roda.core.plugins.base.ingest.PermissionUtils; import org.roda.core.plugins.base.maintenance.reindex.ReindexAIPPlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexActionLogPlugin; import org.roda.core.plugins.base.maintenance.reindex.ReindexDIPPlugin; @@ -156,8 +157,7 @@ public static Report processObjects(Plugin plugin, } catch (Throwable e) { LOGGER.error("Unexpected exception during 'objectsLogic' execution", e); jobPluginInfo.setSourceObjectsProcessedWithFailure( - jobPluginInfo.getSourceObjectsCount() - - jobPluginInfo.getSourceObjectsProcessedWithSuccess()); + jobPluginInfo.getSourceObjectsCount() - jobPluginInfo.getSourceObjectsProcessedWithSuccess()); exceptionOccurred = e; } } @@ -793,13 +793,18 @@ private static Optional createGhost(String ancestor, Optional pa String username = getJobUsername(jobId, index); Permissions permissions = new Permissions(); - permissions.setUserPermissions(username, - new HashSet<>(Arrays.asList(Permissions.PermissionType.CREATE, Permissions.PermissionType.READ, - Permissions.PermissionType.UPDATE, Permissions.PermissionType.DELETE, Permissions.PermissionType.GRANT))); + + User user = model.retrieveUser(username); + + if (parent.isPresent()) { + permissions = model.retrieveAIP(parent.get()).getPermissions(); + } + + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); boolean isGhost = true; - AIP ghostAIP = model.createAIP(parent.orElse(null), "", permissions, Arrays.asList(ancestor), jobId, true, username, - isGhost); + AIP ghostAIP = model.createAIP(parent.orElse(null), "", finalPermissions, Arrays.asList(ancestor), jobId, true, + username, isGhost); return Optional.ofNullable(ghostAIP.getId()); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPlugin.java index 123402d665..dbcc519cff 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPlugin.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPlugin.java @@ -28,9 +28,9 @@ import org.roda.core.model.ModelService; import org.roda.core.plugins.Plugin; import org.roda.core.plugins.PluginException; +import org.roda.core.plugins.PluginHelper; import org.roda.core.plugins.RODAObjectProcessingLogic; import org.roda.core.plugins.orchestrate.JobPluginInfo; -import org.roda.core.plugins.PluginHelper; import org.roda.core.storage.StorageService; import org.roda_project.commons_ip.model.ParseException; import org.roda_project.commons_ip.model.SIP; @@ -118,7 +118,7 @@ private void processTransferredResource(IndexService index, ModelService model, AIP aipCreated = BagitToAIPPluginUtils.bagitToAip(bagit, model, METADATA_FILE, Arrays.asList(transferredResource.getName()), reportItem.getJobId(), computedParentId, job.getUsername(), - PermissionUtils.getIngestPermissions(job.getUsername()), transferredResource.getUUID()); + transferredResource.getUUID()); PluginHelper.createSubmission(model, createSubmission, bagitPath, aipCreated.getId()); diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPluginUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPluginUtils.java index bb15c53c3f..4b0a5aeab2 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPluginUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/BagitToAIPPluginUtils.java @@ -20,6 +20,7 @@ import org.roda.core.data.v2.ip.AIPState; import org.roda.core.data.v2.ip.Permissions; import org.roda.core.data.v2.ip.Representation; +import org.roda.core.data.v2.user.User; import org.roda.core.model.ModelService; import org.roda.core.storage.ContentPayload; import org.roda.core.storage.StringContentPayload; @@ -41,7 +42,7 @@ private BagitToAIPPluginUtils() { } public static AIP bagitToAip(SIP bagit, ModelService model, String metadataFilename, List ingestSIPIds, - String ingestJobId, Optional computedParentId, String createdBy, Permissions permissions, + String ingestJobId, Optional computedParentId, String createdBy, String ingestSIPUUID) throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException, AuthorizationDeniedException { @@ -52,9 +53,18 @@ public static AIP bagitToAip(SIP bagit, ModelService model, String metadataFilen AIPState state = AIPState.INGEST_PROCESSING; String aipType = RodaConstants.AIP_TYPE_MIXED; + Permissions permissions = new Permissions(); boolean notify = false; - AIP aip = model.createAIP(state, computedParentId.orElse(null), aipType, permissions, ingestSIPUUID, ingestSIPIds, + User user = model.retrieveUser(createdBy); + + if (computedParentId.isPresent()){ + permissions = model.retrieveAIP(computedParentId.get()).getPermissions(); + } + + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); + + AIP aip = model.createAIP(state, computedParentId.orElse(null), aipType, finalPermissions, ingestSIPUUID, ingestSIPIds, ingestJobId, notify, createdBy); model.createDescriptiveMetadata(aip.getId(), metadataFilename, metadataAsPayload, METADATA_TYPE, METADATA_VERSION, diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPlugin.java index 158a0c7ba1..c31946b910 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPlugin.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPlugin.java @@ -47,9 +47,9 @@ import org.roda.core.model.ModelService; import org.roda.core.plugins.Plugin; import org.roda.core.plugins.PluginException; +import org.roda.core.plugins.PluginHelper; import org.roda.core.plugins.RODAObjectProcessingLogic; import org.roda.core.plugins.orchestrate.JobPluginInfo; -import org.roda.core.plugins.PluginHelper; import org.roda.core.storage.StorageService; import org.roda.core.storage.fs.FSUtils; import org.roda_project.commons_ip.model.ParseException; @@ -203,8 +203,8 @@ private AIP processNewSIP(IndexService index, ModelService model, Report reportI throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException, ValidationException, IOException, LockingException { String jobUsername = PluginHelper.getJobUsername(this, index); - return EARKSIP2ToAIPPluginUtils.earkSIPToAIP(sip, jobUsername, PermissionUtils.getIngestPermissions(jobUsername), - model, sip.getIds(), reportItem.getJobId(), computedParentId, ingestSIPUUID, this); + return EARKSIP2ToAIPPluginUtils.earkSIPToAIP(sip, jobUsername, model, sip.getIds(), reportItem.getJobId(), + computedParentId, ingestSIPUUID, this); } private AIP processUpdateSIP(IndexService index, ModelService model, StorageService storage, SIP sip, diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPluginUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPluginUtils.java index 9f67d0e0d6..f791ca3904 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPluginUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIP2ToAIPPluginUtils.java @@ -7,7 +7,6 @@ */ package org.roda.core.plugins.base.ingest; -import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.HashMap; @@ -34,6 +33,7 @@ import org.roda.core.data.v2.ip.StoragePath; import org.roda.core.data.v2.ip.metadata.PreservationMetadata.PreservationMetadataType; import org.roda.core.data.v2.jobs.Report; +import org.roda.core.data.v2.user.User; import org.roda.core.data.v2.validation.ValidationException; import org.roda.core.model.ModelService; import org.roda.core.model.utils.ModelUtils; @@ -62,10 +62,10 @@ private EARKSIP2ToAIPPluginUtils() { // do nothing } - public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermissions, ModelService model, - List ingestSIPIds, String ingestJobId, Optional parentId, String ingestSIPUUID, Plugin plugin) + public static AIP earkSIPToAIP(SIP sip, String username, ModelService model, List ingestSIPIds, + String ingestJobId, Optional parentId, String ingestSIPUUID, Plugin plugin) throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException, - AuthorizationDeniedException, ValidationException, IOException, LockingException { + AuthorizationDeniedException, ValidationException, LockingException { AIPState state = AIPState.INGEST_PROCESSING; Permissions permissions = new Permissions(); @@ -73,7 +73,15 @@ public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermiss String aipType = getType(sip); - AIP aip = model.createAIP(state, parentId.orElse(null), aipType, permissions, ingestSIPUUID, ingestSIPIds, + User user = model.retrieveUser(username); + + if (parentId.isPresent()){ + permissions = model.retrieveAIP(parentId.get()).getPermissions(); + } + + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); + + AIP aip = model.createAIP(state, parentId.orElse(null), aipType, finalPermissions, ingestSIPUUID, ingestSIPIds, ingestJobId, notify, username); PluginHelper.acquireObjectLock(aip, plugin); @@ -90,12 +98,6 @@ public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermiss // update the AIP metadata AIP createdAIP = model.retrieveAIP(aip.getId()); - // Set Permissions - Permissions readPermissions = PermissionUtils.grantReadPermissionToUserGroup(model, createdAIP, - aip.getPermissions()); - Permissions finalPermissions = PermissionUtils.grantAllPermissions(username, readPermissions, fullPermissions); - createdAIP.setPermissions(finalPermissions); - return model.updateAIP(createdAIP, username); } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPlugin.java index fd3bfa83f4..a00d933d08 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPlugin.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPlugin.java @@ -47,9 +47,9 @@ import org.roda.core.model.ModelService; import org.roda.core.plugins.Plugin; import org.roda.core.plugins.PluginException; +import org.roda.core.plugins.PluginHelper; import org.roda.core.plugins.RODAObjectProcessingLogic; import org.roda.core.plugins.orchestrate.JobPluginInfo; -import org.roda.core.plugins.PluginHelper; import org.roda.core.storage.StorageService; import org.roda.core.storage.fs.FSUtils; import org.roda_project.commons_ip.model.ParseException; @@ -204,8 +204,8 @@ private AIP processNewSIP(IndexService index, ModelService model, Report reportI throws NotFoundException, GenericException, RequestNotValidException, AuthorizationDeniedException, AlreadyExistsException, ValidationException, IOException, LockingException { String jobUsername = PluginHelper.getJobUsername(this, index); - return EARKSIPToAIPPluginUtils.earkSIPToAIP(sip, jobUsername, PermissionUtils.getIngestPermissions(jobUsername), - model, sip.getIds(), reportItem.getJobId(), computedParentId, ingestSIPUUID, this); + return EARKSIPToAIPPluginUtils.earkSIPToAIP(sip, jobUsername, model, sip.getIds(), reportItem.getJobId(), + computedParentId, ingestSIPUUID, this); } private AIP processUpdateSIP(IndexService index, ModelService model, StorageService storage, SIP sip, diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPluginUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPluginUtils.java index 75b11edbba..aad620c313 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPluginUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/EARKSIPToAIPPluginUtils.java @@ -7,7 +7,6 @@ */ package org.roda.core.plugins.base.ingest; -import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -29,6 +28,7 @@ import org.roda.core.data.v2.ip.Representation; import org.roda.core.data.v2.ip.metadata.PreservationMetadata.PreservationMetadataType; import org.roda.core.data.v2.jobs.Report; +import org.roda.core.data.v2.user.User; import org.roda.core.data.v2.validation.ValidationException; import org.roda.core.model.ModelService; import org.roda.core.plugins.Plugin; @@ -52,10 +52,10 @@ private EARKSIPToAIPPluginUtils() { // do nothing } - public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermissions, ModelService model, - List ingestSIPIds, String ingestJobId, Optional parentId, String ingestSIPUUID, Plugin plugin) + public static AIP earkSIPToAIP(SIP sip, String username, ModelService model, List ingestSIPIds, + String ingestJobId, Optional parentId, String ingestSIPUUID, Plugin plugin) throws RequestNotValidException, NotFoundException, GenericException, AlreadyExistsException, - AuthorizationDeniedException, ValidationException, IOException, LockingException { + AuthorizationDeniedException, ValidationException, LockingException { AIPState state = AIPState.INGEST_PROCESSING; Permissions permissions = new Permissions(); @@ -63,7 +63,15 @@ public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermiss String aipType = getType(sip); - AIP aip = model.createAIP(state, parentId.orElse(null), aipType, permissions, ingestSIPUUID, ingestSIPIds, + User user = model.retrieveUser(username); + + if (parentId.isPresent()){ + permissions = model.retrieveAIP(parentId.get()).getPermissions(); + } + + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); + + AIP aip = model.createAIP(state, parentId.orElse(null), aipType, finalPermissions, ingestSIPUUID, ingestSIPIds, ingestJobId, notify, username); PluginHelper.acquireObjectLock(aip, plugin); @@ -76,17 +84,13 @@ public static AIP earkSIPToAIP(SIP sip, String username, Permissions fullPermiss processIPRepresentationInformation(model, representation, aip.getId(), notify, false, username, null); } + // INFO 20190509 hsilva: this is required as the previous instructions // update the AIP metadata AIP createdAIP = model.retrieveAIP(aip.getId()); - // Set Permissions - Permissions readPermissions = PermissionUtils.grantReadPermissionToUserGroup(model, createdAIP, - aip.getPermissions()); - Permissions finalPermissions = PermissionUtils.grantAllPermissions(username, readPermissions, fullPermissions); - createdAIP.setPermissions(finalPermissions); - return model.updateAIP(createdAIP, username); + } public static AIP earkSIPToAIPUpdate(SIP sip, IndexedAIP indexedAIP, ModelService model, StorageService storage, diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/PermissionUtils.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/PermissionUtils.java index ad0f3bcb65..10745f5a5c 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/PermissionUtils.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/PermissionUtils.java @@ -7,28 +7,20 @@ */ package org.roda.core.plugins.base.ingest; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.apache.commons.lang.StringUtils; import org.roda.core.RodaCoreFactory; -import org.roda.core.common.XMLUtility; -import org.roda.core.data.common.RodaConstants; import org.roda.core.data.exceptions.AuthorizationDeniedException; import org.roda.core.data.exceptions.GenericException; -import org.roda.core.data.exceptions.NotFoundException; -import org.roda.core.data.exceptions.RequestNotValidException; -import org.roda.core.data.v2.ip.AIP; +import org.roda.core.data.exceptions.RODAException; import org.roda.core.data.v2.ip.Permissions; -import org.roda.core.data.v2.ip.metadata.DescriptiveMetadata; -import org.roda.core.model.ModelService; -import org.roda.core.storage.Binary; +import org.roda.core.data.v2.user.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author Andre Pereira apereira@keep.pt @@ -36,69 +28,129 @@ */ public class PermissionUtils { + private static final Logger LOGGER = LoggerFactory.getLogger(PermissionUtils.class); + private PermissionUtils() { // do nothing } - public static Permissions grantReadPermissionToUserGroup(ModelService model, AIP aip, Permissions permissions) - throws RequestNotValidException, GenericException, NotFoundException, AuthorizationDeniedException, IOException { - List descriptiveMetadataList = aip.getDescriptiveMetadata(); - Set readPermissionToUserGroup = new HashSet<>(); - - String xpath = RodaCoreFactory.getRodaConfigurationAsString("core", "permissions", "xpath"); - String freeAccessTerm = RodaCoreFactory.getRodaConfigurationAsString("core", "permissions", "freeaccess"); - - if (StringUtils.isNotBlank(xpath) && StringUtils.isNotBlank(freeAccessTerm)) { - for (DescriptiveMetadata descriptiveMetadata : descriptiveMetadataList) { - Binary descriptiveMetadataBinary = model.retrieveDescriptiveMetadataBinary(aip.getId(), - descriptiveMetadata.getId()); - - try (InputStream createInputStream = descriptiveMetadataBinary.getContent().createInputStream()) { - String useRestrict = XMLUtility.getString(createInputStream, xpath); - if (useRestrict.equals(freeAccessTerm)) { - readPermissionToUserGroup.add(Permissions.PermissionType.READ); - permissions.setGroupPermissions(RodaConstants.OBJECT_PERMISSIONS_USER_GROUP, readPermissionToUserGroup); - } - } + public static Permissions calculatePermissions(User user, Optional inheritedPermissions) + throws GenericException { + + Permissions finalPermissions = new Permissions(); + + Set creatorGroups = user.getGroups(); + + String creatorUsername = user.getId(); + + // get inherited permissions + if (inheritedPermissions.isPresent()) { + + for (String name : inheritedPermissions.get().getUsernames()) { + finalPermissions.setUserPermissions(name, inheritedPermissions.get().getUserPermissions(name)); } + for (String name : inheritedPermissions.get().getGroupnames()) { + finalPermissions.setGroupPermissions(name, inheritedPermissions.get().getGroupPermissions(name)); + } + } - return permissions; - } + // SET administrators permissions + List adminGroups = RodaCoreFactory.getRodaConfigurationAsList("core.aip.default_permissions.admin.group[]"); + + for (String name : adminGroups) { + finalPermissions.setGroupPermissions(name, + RodaCoreFactory + .getRodaConfigurationAsList("core.aip.default_permissions.admin.group[]." + name + ".permission[]").stream() + .map(Permissions.PermissionType::valueOf) // Convert string to PermissionType enum + .collect(Collectors.toSet())); + } + + // default creator Permissions + Set defaultCreatorPermissions = RodaCoreFactory + .getRodaConfigurationAsList("core.aip.default_permissions.creator.permission[]").stream() + .map(Permissions.PermissionType::valueOf) // Convert string to PermissionType enum + .collect(Collectors.toSet()); + + // defaultPermissions + Permissions defaultPermissions = new Permissions(); - public static Permissions grantAllPermissions(String username, Permissions permissions, Permissions parentPermissions) - throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException { - Permissions grantedPermissions = grantPermissionToUser(username, permissions); + // add default users + List defaultUsers = RodaCoreFactory.getRodaConfigurationAsList("core.aip.default_permissions.users[]"); - for (String name : parentPermissions.getUsernames()) { - grantedPermissions.setUserPermissions(name, parentPermissions.getUserPermissions(name)); + for (String name : defaultUsers) { + defaultPermissions.setUserPermissions(name, + RodaCoreFactory.getRodaConfigurationAsList("core.aip.default_permissions.users[]." + name + ".permission[]") + .stream().map(Permissions.PermissionType::valueOf) // Convert string to PermissionType enum + .collect(Collectors.toSet())); } - for (String name : parentPermissions.getGroupnames()) { - grantedPermissions.setGroupPermissions(name, parentPermissions.getGroupPermissions(name)); + // add default groups + List defaultGroups = RodaCoreFactory.getRodaConfigurationAsList("core.aip.default_permissions.group[]"); + + for (String name : defaultGroups) { + defaultPermissions.setGroupPermissions(name, + RodaCoreFactory.getRodaConfigurationAsList("core.aip.default_permissions.group[]." + name + ".permission[]") + .stream().map(Permissions.PermissionType::valueOf) // Convert string to PermissionType enum + .collect(Collectors.toSet())); } - return grantedPermissions; - } + // add default User permissions + for (String name : defaultPermissions.getUsernames()) { + Set temp = finalPermissions.getUserPermissions(name); + temp.addAll(defaultPermissions.getUserPermissions(name)); + finalPermissions.setUserPermissions(name, temp); + } - private static Permissions grantPermissionToUser(String username, Permissions permissions) - throws GenericException, NotFoundException, RequestNotValidException, AuthorizationDeniedException { - Set allPermissions = Stream - .of(Permissions.PermissionType.CREATE, Permissions.PermissionType.DELETE, Permissions.PermissionType.GRANT, - Permissions.PermissionType.READ, Permissions.PermissionType.UPDATE) - .collect(Collectors.toSet()); - permissions.setUserPermissions(username, allPermissions); - return permissions; - } + // add default creator permissions + Set temp = finalPermissions.getUserPermissions(creatorUsername); + temp.addAll(defaultCreatorPermissions); + finalPermissions.setUserPermissions(creatorUsername, temp); + + // intersection + boolean intersection = RodaCoreFactory.getProperty("core.aip.default_permissions.intersect_groups", false); + + // configuration + if (intersection) { + // add default groups intercepted with creator groups + Set interceptedGroups = new HashSet<>(defaultPermissions.getGroupnames()); + // intercept creator groups with config groups + interceptedGroups.retainAll(creatorGroups); + + for (String name : interceptedGroups) { + Set tempGroups = finalPermissions.getGroupPermissions(name); + tempGroups.addAll(defaultPermissions.getGroupPermissions(name)); + finalPermissions.setGroupPermissions(name, tempGroups); + } + + } else { + // add default groups without interception + for (String name : defaultPermissions.getGroupnames()) { + Set tempGroups = finalPermissions.getGroupPermissions(name); + tempGroups.addAll(defaultPermissions.getGroupPermissions(name)); + finalPermissions.setGroupPermissions(name, tempGroups); + } + } + + // check if user has must have permissions to create the aip or the sublevel aip + Set mustHavePermissions = RodaCoreFactory + .getRodaConfigurationAsList("core.aip.default_permissions.creator.minimum.permissions[]").stream() + .map(Permissions.PermissionType::valueOf).collect(Collectors.toSet()); + + Set creatorPermissions = creatorGroups.stream() + .map(g -> finalPermissions.getGroupPermissions(g)).flatMap(Set::stream).collect(Collectors.toSet()); + creatorPermissions.addAll(finalPermissions.getUserPermissions(creatorUsername)); + + if(mustHavePermissions.isEmpty()) { + LOGGER.error("Minimum set of permissions is empty!"); + throw new GenericException("Configuration issue, please contact administrator"); + } else if (!creatorPermissions.containsAll(mustHavePermissions)) { + finalPermissions.setUserPermissions(creatorUsername, mustHavePermissions); + } + + LOGGER.warn("Permissions have been set"); + + return finalPermissions; - public static Permissions getIngestPermissions(String username) { - Permissions permissions = new Permissions(); - permissions.setUserPermissions(username, - new HashSet<>(Arrays.asList(Permissions.PermissionType.CREATE, Permissions.PermissionType.READ, - Permissions.PermissionType.UPDATE, Permissions.PermissionType.DELETE, Permissions.PermissionType.GRANT))); - permissions.setGroupPermissions(RodaConstants.ADMINISTRATORS, - new HashSet<>(Arrays.asList(Permissions.PermissionType.CREATE, Permissions.PermissionType.READ, - Permissions.PermissionType.UPDATE, Permissions.PermissionType.DELETE, Permissions.PermissionType.GRANT))); - return permissions; } } diff --git a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/TransferredResourceToAIPPlugin.java b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/TransferredResourceToAIPPlugin.java index b8b74fee14..6c49f1d237 100644 --- a/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/TransferredResourceToAIPPlugin.java +++ b/roda-core/roda-core/src/main/java/org/roda/core/plugins/base/ingest/TransferredResourceToAIPPlugin.java @@ -30,10 +30,12 @@ import org.roda.core.data.v2.LiteOptionalWithCause; import org.roda.core.data.v2.ip.AIP; import org.roda.core.data.v2.ip.AIPState; +import org.roda.core.data.v2.ip.Permissions; import org.roda.core.data.v2.ip.TransferredResource; import org.roda.core.data.v2.jobs.Job; import org.roda.core.data.v2.jobs.PluginState; import org.roda.core.data.v2.jobs.Report; +import org.roda.core.data.v2.user.User; import org.roda.core.index.IndexService; import org.roda.core.model.ModelService; import org.roda.core.plugins.Plugin; @@ -123,10 +125,19 @@ private void processTransferredResource(IndexService index, ModelService model, LOGGER.debug("Converting {} to AIP", transferredResourcePath); AIPState state = AIPState.INGEST_PROCESSING; String aipType = RodaConstants.AIP_TYPE_MIXED; + Permissions permissions = new Permissions(); - final AIP aip = model.createAIP(state, computedSearchScope.orElse(null), aipType, - PermissionUtils.getIngestPermissions(job.getUsername()), transferredResource.getUUID(), - Arrays.asList(transferredResource.getName()), job.getId(), false, job.getUsername()); + User user = model.retrieveUser(job.getUsername()); + + if (computedSearchScope.isPresent()) { + permissions = model.retrieveAIP(computedSearchScope.get()).getPermissions(); + } + + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); + + final AIP aip = model.createAIP(state, computedSearchScope.orElse(null), aipType, finalPermissions, + transferredResource.getUUID(), Arrays.asList(transferredResource.getName()), job.getId(), false, + job.getUsername()); PluginHelper.createSubmission(model, createSubmission, transferredResourcePath, aip.getId()); diff --git a/roda-core/roda-core/src/main/resources/config/roda-core.properties b/roda-core/roda-core/src/main/resources/config/roda-core.properties index e8ef9b1dfe..122f7e3577 100644 --- a/roda-core/roda-core/src/main/resources/config/roda-core.properties +++ b/roda-core/roda-core/src/main/resources/config/roda-core.properties @@ -531,6 +531,54 @@ core.synchronization.scheduleInfo=0 0 * * * ########################################################################## core.aip.lockToEdit=false +########################################################################## +#User and group permissions regarding creations of aip +########################################################################## + +# Direct creator permissions +#core.aip.default_permissions.creator.permission[] = CREATE +#core.aip.default_permissions.creator.permission[] = UPDATE +#core.aip.default_permissions.creator.permission[] = READ + +# Admin users or groups so AIPs can be administered +#core.aip.default_permissions.admin.user[] = admin +core.aip.default_permissions.admin.group[] = administrators + +core.aip.default_permissions.admin.group[].administrators.permission[] = READ +core.aip.default_permissions.admin.group[].administrators.permission[] = UPDATE +core.aip.default_permissions.admin.group[].administrators.permission[] = CREATE +core.aip.default_permissions.admin.group[].administrators.permission[] = GRANT +core.aip.default_permissions.admin.group[].administrators.permission[] = DELETE + +# Additional group permissions +core.aip.default_permissions.group[] = archivists +core.aip.default_permissions.group[] = producers +core.aip.default_permissions.group[] = guests + +core.aip.default_permissions.group[].archivists.permission[] = READ +core.aip.default_permissions.group[].archivists.permission[] = UPDATE +core.aip.default_permissions.group[].archivists.permission[] = CREATE + +core.aip.default_permissions.group[].producers.permission[] = READ + +core.aip.default_permissions.group[].guests.permission[] = READ + +# Intersect creator groups with the configuration groups +core.aip.default_permissions.intersect_groups = true + +# System expects a minimum set of direct or indirect permissions for the creator +core.aip.default_permissions.creator.minimum.permissions[] = UPDATE +core.aip.default_permissions.creator.minimum.permissions[] = READ + +# Default permissions for the old behaviour +#core.aip.default_permissions.creator.permission[] = READ +#core.aip.default_permissions.creator.permission[] = CREATE +#core.aip.default_permissions.creator.permission[] = UPDATE +#core.aip.default_permissions.creator.permission[] = DELETE. +#core.aip.default_permissions.creator.permission[] = GRANT + + + ########################################################################## # User registration settings # diff --git a/roda-ui/roda-wui/pom.xml b/roda-ui/roda-wui/pom.xml index e488275228..2b7a1ca037 100644 --- a/roda-ui/roda-wui/pom.xml +++ b/roda-ui/roda-wui/pom.xml @@ -169,6 +169,7 @@ -Dorg.springframework.boot.logging.LoggingSystem=none -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5007 --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED @@ -222,6 +223,7 @@ -Dorg.springframework.boot.logging.LoggingSystem=none -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5006 --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED @@ -274,6 +276,8 @@ -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005 -Droda.environment.collect.version=false + --add-opens java.base/java.util=ALL-UNNAMED + --add-opens java.base/java.lang=ALL-UNNAMED diff --git a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/controllers/Browser.java b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/controllers/Browser.java index 4b0fe9abc4..f95464be70 100644 --- a/roda-ui/roda-wui/src/main/java/org/roda/wui/api/controllers/Browser.java +++ b/roda-ui/roda-wui/src/main/java/org/roda/wui/api/controllers/Browser.java @@ -16,7 +16,6 @@ import java.util.Arrays; import java.util.Date; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -55,7 +54,6 @@ import org.roda.core.data.v2.ip.IndexedFile; import org.roda.core.data.v2.ip.IndexedRepresentation; import org.roda.core.data.v2.ip.Permissions; -import org.roda.core.data.v2.ip.Permissions.PermissionType; import org.roda.core.data.v2.ip.Representation; import org.roda.core.data.v2.ip.TransferredResource; import org.roda.core.data.v2.ip.disposal.DisposalHold; @@ -81,6 +79,7 @@ import org.roda.core.data.v2.validation.ValidationException; import org.roda.core.index.utils.IterableIndexResult; import org.roda.core.model.utils.UserUtility; +import org.roda.core.plugins.base.ingest.PermissionUtils; import org.roda.core.storage.ContentPayload; import org.roda.core.storage.fs.FSPathContentPayload; import org.roda.core.storage.fs.FSUtils; @@ -1303,10 +1302,13 @@ private static AIP createAIPTop(User user, String type) throws AuthorizationDeni try { Permissions permissions = new Permissions(); - permissions.setUserPermissions(user.getId(), new HashSet<>(Arrays.asList(PermissionType.values()))); + + //calculate final permissions + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); // delegate - return BrowserHelper.createAIP(user, null, type, permissions); + return BrowserHelper.createAIP(user, null, type, finalPermissions); + } catch (RODAException e) { state = LogEntryState.FAILURE; throw e; @@ -1352,10 +1354,11 @@ private static AIP createAIPBelow(User user, String parentId, String type) throw throw new RequestNotValidException("Creating AIP that should be below another with a null parentId"); } - permissions.setUserPermissions(user.getId(), new HashSet<>(Arrays.asList(PermissionType.values()))); + //calculate final permissions + Permissions finalPermissions = PermissionUtils.calculatePermissions(user, Optional.of(permissions)); // delegate - return BrowserHelper.createAIP(user, parentId, type, permissions); + return BrowserHelper.createAIP(user, parentId, type, finalPermissions); } catch (RODAException e) { state = LogEntryState.FAILURE; throw e; @@ -3234,7 +3237,8 @@ public static Notification acknowledgeNotification(User user, String notificatio } public static Reports listReports(User user, String id, String resourceOrSip, int start, int limit, - String acceptFormat) throws AuthorizationDeniedException, GenericException, RequestNotValidException, NotFoundException { + String acceptFormat) + throws AuthorizationDeniedException, GenericException, RequestNotValidException, NotFoundException { final ControllerAssistant controllerAssistant = new ControllerAssistant() {}; // validate input