MNT-22040 Copying nodes that contain sys:pendingFixAcl aspect (#188)

* MNT-22040 - Copying nodes that contain sys:pendingFixAcl aspect
* Increased coverage of unit tests
* Added public method removePendingAclAspect
* Changed the job to use the removePendingAclAspect method instead of
repeating the same set of operations twice
* Changed method setFixedAcls to use sharedAclToReplace property of node
if it has the pendingFixAcl aspect applied instead of using the current
shared ACL to be replaced

* Added more tests and validations

* Fixed cuncurrency issues when updating parent and child at the same time, permissions triggered by a move on a node with a pending acl and permissions needeing to be reapplied on nodes that already have the pending acl aspect

* Increase the tree strcuture as sometimes we don't reach the timeout in tests

* code cleanup and formatting
This commit is contained in:
evasques
2020-12-14 15:08:21 +00:00
committed by GitHub
parent c0d1098db0
commit 1b2c5d4c37
4 changed files with 1433 additions and 251 deletions

View File

@@ -394,6 +394,18 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
else else
{ {
// When node is copied when the aspect is applied, the sharedACLtoReplace will not match the children's ACLS
// to replace, we need to use the current one.
Long currentAcl = nodeDAO.getNodeAclId(nodeId);
if (nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
// If node has a pending acl, retrieve the sharedAclToReplace from node property. When the job calls
// this, it already does it but on move and copy operations, it uses the new parents old ACL.
sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
}
// Lazily retrieve/create the shared ACL // Lazily retrieve/create the shared ACL
if (mergeFrom == null) if (mergeFrom == null)
{ {
@@ -407,18 +419,15 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId); List<NodeIdAndAclId> children = nodeDAO.getPrimaryChildrenAcls(nodeId);
if(children.size() > 0)
{
nodeDAO.setPrimaryChildrenSharedAclId(nodeId, sharedAclToReplace, mergeFrom);
}
if (!propagateOnChildren) if (!propagateOnChildren)
{ {
return; return;
} }
for (NodeIdAndAclId child : children) for (NodeIdAndAclId child : children)
{ {
Long acl = child.getAclId(); //Use the current ACL instead of the stored value, it could've been changed meanwhile
Long acl = nodeDAO.getNodeAclId(child.getId());
if (acl == null) if (acl == null)
{ {
@@ -426,12 +435,8 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
else else
{ {
// if(acl.equals(mergeFrom))
// {
// setFixedAcls(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false);
// }
// Still has old shared ACL or already replaced // Still has old shared ACL or already replaced
if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom)) if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom) || acl.equals(currentAcl))
{ {
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren); propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren);
} }
@@ -457,6 +462,21 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
} }
} }
// By doing an eager update of the direct children we canot see if another thread has changed the ACL
// between the time we get the child nodes and we update them. By updating the direct children last it is
// possible to verify if any child has changed meanwhile.
if(children.size() > 0)
{
nodeDAO.setPrimaryChildrenSharedAclId(nodeId, sharedAclToReplace, mergeFrom);
}
// When this is not executed triggered by the job, but a move or copy operation occures on a pending
// node, we don't want to apply the OLD ACL that was pending
if(nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
removePendingAclAspect(nodeId);
}
} }
} }
@@ -509,14 +529,23 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
// set ASPECT_PENDING_FIX_ACL aspect on node to be later on processed with FixedAclUpdater amd switch flag // set ASPECT_PENDING_FIX_ACL aspect on node to be later on processed with FixedAclUpdater amd switch flag
// FIXED_ACL_ASYNC_REQUIRED_KEY // FIXED_ACL_ASYNC_REQUIRED_KEY
addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom); addFixedAclPendingAspect(nodeId, sharedAclToReplace, inheritFrom, mergeFrom);
AlfrescoTransactionSupport.bindResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY, true); AlfrescoTransactionSupport.bindResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY, true);
// stop propagating on children nodes // stop propagating on children nodes
return false; return false;
} }
private void addFixedAclPendingAspect(Long nodeId, Long sharedAclToReplace, Long inheritFrom) private void addFixedAclPendingAspect(Long nodeId, Long sharedAclToReplace, Long inheritFrom, Long mergeFrom)
{ {
//If the node already has the pending ACL aspect, just update the new inheritFrom value
if (nodeDAO.hasNodeAspect(nodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
Map<QName, Serializable> pendingAclProperties = new HashMap<>();
pendingAclProperties.put(ContentModel.PROP_INHERIT_FROM_ACL, inheritFrom);
nodeDAO.addNodeProperties(nodeId, pendingAclProperties);
return;
}
Set<QName> aspect = new HashSet<>(); Set<QName> aspect = new HashSet<>();
aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL); aspect.add(ContentModel.ASPECT_PENDING_FIX_ACL);
nodeDAO.addNodeAspects(nodeId, aspect); nodeDAO.addNodeAspects(nodeId, aspect);
@@ -530,6 +559,17 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
} }
} }
public void removePendingAclAspect(Long nodeId)
{
Set<QName> aspects = new HashSet<>(1);
aspects.add(ContentModel.ASPECT_PENDING_FIX_ACL);
Set<QName> pendingFixAclProperties = new HashSet<>();
pendingFixAclProperties.add(ContentModel.PROP_SHARED_ACL_TO_REPLACE);
pendingFixAclProperties.add(ContentModel.PROP_INHERIT_FROM_ACL);
nodeDAO.removeNodeAspects(nodeId, aspects);
nodeDAO.removeNodeProperties(nodeId, pendingFixAclProperties);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@@ -106,4 +106,6 @@ public interface AccessControlListDAO
public void updateInheritance(Long childNodeId, Long oldParentAclId, Long newParentAclId); public void updateInheritance(Long childNodeId, Long oldParentAclId, Long newParentAclId);
public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set); public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set);
public void removePendingAclAspect(Long nodeId);
} }

View File

@@ -53,7 +53,6 @@ import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transaction.TransactionListenerAdapter; import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -151,7 +150,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
public void init() public void init()
{ {
onInheritPermissionsDisabledDelegate = policyComponent.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class); onInheritPermissionsDisabledDelegate = policyComponent
.registerClassPolicy(PermissionServicePolicies.OnInheritPermissionsDisabled.class);
} }
private class GetNodesWithAspects private class GetNodesWithAspects
@@ -264,35 +264,34 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
{ {
log.debug(String.format("Processing node %s", nodeRef)); log.debug(String.format("Processing node %s", nodeRef));
} }
final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst(); final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst();
// MNT-22009 - If node was deleted and in archive store, remove the aspect and properties and do not // MNT-22009 - If node was deleted and in archive store, remove the aspect and properties and do not
// process // process
if (nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE)) if (nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE))
{ {
nodeDAO.removeNodeAspects(nodeId, aspects); accessControlListDAO.removePendingAclAspect(nodeId);
nodeDAO.removeNodeProperties(nodeId, PENDING_FIX_ACL_ASPECT_PROPS);
return null; return null;
} }
// retrieve acl properties from node // retrieve acl properties from node
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_INHERIT_FROM_ACL);
ContentModel.PROP_INHERIT_FROM_ACL); Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId,
ContentModel.PROP_SHARED_ACL_TO_REPLACE);
// set inheritance using retrieved prop // set inheritance using retrieved prop
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, true);
true);
nodeDAO.removeNodeAspects(nodeId, aspects); // Remove aspect
nodeDAO.removeNodeProperties(nodeId, PENDING_FIX_ACL_ASPECT_PROPS); accessControlListDAO.removePendingAclAspect(nodeId);
if (!policyIgnoreUtil.ignorePolicy(nodeRef)) if (!policyIgnoreUtil.ignorePolicy(nodeRef))
{ {
boolean transformedToAsyncOperation = toBoolean((Boolean) AlfrescoTransactionSupport.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY)); boolean transformedToAsyncOperation = toBoolean(
(Boolean) AlfrescoTransactionSupport.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY));
OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate.get(ContentModel.TYPE_BASE); OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate
.get(ContentModel.TYPE_BASE);
onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation); onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation);
} }
@@ -406,12 +405,8 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
AclWorkProvider provider = new AclWorkProvider(); AclWorkProvider provider = new AclWorkProvider();
AclWorker worker = new AclWorker(); AclWorker worker = new AclWorker();
BatchProcessor<NodeRef> bp = new BatchProcessor<>( BatchProcessor<NodeRef> bp = new BatchProcessor<>("FixedAclUpdater",
"FixedAclUpdater", transactionService.getRetryingTransactionHelper(), provider, numThreads, maxItemBatchSize, applicationContext,
transactionService.getRetryingTransactionHelper(),
provider,
numThreads, maxItemBatchSize,
applicationContext,
log, 100); log, 100);
int count = bp.process(worker, true); int count = bp.process(worker, true);
return count; return count;
@@ -424,7 +419,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
finally finally
{ {
jobLockRefreshCallback.isActive.set(false); jobLockRefreshCallback.isActive.set(false);
if(lockToken != null) if (lockToken != null)
{ {
jobLockService.releaseLock(lockToken, lockQName); jobLockService.releaseLock(lockToken, lockQName);
} }