diff --git a/source/java/org/alfresco/opencmis/CMISConnector.java b/source/java/org/alfresco/opencmis/CMISConnector.java index e13b760156..bd388f55ee 100644 --- a/source/java/org/alfresco/opencmis/CMISConnector.java +++ b/source/java/org/alfresco/opencmis/CMISConnector.java @@ -2641,6 +2641,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen } Set currentAces = permissionService.getAllSetPermissions(nodeRef); + Acl currentACL = getACL(nodeRef, false); // remove all permissions permissionService.deletePermissions(nodeRef); @@ -2654,7 +2655,9 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen principalId = AuthenticationUtil.getFullyAuthenticatedUser(); } - List permissions = translatePermissionsFromCMIS(ace.getPermissions()); + List acePermissions = ace.getPermissions(); + normaliseAcePermissions(currentACL, ace, acePermissions); + List permissions = translatePermissionsFromCMIS(acePermissions); normalisePermissions(currentAces, permissions); for (String permission : permissions) { @@ -2663,6 +2666,38 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen } } + /* + * MNT-10165: CMIS 1.1 API: Impossible to remove ACL through Atom binding + * + * Detect permission to delete for principal and + * also delete all the concomitant basic permissions + */ + private void normaliseAcePermissions(Acl currentACL, Ace newAce, List acePermissions) + { + for (Ace oldAce : currentACL.getAces()) + { + if (oldAce.getPrincipalId().equals(newAce.getPrincipalId())) + { + // detect what permissions were deleted for principal + Set permissionsDeletedForPrincipal = new HashSet(oldAce.getPermissions()); + Set newPermissions = new HashSet(newAce.getPermissions()); + permissionsDeletedForPrincipal.removeAll(newPermissions); + for (String permissionDeleted : permissionsDeletedForPrincipal) + { + // for deleted permission also delete all attendant basic permissions + List onePermissionList = new ArrayList(); + onePermissionList.add(permissionDeleted); + + List cmisPermissions = translatePermmissionsToCMIS(onePermissionList, false); + for (String cmisPermission : cmisPermissions) + { + acePermissions.remove(cmisPermission); + } + } + } + } + } + /* * ALF-11868: the cmis client library may incorrectly send READ or WRITE permissions to applyAcl. * This method works around this by "normalising" permissions: diff --git a/source/test-java/org/alfresco/opencmis/CMISTest.java b/source/test-java/org/alfresco/opencmis/CMISTest.java index bea15f95f1..f38fc652ff 100644 --- a/source/test-java/org/alfresco/opencmis/CMISTest.java +++ b/source/test-java/org/alfresco/opencmis/CMISTest.java @@ -37,6 +37,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.alfresco.cmis.CMISAccessControlService; import org.alfresco.cmis.CMISDictionaryModel; import org.alfresco.model.ContentModel; import org.alfresco.opencmis.search.CMISQueryOptions; @@ -62,6 +63,10 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.AuthorityType; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.tagging.TaggingService; import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.namespace.NamespaceService; @@ -70,6 +75,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.Pair; import org.apache.chemistry.opencmis.commons.PropertyIds; +import org.apache.chemistry.opencmis.commons.data.Ace; import org.apache.chemistry.opencmis.commons.data.AllowableActions; import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; import org.apache.chemistry.opencmis.commons.data.ObjectData; @@ -80,6 +86,7 @@ import org.apache.chemistry.opencmis.commons.data.Properties; import org.apache.chemistry.opencmis.commons.data.PropertyData; import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; +import org.apache.chemistry.opencmis.commons.enums.AclPropagation; import org.apache.chemistry.opencmis.commons.enums.Action; import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; @@ -87,6 +94,9 @@ import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException; import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlEntryImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlPrincipalDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.CmisExtensionElementImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ExtensionDataImpl; @@ -123,6 +133,8 @@ public class CMISTest private LockService lockService; private TaggingService taggingService; private NamespaceService namespaceService; + private AuthorityService authorityService; + private PermissionService permissionService; private AlfrescoCmisServiceFactory factory; @@ -289,6 +301,8 @@ public class CMISTest this.factory = (AlfrescoCmisServiceFactory)ctx.getBean("CMISServiceFactory"); this.cmisConnector = (CMISConnector) ctx.getBean("CMISConnector"); this.nodeDAO = (NodeDAO) ctx.getBean("nodeDAO"); + this.authorityService = (AuthorityService)ctx.getBean("AuthorityService"); + this.permissionService = (PermissionService) ctx.getBean("permissionService"); } /** @@ -2015,4 +2029,119 @@ public class CMISTest AuthenticationUtil.popAuthentication(); } } + + /** + * MNT-10165: Check that all concomitant basic CMIS permissions are deleted + * when permission is deleted vai CMIS 1.1 API. For Atom binding it applies + * new set of permissions instead of deleting the old ones. + */ + @Test + public void testRemoveACL() throws Exception + { + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + try + { + // preconditions: create test document + final String testGroup = PermissionService.GROUP_PREFIX + "group1"; + if (!authorityService.authorityExists(testGroup)) + { + authorityService.createAuthority(AuthorityType.GROUP, "group1"); + } + + final FileInfo document = transactionService.getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionCallback() + { + @Override + public FileInfo execute() throws Throwable + { + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + + String folderName = GUID.generate(); + FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName, ContentModel.TYPE_FOLDER); + nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName); + assertNotNull(folderInfo); + + String docName = GUID.generate(); + FileInfo document = fileFolderService.create(folderInfo.getNodeRef(), docName, ContentModel.TYPE_CONTENT); + assertNotNull(document); + nodeService.setProperty(document.getNodeRef(), ContentModel.PROP_NAME, docName); + + return document; + } + }); + + Set permissions = permissionService.getAllSetPermissions(document.getNodeRef()); + assertEquals(permissions.size(), 1); + AccessPermission current = permissions.iterator().next(); + assertEquals(current.getAuthority(), "GROUP_EVERYONE"); + assertEquals(current.getPermission(), "Consumer"); + + // add group1 with Coordinator permissions + permissionService.setPermission(document.getNodeRef(), testGroup, PermissionService.COORDINATOR, true); + permissions = permissionService.getAllSetPermissions(document.getNodeRef()); + + Map docPermissions = new HashMap(); + for (AccessPermission permission : permissions) + { + docPermissions.put(permission.getAuthority(), permission.getPermission()); + } + assertTrue(docPermissions.keySet().contains(testGroup)); + assertEquals(docPermissions.get(testGroup), PermissionService.COORDINATOR); + + // update permissions for group1 via CMIS 1.1 API + withCmisService(new CmisServiceCallback() + { + @Override + public Void execute(CmisService cmisService) + { + List repositories = cmisService.getRepositoryInfos(null); + assertNotNull(repositories); + assertTrue(repositories.size() > 0); + RepositoryInfo repo = repositories.iterator().next(); + String repositoryId = repo.getId(); + String docIdStr = document.getNodeRef().toString(); + + // when removing Coordinator ACE from workbench-0.10.0 it sends PUT request + // to apply basic cmis:write, cmis:read, cmis:all for principal + AccessControlListImpl acesToPut = new AccessControlListImpl(); + List acesList = new ArrayList(); + acesToPut.setAces(acesList); + AccessControlEntryImpl ace = new AccessControlEntryImpl(); + ace.setPrincipal(new AccessControlPrincipalDataImpl(testGroup)); + List putPermissions = new ArrayList(); + putPermissions.add(CMISAccessControlService.CMIS_ALL_PERMISSION); + putPermissions.add(CMISAccessControlService.CMIS_READ_PERMISSION); + putPermissions.add(CMISAccessControlService.CMIS_WRITE_PERMISSION); + ace.setPermissions(putPermissions); + ace.setDirect(true); + acesList.add(ace); + cmisService.applyAcl(repositoryId, docIdStr, acesToPut, AclPropagation.REPOSITORYDETERMINED); + + return null; + } + }, CmisVersion.CMIS_1_1); + + // check that permissions are the same as they were before Coordinator was added + permissions = permissionService.getAllSetPermissions(document.getNodeRef()); + docPermissions = new HashMap(); + for (AccessPermission permission : permissions) + { + docPermissions.put(permission.getAuthority(), permission.getPermission()); + } + assertFalse(docPermissions.keySet().contains(testGroup)); + assertEquals(permissions.size(), 1); + current = permissions.iterator().next(); + assertEquals(current.getAuthority(), "GROUP_EVERYONE"); + assertEquals(current.getPermission(), "Consumer"); + } + catch (CmisConstraintException e) + { + fail(e.toString()); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + } }