From 3d2a5a0c8c86faa8a9c97642bc13da88e9cb349b Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Mon, 13 Aug 2012 05:45:17 +0000 Subject: [PATCH 1/8] RM-477: RM 2.0 does not work with SOLR * first pass at supporting SOLR in RM2 * currently requires reimplementation of permissionServiceImpl due to visibility of methods and member variables * job execution delayed to try and prevent start up errors * fetchSOLR task added to gradle to retrieve SOLR zip from Maven (could do with some scripts to help with setup for module) * TODO more testing and refinement of solution git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40344 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- build.gradle | 7 + rm-server/build.gradle | 11 + .../org_alfresco_module_rm/module-context.xml | 47 + .../rm-service-context.xml | 2 +- rm-server/gradle.properties | 1 + .../capability/RMAfterInvocationProvider.java | 37 +- .../capability/RMSecurityCommon.java | 38 +- .../job/DispositionLifecycleJobExecuter.java | 2 +- ...otifyOfRecordsDueForReviewJobExecuter.java | 2 +- .../job/PublishUpdatesJobExecuter.java | 2 +- .../job/RecordsManagementJobExecuter.java | 59 +- .../permission/OtherImpl.java | 370 +++ .../permission/PermissionServiceImpl.java | 2776 +++++++++++++++++ 13 files changed, 3316 insertions(+), 38 deletions(-) create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java create mode 100644 rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/PermissionServiceImpl.java diff --git a/build.gradle b/build.gradle index 6c3a242af1..da926c470b 100644 --- a/build.gradle +++ b/build.gradle @@ -239,6 +239,13 @@ subprojects { } } + task localisationPackage (type: Copy) << { + + from configDir + include "**/*.properties" + into "${assembleDir}/i18n" + } + task deployExploded(dependsOn: 'jar') << { def jarFileObj = file(jarFilePath) diff --git a/rm-server/build.gradle b/rm-server/build.gradle index 58e8f695ae..1934834862 100644 --- a/rm-server/build.gradle +++ b/rm-server/build.gradle @@ -13,6 +13,17 @@ dependencies { testRuntime files(testResourceDir) testRuntime group: 'org.alfresco.enterprise', name: 'alfresco', version: alfrescoBaseVersion, type: 'war' + testRuntime group: 'org.alfresco.enterprise', name: 'alfresco-solr', version: alfrescoBaseVersion, type: 'zip' +} + +task fetchSOLR(type:Copy) { + from configurations.testRuntime + into '.' + include '*.zip' + rename { String filename -> solrFile } +} + +task useSOLR << { } task resetDatabase << { diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml index ae89fe7725..8aabd4625d 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/module-context.xml @@ -2,6 +2,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${security.anyDenyDenies} + + + + + + + + = maxChecks || (currentTimeMillis - startTimeMillis) > maxCheckTime) - { - filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, PermissionEvaluationMode.EAGER, returnedObject - .getResultSetMetaData().getSearchParameters())); - break; - } +// if (i >= maxChecks || (currentTimeMillis - startTimeMillis) > maxCheckTime) +// { +// filteringResultSet.setResultSetMetaData( +// new SimpleResultSetMetaData( +// LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS, +// PermissionEvaluationMode.EAGER, +// returnedObject.getResultSetMetaData().getSearchParameters())); +// break; +// } // All permission checks must pass inclusionMask.set(i, true); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java index fd58ad0463..c80837debf 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/capability/RMSecurityCommon.java @@ -106,13 +106,14 @@ public class RMSecurityCommon */ public int checkRead(NodeRef nodeRef) { + int result = AccessDecisionVoter.ACCESS_ABSTAIN; if (nodeRef != null) { // now we know the node - we can abstain for certain types and aspects (eg, rm) - return checkRead(nodeRef, false); + result = checkRead(nodeRef, false); } - return AccessDecisionVoter.ACCESS_ABSTAIN; + return result; } /** @@ -123,35 +124,32 @@ public class RMSecurityCommon */ public int checkRead(NodeRef nodeRef, boolean allowDMRead) { - if (nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT)) + int result = AccessDecisionVoter.ACCESS_ABSTAIN; + + if (rmService.isFilePlanComponent(nodeRef) == true) { - return checkRmRead(nodeRef); + result = checkRmRead(nodeRef); } - else + else if (allowDMRead == true) { - if (allowDMRead) + // Check DM read for copy etc + // DM does not grant - it can only deny + if (permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED) { - // Check DM read for copy etc - // DM does not grant - it can only deny - if (permissionService.hasPermission(nodeRef, PermissionService.READ) == AccessStatus.DENIED) + if (logger.isDebugEnabled()) { - if (logger.isDebugEnabled()) - { - logger.debug("\t\tPermission is denied"); - Thread.dumpStack(); - } - return AccessDecisionVoter.ACCESS_DENIED; - } - else - { - return AccessDecisionVoter.ACCESS_GRANTED; + logger.debug("\t\tPermission is denied"); + Thread.dumpStack(); } + result = AccessDecisionVoter.ACCESS_DENIED; } else { - return AccessDecisionVoter.ACCESS_ABSTAIN; + result = AccessDecisionVoter.ACCESS_GRANTED; } } + + return result; } /** diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java index 160b86ba10..850d7148e0 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/DispositionLifecycleJobExecuter.java @@ -72,7 +72,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute /** * @see org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJobExecuter#execute() */ - public void execute() + public void executeImpl() { logger.debug("Job Starting"); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJobExecuter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJobExecuter.java index 43057e0bc8..1266b45962 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJobExecuter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/NotifyOfRecordsDueForReviewJobExecuter.java @@ -69,7 +69,7 @@ public class NotifyOfRecordsDueForReviewJobExecuter extends RecordsManagementJob /** * @see org.alfresco.module.org_alfresco_module_rm.job.RecordsManagementJobExecuter#execute() */ - public void execute() + public void executeImpl() { if (logger.isDebugEnabled()) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJobExecuter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJobExecuter.java index ba13a75752..b40c648659 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJobExecuter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/PublishUpdatesJobExecuter.java @@ -80,7 +80,7 @@ public class PublishUpdatesJobExecuter extends RecordsManagementJobExecuter this.behaviourFilter = behaviourFilter; } - public void execute() + public void executeImpl() { if (logger.isDebugEnabled() == true) { diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJobExecuter.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJobExecuter.java index 871a65bb28..5aa7d4b80a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJobExecuter.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/job/RecordsManagementJobExecuter.java @@ -20,18 +20,73 @@ package org.alfresco.module.org_alfresco_module_rm.job; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; /** + * Records management job executer base class. + * * @author Roy Wetherall */ -public abstract class RecordsManagementJobExecuter implements RecordsManagementModel +public abstract class RecordsManagementJobExecuter extends AbstractLifecycleBean + implements RecordsManagementModel { + /** Retrying transaction helper */ protected RetryingTransactionHelper retryingTransactionHelper; + /** Indicates whether the application bootstrap is complete or not */ + protected boolean bootstrapComplete = false; + + /** + * @param retryingTransactionHelper retrying transaction helper + */ public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) { this.retryingTransactionHelper = retryingTransactionHelper; } - public abstract void execute(); + /** + * Executes the jobs work. + */ + public void execute() + { + // jobs not allowed to execute unless bootstrap is complete + if (bootstrapComplete == true) + { + executeImpl(); + } + } + + /** + * Jobs work implementation. + */ + public abstract void executeImpl(); + + /** + * @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent) + */ + @Override + protected void onBootstrap(ApplicationEvent arg0) + { + // record that the bootstrap has complete + bootstrapComplete = true; + } + + /** + * @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent) + */ + @Override + protected void onShutdown(ApplicationEvent arg0) + { + // no implementation + } + + /** + * @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onApplicationEvent(org.springframework.context.ApplicationEvent) + */ + @Override + public void onApplicationEvent(ApplicationEvent arg0) + { + // no implementation + } } diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java new file mode 100644 index 0000000000..958d67566a --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/OtherImpl.java @@ -0,0 +1,370 @@ +package org.alfresco.module.org_alfresco_module_rm.permission; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.domain.permissions.AclDAO; +import org.alfresco.repo.security.permissions.AccessControlEntry; +import org.alfresco.repo.security.permissions.AccessControlList; +import org.alfresco.repo.security.permissions.PermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.impl.ModelDAO; +import org.alfresco.repo.security.permissions.impl.PermissionServiceImpl; +import org.alfresco.repo.security.permissions.impl.RequiredPermission; +import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +public class OtherImpl extends PermissionServiceImpl +{ + static SimplePermissionReference RM_OLD_ALL_PERMISSIONS_REFERENCE = SimplePermissionReference.getPermissionReference( + QName.createQName("", PermissionService.ALL_PERMISSIONS), + PermissionService.ALL_PERMISSIONS); + + private SimpleCache> rmReadersCache; + + private AclDAO rmAclDaoComponent; + + private ModelDAO rmModelDao; + + public void setRmReadersCache(SimpleCache> rmReadersCache) + { + this.rmReadersCache = rmReadersCache; + } + + public void setRmAclDAO(AclDAO rmAclDaoComponent) + { + this.rmAclDaoComponent = rmAclDaoComponent; + } + + public void setRmModelDAO(ModelDAO rmModelDao) + { + this.rmModelDao = rmModelDao; + } + + @Override + public void setAnyDenyDenies(boolean anyDenyDenies) + { + super.setAnyDenyDenies(anyDenyDenies); + rmReadersCache.clear(); + } + + @Override + public Set getReaders(Long aclId) + { + Set dmReaders = super.getReaders(aclId); + + Set rmReaders = rmReadersCache.get(aclId); + if (rmReaders == null) + { + rmReaders = buildRMReaders(aclId); + rmReadersCache.put(aclId, rmReaders); + } + + Set result = new HashSet(); + result.addAll(dmReaders); + result.addAll(rmReaders); + return result; + } + + private Set buildRMReaders(Long aclId) + { + AccessControlList acl = rmAclDaoComponent.getAccessControlList(aclId); + if (acl == null) + { + return Collections.emptySet(); + } + + HashSet assigned = new HashSet(); + HashSet readers = new HashSet(); + + for (AccessControlEntry ace : acl.getEntries()) + { + assigned.add(ace.getAuthority()); + } + + PermissionReference permissionRef = getPermissionReference(RMPermissionModel.READ_RECORDS); + + for (String authority : assigned) + { + RMUnconditionalAclTest rmTest = new RMUnconditionalAclTest(permissionRef); + if (rmTest.evaluate(authority, aclId)) + { + readers.add(authority); + } + } + + return Collections.unmodifiableSet(readers); + } + + /** + * Ignores type and aspect requirements on the node + * + */ + private class RMUnconditionalAclTest + { + /* + * The required permission. + */ + PermissionReference required; + + /* + * Granters of the permission + */ + Set granters; + + /* + * The additional permissions required at the node level. + */ + Set nodeRequirements = new HashSet(); + + /* + * Constructor just gets the additional requirements + */ + RMUnconditionalAclTest(PermissionReference required) + { + this.required = required; + + // Set the required node permissions + if (required.equals(getPermissionReference(ALL_PERMISSIONS))) + { + nodeRequirements = rmModelDao.getUnconditionalRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE); + } + else + { + nodeRequirements = rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.NODE); + } + + if (rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) + { + throw new IllegalStateException("Parent permissions can not be checked for an acl"); + } + + if (rmModelDao.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) + { + throw new IllegalStateException("Child permissions can not be checked for an acl"); + } + + // Find all the permissions that grant the allowed permission + // All permissions are treated specially. + granters = new LinkedHashSet(128, 1.0f); + granters.addAll(rmModelDao.getGrantingPermissions(required)); + granters.add(getAllPermissionReference()); + granters.add(RM_OLD_ALL_PERMISSIONS_REFERENCE); + } + + /** + * Internal hook point for recursion + * + * @param authorisations + * @param nodeRef + * @param denied + * @param recursiveIn + * @return true if granted + */ + boolean evaluate(String authority, Long aclId) + { + // Start out true and "and" all other results + boolean success = true; + + // Check the required permissions but not for sets they rely on + // their underlying permissions + //if (modelDAO.checkPermission(required)) + //{ + + // We have to do the test as no parent will help us out + success &= hasSinglePermission(authority, aclId); + + if (!success) + { + return false; + } + //} + + // Check the other permissions required on the node + for (PermissionReference pr : nodeRequirements) + { + // Build a new test + RMUnconditionalAclTest nt = new RMUnconditionalAclTest(pr); + success &= nt.evaluate(authority, aclId); + if (!success) + { + return false; + } + } + + return success; + } + + boolean hasSinglePermission(String authority, Long aclId) + { + // Check global permission + + if (checkGlobalPermissions(authority)) + { + return true; + } + + if(aclId == null) + { + return false; + } + else + { + return checkRequired(authority, aclId); + } + + } + + /** + * Check if we have a global permission + * + * @param authorisations + * @return true if granted + */ + private boolean checkGlobalPermissions(String authority) + { + for (PermissionEntry pe : rmModelDao.getGlobalPermissionEntries()) + { + if (isGranted(pe, authority)) + { + return true; + } + } + return false; + } + + /** + * Check that a given authentication is available on a node + * + * @param authorisations + * @param nodeRef + * @param denied + * @return true if a check is required + */ + boolean checkRequired(String authority, Long aclId) + { + AccessControlList acl = rmAclDaoComponent.getAccessControlList(aclId); + + if (acl == null) + { + return false; + } + + Set> denied = new HashSet>(); + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (AccessControlEntry ace : acl.getEntries()) + { + if (isGranted(ace, authority, denied)) + { + return true; + } + } + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isGranted(AccessControlEntry ace, String authority, Set> denied) + { + // If the permission entry denies then we just deny + if (ace.getAccessStatus() == AccessStatus.DENIED) + { + denied.add(new Pair(ace.getAuthority(), ace.getPermission())); + + Set granters = rmModelDao.getGrantingPermissions(ace.getPermission()); + for (PermissionReference granter : granters) + { + denied.add(new Pair(ace.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = rmModelDao.getGranteePermissions(ace.getPermission()); + for (PermissionReference grantee : grantees) + { + denied.add(new Pair(ace.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(RM_OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : rmModelDao.getAllPermissions()) + { + denied.add(new Pair(ace.getAuthority(), deny)); + } + } + + return false; + } + + // The permission is allowed but we deny it as it is in the denied + // set + + if (denied != null) + { + Pair specific = new Pair(ace.getAuthority(), required); + if (denied.contains(specific)) + { + return false; + } + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) + { + { + return true; + } + } + + // Default deny + return false; + } + + private boolean isGranted(PermissionEntry pe, String authority) + { + // If the permission entry denies then we just deny + if (pe.isDenied()) + { + return false; + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) + { + { + return true; + } + } + + // Default deny + return false; + } + } +} diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/PermissionServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/PermissionServiceImpl.java new file mode 100644 index 0000000000..4a0ed61f9e --- /dev/null +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/permission/PermissionServiceImpl.java @@ -0,0 +1,2776 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.module.org_alfresco_module_rm.permission; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import net.sf.acegisecurity.Authentication; +import net.sf.acegisecurity.GrantedAuthority; +import net.sf.acegisecurity.providers.dao.User; + +import org.alfresco.model.ContentModel; +import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; +import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.avm.AVMRepository; +import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.domain.permissions.AclDAO; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.security.authority.AuthorityServiceImpl; +import org.alfresco.repo.security.permissions.ACLType; +import org.alfresco.repo.security.permissions.AccessControlEntry; +import org.alfresco.repo.security.permissions.AccessControlList; +import org.alfresco.repo.security.permissions.AccessControlListProperties; +import org.alfresco.repo.security.permissions.DynamicAuthority; +import org.alfresco.repo.security.permissions.NodePermissionEntry; +import org.alfresco.repo.security.permissions.PermissionEntry; +import org.alfresco.repo.security.permissions.PermissionReference; +import org.alfresco.repo.security.permissions.PermissionServiceSPI; +import org.alfresco.repo.security.permissions.impl.AccessPermissionImpl; +import org.alfresco.repo.security.permissions.impl.ModelDAO; +import org.alfresco.repo.security.permissions.impl.PermissionsDaoComponent; +import org.alfresco.repo.security.permissions.impl.RequiredPermission; +import org.alfresco.repo.security.permissions.impl.SimplePermissionReference; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.version.Version2Model; +import org.alfresco.repo.version.VersionModel; +import org.alfresco.repo.version.common.VersionUtil; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionContext; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.EqualsHelper; +import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + +/** + * @author Roy Wetherall + */ +public class PermissionServiceImpl extends AbstractLifecycleBean implements PermissionServiceSPI +{ + static SimplePermissionReference OLD_ALL_PERMISSIONS_REFERENCE = SimplePermissionReference.getPermissionReference( + QName.createQName("", PermissionService.ALL_PERMISSIONS), + PermissionService.ALL_PERMISSIONS); + + private static Log log = LogFactory.getLog(PermissionServiceImpl.class); + + /** a transactionally-safe cache to be injected */ + private SimpleCache accessCache; + + private SimpleCache> readersCache; + + private SimpleCache> readersDeniedCache; + + /* + * Access to the model + */ + private ModelDAO modelDAO; + + /* + * Access to permissions + */ + private PermissionsDaoComponent permissionsDaoComponent; + + /* + * Access to the node service + */ + private NodeService nodeService; + + /* + * Access to the tenant service + */ + private TenantService tenantService; + + /* + * Access to the data dictionary + */ + private DictionaryService dictionaryService; + + /* + * Access to the ownable service + */ + private OwnableService ownableService; + + /* + * Access to the authority component + */ + private AuthorityService authorityService; + + /* + * Dynamic authorities providers + */ + private List dynamicAuthorities; + + private PolicyComponent policyComponent; + + private AclDAO aclDaoComponent; + + private PermissionReference allPermissionReference; + + private boolean anyDenyDenies = false; + + /** + * Standard spring construction. + */ + public PermissionServiceImpl() + { + super(); + } + + // + // Inversion of control + // + + + + /** + * Set the dictionary service + * @param dictionaryService + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param anyDenyDenies the anyDenyDenies to set + */ + public void setAnyDenyDenies(boolean anyDenyDenies) + { + this.anyDenyDenies = anyDenyDenies; + accessCache.clear(); + readersCache.clear(); + readersDeniedCache.clear(); + } + + public boolean getAnyDenyDenies() + { + return anyDenyDenies; + } + + /** + * Set the permissions model dao + * + * @param modelDAO + */ + public void setModelDAO(ModelDAO modelDAO) + { + this.modelDAO = modelDAO; + } + + /** + * Set the node service. + * + * @param nodeService + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * Set the ownable service. + * + * @param ownableService + */ + public void setOwnableService(OwnableService ownableService) + { + this.ownableService = ownableService; + } + + /** + * Set the tenant service. + * @param tenantService + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + /** + * Set the permissions dao component + * + * @param permissionsDaoComponent + */ + public void setPermissionsDaoComponent(PermissionsDaoComponent permissionsDaoComponent) + { + this.permissionsDaoComponent = permissionsDaoComponent; + } + + /** + * Set the authority service. + * + * @param authorityService + */ + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + /** + * Set the dynamic authorities + * + * @param dynamicAuthorities + */ + public void setDynamicAuthorities(List dynamicAuthorities) + { + this.dynamicAuthorities = dynamicAuthorities; + } + + /** + * Set the ACL DAO component. + * + * @param aclDaoComponent + */ + public void setAclDAO(AclDAO aclDaoComponent) + { + this.aclDaoComponent = aclDaoComponent; + } + + /** + * Set the permissions access cache. + * + * @param accessCache + * a transactionally safe cache + */ + public void setAccessCache(SimpleCache accessCache) + { + this.accessCache = accessCache; + } + + /** + * @param readersCache the readersCache to set + */ + public void setReadersCache(SimpleCache> readersCache) + { + this.readersCache = readersCache; + } + + + /** + * @param readersDeniedCache the readersDeniedCache to set + */ + public void setReadersDeniedCache(SimpleCache> readersDeniedCache) + { + this.readersDeniedCache = readersDeniedCache; + } + + /** + * Set the policy component + * + * @param policyComponent + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Cache clear on move node + * + * @param oldChildAssocRef + * @param newChildAssocRef + */ + public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef) + { + accessCache.clear(); + } + + /** + * Cache clear on create of a child association from an authority container. + * + * @param childAssocRef + */ + public void onCreateChildAssociation(ChildAssociationRef childAssocRef) + { + accessCache.clear(); + } + + /** + * Cache clear on delete of a child association from an authority container. + * + * @param childAssocRef + */ + public void beforeDeleteChildAssociation(ChildAssociationRef childAssocRef) + { + accessCache.clear(); + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); + PropertyCheck.mandatory(this, "modelDAO", modelDAO); + PropertyCheck.mandatory(this, "nodeService", nodeService); + PropertyCheck.mandatory(this, "ownableService", ownableService); + PropertyCheck.mandatory(this, "permissionsDaoComponent", permissionsDaoComponent); + PropertyCheck.mandatory(this, "authorityService", authorityService); + PropertyCheck.mandatory(this, "accessCache", accessCache); + PropertyCheck.mandatory(this, "readersCache", readersCache); + PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "aclDaoComponent", aclDaoComponent); + + allPermissionReference = getPermissionReference(ALL_PERMISSIONS); + } + + /** + * No-op + */ + @Override + protected void onShutdown(ApplicationEvent event) + { + } + + public void init() + { + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onMoveNode"), ContentModel.TYPE_BASE, new JavaBehaviour(this, "onMoveNode")); + + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "onCreateChildAssociation"), ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "onCreateChildAssociation")); + policyComponent.bindClassBehaviour(QName.createQName(NamespaceService.ALFRESCO_URI, "beforeDeleteChildAssociation"), ContentModel.TYPE_AUTHORITY_CONTAINER, new JavaBehaviour(this, "beforeDeleteChildAssociation")); + } + + // + // Permissions Service + // + + public String getOwnerAuthority() + { + return OWNER_AUTHORITY; + } + + public String getAllAuthorities() + { + return ALL_AUTHORITIES; + } + + public String getAllPermission() + { + return ALL_PERMISSIONS; + } + + public Set getPermissions(NodeRef nodeRef) + { + return getAllPermissionsImpl(nodeRef, true, true); + } + + public Set getAllSetPermissions(NodeRef nodeRef) + { + HashSet accessPermissions = new HashSet(); + NodePermissionEntry nodePremissionEntry = getSetPermissions(nodeRef); + for (PermissionEntry pe : nodePremissionEntry.getPermissionEntries()) + { + accessPermissions.add(new AccessPermissionImpl(getPermission(pe.getPermissionReference()), pe.getAccessStatus(), pe.getAuthority(), pe.getPosition())); + } + return accessPermissions; + } + + public Set getAllSetPermissions(StoreRef storeRef) + { + HashSet accessPermissions = new HashSet(); + NodePermissionEntry nodePremissionEntry = getSetPermissions(storeRef); + for (PermissionEntry pe : nodePremissionEntry.getPermissionEntries()) + { + accessPermissions.add(new AccessPermissionImpl(getPermission(pe.getPermissionReference()), pe.getAccessStatus(), pe.getAuthority(), pe.getPosition())); + } + return accessPermissions; + } + + private Set getAllPermissionsImpl(NodeRef nodeRef, boolean includeTrue, boolean includeFalse) + { + String userName = AuthenticationUtil.getRunAsUser(); + HashSet accessPermissions = new HashSet(); + for (PermissionReference pr : getSettablePermissionReferences(nodeRef)) + { + if (hasPermission(nodeRef, pr) == AccessStatus.ALLOWED) + { + accessPermissions.add(new AccessPermissionImpl(getPermission(pr), AccessStatus.ALLOWED, userName, -1)); + } + else + { + if (includeFalse) + { + accessPermissions.add(new AccessPermissionImpl(getPermission(pr), AccessStatus.DENIED, userName, -1)); + } + } + } + return accessPermissions; + } + + public Set getSettablePermissions(NodeRef nodeRef) + { + Set settable = getSettablePermissionReferences(nodeRef); + Set strings = new HashSet(settable.size()); + for (PermissionReference pr : settable) + { + strings.add(getPermission(pr)); + } + return strings; + } + + public Set getSettablePermissions(QName type) + { + Set settable = getSettablePermissionReferences(type); + Set strings = new LinkedHashSet(settable.size()); + for (PermissionReference pr : settable) + { + strings.add(getPermission(pr)); + } + return strings; + } + + public NodePermissionEntry getSetPermissions(NodeRef nodeRef) + { + return permissionsDaoComponent.getPermissions(tenantService.getName(nodeRef)); + } + + public NodePermissionEntry getSetPermissions(StoreRef storeRef) + { + return permissionsDaoComponent.getPermissions(storeRef); + } + + public AccessStatus hasPermission(NodeRef passedNodeRef, final PermissionReference permIn) + { + // If the node ref is null there is no sensible test to do - and there + // must be no permissions + // - so we allow it + if (passedNodeRef == null) + { + return AccessStatus.ALLOWED; + } + + // If the permission is null we deny + if (permIn == null) + { + return AccessStatus.DENIED; + } + + // AVM nodes - test for existence underneath + if (passedNodeRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + return doAvmCan(passedNodeRef, permIn); + } + + // Note: if we're directly accessing a frozen state (version) node (ie. in the 'version' store) we need to check permissions for the versioned node (ie. in the 'live' store) + if (isVersionNodeRef(passedNodeRef)) + { + passedNodeRef = convertVersionNodeRefToVersionedNodeRef(VersionUtil.convertNodeRef(passedNodeRef)); + } + + // Allow permissions for nodes that do not exist + if (!nodeService.exists(passedNodeRef)) + { + return AccessStatus.ALLOWED; + } + + final NodeRef nodeRef = tenantService.getName(passedNodeRef); + + final PermissionReference perm; + if (permIn.equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + perm = getAllPermissionReference(); + } + else + { + perm = permIn; + } + + if (AuthenticationUtil.getRunAsUser() == null) + { + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + return AccessStatus.ALLOWED; + } + + // New ACLs + + AccessControlListProperties properties = permissionsDaoComponent.getAccessControlListProperties(nodeRef); + if ((properties != null) && (properties.getAclType() != null) && (properties.getAclType() != ACLType.OLD)) + { + QName typeQname = nodeService.getType(nodeRef); + Set aspectQNames = nodeService.getAspects(nodeRef); + PermissionContext context = new PermissionContext(typeQname); + context.getAspects().addAll(aspectQNames); + Authentication auth = AuthenticationUtil.getRunAsAuthentication(); + if (auth != null) + { + String user = AuthenticationUtil.getRunAsUser(); + for (String dynamicAuthority : getDynamicAuthorities(auth, nodeRef, perm)) + { + context.addDynamicAuthorityAssignment(user, dynamicAuthority); + } + } + return hasPermission(properties.getId(), context, perm); + } + + // Get the current authentications + // Use the smart authentication cache to improve permissions performance + Authentication auth = AuthenticationUtil.getRunAsAuthentication(); + final Set authorisations = getAuthorisations(auth, nodeRef, perm); + + // If the node does not support the given permission there is no point + // doing the test + Set available = AuthenticationUtil.runAs(new RunAsWork>() + { + public Set doWork() throws Exception + { + return modelDAO.getAllPermissions(nodeRef); + } + + }, AuthenticationUtil.getSystemUserName()); + + available.add(getAllPermissionReference()); + available.add(OLD_ALL_PERMISSIONS_REFERENCE); + + final Serializable key = generateKey(authorisations, nodeRef, perm, CacheType.HAS_PERMISSION); + if (!(available.contains(perm))) + { + accessCache.put(key, AccessStatus.DENIED); + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + return AccessStatus.ALLOWED; + } + + return AuthenticationUtil.runAs(new RunAsWork() + { + + public AccessStatus doWork() throws Exception + { + + AccessStatus status = accessCache.get(key); + if (status != null) + { + return status; + } + + // + // TODO: Dynamic permissions via evaluators + // + + /* + * Does the current authentication have the supplied permission on the given node. + */ + + QName typeQname = nodeService.getType(nodeRef); + Set aspectQNames = nodeService.getAspects(nodeRef); + + NodeTest nt = new NodeTest(perm, typeQname, aspectQNames); + boolean result = nt.evaluate(authorisations, nodeRef); + if (log.isDebugEnabled()) + { + log.debug("Permission <" + + perm + "> is " + (result ? "allowed" : "denied") + " for " + AuthenticationUtil.getRunAsUser() + " on node " + + nodeService.getPath(nodeRef)); + } + + status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED; + accessCache.put(key, status); + return status; + } + }, AuthenticationUtil.getSystemUserName()); + + } + + private AccessStatus doAvmCan(NodeRef nodeRef, PermissionReference permission) + { + org.alfresco.util.Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + int version = avmVersionPath.getFirst(); + String path = avmVersionPath.getSecond(); + boolean result = AVMRepository.GetInstance().can(nodeRef.getStoreRef().getIdentifier(), version, path, permission.getName()); + AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED; + return status; + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.service.cmr.security.PermissionService#hasPermission(java.lang.Long, java.lang.String, + * java.lang.String) + */ + public AccessStatus hasPermission(Long aclID, PermissionContext context, String permission) + { + return hasPermission(aclID, context, getPermissionReference(permission)); + } + + + private AccessStatus hasPermission(Long aclId, PermissionContext context, PermissionReference permission) + { + if (aclId == null) + { + // Enforce store ACLs if set - the AVM default was to "allow" if there are no permissions set ... + if (context.getStoreAcl() == null) + { + return AccessStatus.ALLOWED; + } + else + { + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + return AccessStatus.ALLOWED; + } + + Authentication auth = AuthenticationUtil.getRunAsAuthentication(); + if (auth == null) + { + throw new IllegalStateException("Unauthenticated"); + } + Set storeAuthorisations = getAuthorisations(auth, (PermissionContext) null); + QName typeQname = context.getType(); + Set aspectQNames = context.getAspects(); + AclTest aclTest = new AclTest(permission, typeQname, aspectQNames); + boolean result = aclTest.evaluate(storeAuthorisations, context.getStoreAcl(), context); + AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED; + return status; + } + } + + if (permission == null) + { + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.getRunAsUser() == null) + { + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.getRunAsUser().equals(AuthenticationUtil.getSystemUserName())) + { + return AccessStatus.ALLOWED; + } + + // Get the current authentications + // Use the smart authentication cache to improve permissions performance + Authentication auth = AuthenticationUtil.getRunAsAuthentication(); + if (auth == null) + { + throw new IllegalStateException("Unauthenticated"); + } + + Set authorisations = getAuthorisations(auth, context); + + // If the node does not support the given permission there is no point + // doing the test + + final QName typeQname = context.getType(); + final Set aspectQNames = context.getAspects(); + + Set available = AuthenticationUtil.runAs(new RunAsWork>() + { + public Set doWork() throws Exception + { + return modelDAO.getAllPermissions(typeQname, aspectQNames); + } + + }, AuthenticationUtil.getSystemUserName()); + available.add(getAllPermissionReference()); + available.add(OLD_ALL_PERMISSIONS_REFERENCE); + + if (!(available.contains(permission))) + { + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + return AccessStatus.ALLOWED; + } + + if (permission.equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + permission = getAllPermissionReference(); + } + + boolean result; + if (context.getStoreAcl() == null) + { + AclTest aclTest = new AclTest(permission, typeQname, aspectQNames); + result = aclTest.evaluate(authorisations, aclId, context); + } + else + { + Set storeAuthorisations = getAuthorisations(auth, (PermissionContext) null); + AclTest aclTest = new AclTest(permission, typeQname, aspectQNames); + result = aclTest.evaluate(authorisations, aclId, context) && aclTest.evaluate(storeAuthorisations, context.getStoreAcl(), context); + } + AccessStatus status = result ? AccessStatus.ALLOWED : AccessStatus.DENIED; + return status; + + } + + /** + * Control permissions cache - only used when we do old style permission evaluations + * - which should only be in DM stores where no permissions have been set + * + * @author andyh + * + */ + enum CacheType + { + /** + * cache full check + */ + HAS_PERMISSION, + /** + * Cache single permission check + */ + SINGLE_PERMISSION, + /** + * Cache single permission check for global permission checks + */ + SINGLE_PERMISSION_GLOBAL; + } + + /** + * Key for a cache object is built from all the known Authorities (which can change dynamically so they must all be + * used) the NodeRef ID and the permission reference itself. This gives a unique key for each permission test. + */ + static Serializable generateKey(Set auths, NodeRef nodeRef, PermissionReference perm, CacheType type) + { + LinkedHashSet key = new LinkedHashSet(); + key.add(perm.toString()); + // We will just have to key our dynamic sets by username. We wrap it so as not to be confused with a static set + if (auths instanceof AuthorityServiceImpl.UserAuthoritySet) + { + key.add((Serializable)Collections.singleton(((AuthorityServiceImpl.UserAuthoritySet)auths).getUsername())); + } + else + { + key.addAll(auths); + } + key.add(nodeRef); + key.add(type); + return key; + } + + /** + * Get the core authorisations for this {@code auth}. If {@code null} this + * will be an empty set. Otherwise it will be a Lazy loaded Set of authorities + * from the authority node structure PLUS any granted authorities. + */ + private Set getCoreAuthorisations(Authentication auth) + { + if (auth == null) + { + return Collections.emptySet(); + } + + User user = (User) auth.getPrincipal(); + String username = user.getUsername(); + Set auths = authorityService.getAuthoritiesForUser(username); + + auths.add(username); + + for (GrantedAuthority grantedAuthority : auth.getAuthorities()) + { + auths.add(grantedAuthority.getAuthority()); + } + return auths; + } + + /** + * Get the authorisations for the currently authenticated user + * + * @param auth + * @return the set of authorisations + */ + private Set getAuthorisations(Authentication auth, NodeRef nodeRef, PermissionReference required) + { + Set auths = getCoreAuthorisations(auth); + if (auth != null) + { + auths.addAll(getDynamicAuthorities(auth, nodeRef, required)); + } + return auths; + } + + private Set getDynamicAuthorities(Authentication auth, NodeRef nodeRef, PermissionReference required) + { + Set dynAuths = new HashSet(64); + User user = (User) auth.getPrincipal(); + String username = user.getUsername(); + + nodeRef = tenantService.getName(nodeRef); + if (nodeRef != null) + { + if (dynamicAuthorities != null) + { + for (DynamicAuthority da : dynamicAuthorities) + { + Set requiredFor = da.requiredFor(); + if ((requiredFor == null) || (requiredFor.contains(required))) + { + if (da.hasAuthority(nodeRef, username)) + { + dynAuths.add(da.getAuthority()); + } + } + } + } + } + return dynAuths; + } + + private Set getAuthorisations(Authentication auth, PermissionContext context) + { + Set auths = getCoreAuthorisations(auth); + if (auth != null) + { + if (context != null) + { + auths.addAll(getDynamicAuthorities(auth, context, auths)); + } + } + return auths; + } + + private Set getDynamicAuthorities(Authentication auth, PermissionContext context, Set auths) + { + Set dynAuths = new HashSet(); + Map> dynamicAuthorityAssignments = context.getDynamicAuthorityAssignment(); + for (String dynKey : dynamicAuthorityAssignments.keySet()) + { + if (auths.contains(dynKey)) + { + Set dynos = dynamicAuthorityAssignments.get(dynKey); + if (dynos != null) + { + dynAuths.addAll(dynos); + } + } + } + return dynAuths; + } + + public NodePermissionEntry explainPermission(NodeRef nodeRef, PermissionReference perm) + { + // TODO Auto-generated method stub + return null; + } + + public void clearPermission(StoreRef storeRef, String authority) + { + permissionsDaoComponent.deletePermissions(storeRef, authority); + accessCache.clear(); + } + + public void deletePermission(StoreRef storeRef, String authority, String perm) + { + deletePermission(storeRef, authority, getPermissionReference(perm)); + } + + private void deletePermission(StoreRef storeRef, String authority, PermissionReference perm) + { + permissionsDaoComponent.deletePermission(storeRef, authority, perm); + accessCache.clear(); + } + + public void deletePermissions(StoreRef storeRef) + { + permissionsDaoComponent.deletePermissions(storeRef); + accessCache.clear(); + } + + public void setPermission(StoreRef storeRef, String authority, String perm, boolean allow) + { + setPermission(storeRef, authority, getPermissionReference(perm), allow); + } + + private void setPermission(StoreRef storeRef, String authority, PermissionReference permission, boolean allow) + { + permissionsDaoComponent.setPermission(storeRef, authority, permission, allow); + accessCache.clear(); + } + + public void deletePermissions(NodeRef nodeRef) + { + permissionsDaoComponent.deletePermissions(tenantService.getName(nodeRef)); + accessCache.clear(); + } + + public void deletePermissions(NodePermissionEntry nodePermissionEntry) + { + permissionsDaoComponent.deletePermissions(tenantService.getName(nodePermissionEntry.getNodeRef())); + accessCache.clear(); + } + + /** + * @see #deletePermission(NodeRef, String, PermissionReference) + */ + public void deletePermission(PermissionEntry permissionEntry) + { + NodeRef nodeRef = permissionEntry.getNodeRef(); + String authority = permissionEntry.getAuthority(); + PermissionReference permission = permissionEntry.getPermissionReference(); + deletePermission(nodeRef, authority, permission); + } + + private void deletePermission(NodeRef nodeRef, String authority, PermissionReference perm) + { + permissionsDaoComponent.deletePermission(tenantService.getName(nodeRef), authority, perm); + accessCache.clear(); + } + + public void clearPermission(NodeRef nodeRef, String authority) + { + permissionsDaoComponent.deletePermissions(tenantService.getName(nodeRef), authority); + accessCache.clear(); + } + + private void setPermission(NodeRef nodeRef, String authority, PermissionReference perm, boolean allow) + { + permissionsDaoComponent.setPermission(tenantService.getName(nodeRef), authority, perm, allow); + accessCache.clear(); + } + + public void setPermission(PermissionEntry permissionEntry) + { + // TODO - not MT-enabled nodeRef - currently only used by tests + permissionsDaoComponent.setPermission(permissionEntry); + accessCache.clear(); + } + + public void setPermission(NodePermissionEntry nodePermissionEntry) + { + // TODO - not MT-enabled nodeRef- currently only used by tests + permissionsDaoComponent.setPermission(nodePermissionEntry); + accessCache.clear(); + } + + public void setInheritParentPermissions(NodeRef nodeRef, boolean inheritParentPermissions) + { + NodeRef actualRef = tenantService.getName(nodeRef); + permissionsDaoComponent.setInheritParentPermissions(actualRef, inheritParentPermissions); + accessCache.clear(); + } + + /** + * @see org.alfresco.service.cmr.security.PermissionService#getInheritParentPermissions(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean getInheritParentPermissions(NodeRef nodeRef) + { + return permissionsDaoComponent.getInheritParentPermissions(tenantService.getName(nodeRef)); + } + + public PermissionReference getPermissionReference(QName qname, String permissionName) + { + return modelDAO.getPermissionReference(qname, permissionName); + } + + public PermissionReference getAllPermissionReference() + { + return allPermissionReference; + } + + public String getPermission(PermissionReference permissionReference) + { + if (modelDAO.isUnique(permissionReference)) + { + return permissionReference.getName(); + } + else + { + return permissionReference.toString(); + } + } + + public PermissionReference getPermissionReference(String permissionName) + { + return modelDAO.getPermissionReference(null, permissionName); + } + + public Set getSettablePermissionReferences(QName type) + { + return modelDAO.getExposedPermissions(type); + } + + public Set getSettablePermissionReferences(NodeRef nodeRef) + { + return modelDAO.getExposedPermissions(tenantService.getName(nodeRef)); + } + + public void deletePermission(NodeRef nodeRef, String authority, String perm) + { + deletePermission(nodeRef, authority, getPermissionReference(perm)); + } + + public AccessStatus hasPermission(NodeRef nodeRef, String perm) + { + return hasPermission(nodeRef, getPermissionReference(perm)); + } + + public void setPermission(NodeRef nodeRef, String authority, String perm, boolean allow) + { + setPermission(nodeRef, authority, getPermissionReference(perm), allow); + } + + public void deletePermissions(String recipient) + { + permissionsDaoComponent.deletePermissions(recipient); + accessCache.clear(); + } + + /** + * Optimised read permission evaluation + * caveats: + * doesn't take into account dynamic authorities/groups + * doesn't take into account node types/aspects for permissions + * + */ + public AccessStatus hasReadPermission(NodeRef nodeRef) + { + AccessStatus status = AccessStatus.DENIED; + + // If the node ref is null there is no sensible test to do - and there + // must be no permissions + // - so we allow it + if (nodeRef == null) + { + return AccessStatus.ALLOWED; + } + + // Allow permissions for nodes that do not exist + if (!nodeService.exists(nodeRef)) + { + return AccessStatus.ALLOWED; + } + + String runAsUser = AuthenticationUtil.getRunAsUser(); + if (runAsUser == null) + { + return AccessStatus.DENIED; + } + + if (AuthenticationUtil.isRunAsUserTheSystemUser()) + { + return AccessStatus.ALLOWED; + } + + // any dynamic authorities other than those defined in the default permissions model with full + // control or read permission force hasPermission check + Boolean forceHasPermission = (Boolean)AlfrescoTransactionSupport.getResource("forceHasPermission"); + if(forceHasPermission == null) + { + for(DynamicAuthority dynamicAuthority : dynamicAuthorities) + { + String authority = dynamicAuthority.getAuthority(); + Set requiredFor = dynamicAuthority.requiredFor(); + if(authority != PermissionService.OWNER_AUTHORITY && + authority != PermissionService.ADMINISTRATOR_AUTHORITY && + authority != PermissionService.LOCK_OWNER_AUTHORITY && + (requiredFor == null || + requiredFor.contains(modelDAO.getPermissionReference(null, PermissionService.FULL_CONTROL)) || + requiredFor.contains(modelDAO.getPermissionReference(null, PermissionService.READ)))) + { + forceHasPermission = Boolean.TRUE; + break; + } + } + AlfrescoTransactionSupport.bindResource("forceHasPermission", forceHasPermission); + } + + if(forceHasPermission == Boolean.TRUE) + { + return hasPermission(nodeRef, PermissionService.READ); + } + + Long aclID = nodeService.getNodeAclId(nodeRef); + if(aclID == null) + { + // ACLID is null - need to call default permissions evaluation + // This will end up calling the old-style ACL code that walks up the ACL tree + status = hasPermission(nodeRef, getPermissionReference(null, PermissionService.READ)); + } + else + { + status = (canRead(aclID) == AccessStatus.ALLOWED || + adminRead() == AccessStatus.ALLOWED || + ownerRead(runAsUser, nodeRef) == AccessStatus.ALLOWED) ? AccessStatus.ALLOWED : AccessStatus.DENIED; + } + + return status; + } + + private AccessStatus adminRead() + { + AccessStatus result = AccessStatus.DENIED; + + Set authorisations = getAuthorisations(); + if(authorisations.contains(AuthenticationUtil.getAdminRoleName())) + { + result = AccessStatus.ALLOWED; + } + + // ROLE_ADMINISTRATOR authority has FULL_CONTROL in permissionDefinitions + // so we don't need to check node requirements + return result; + } + + private AccessStatus ownerRead(String username, NodeRef nodeRef) + { + // Reviewed the behaviour of deny and ownership with Mike F + // ATM ownership takes precendence over READ deny + // TODO: check that global owner rights are set + + AccessStatus result = AccessStatus.DENIED; + + String owner = ownableService.getOwner(nodeRef); + if(owner == null) + { + // TODO node may not have auditable aspect and hence creator property + result = AccessStatus.DENIED; + } + + // is the user the owner of the node? + if(EqualsHelper.nullSafeEquals(username, owner)) + { + // ROLE_OWNER authority has FULL_CONTROL in permissionDefinitions + // so we don't need to check node requirements + return AccessStatus.ALLOWED; + } + + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getReaders(Long aclId) + { + Set aclReaders = readersCache.get(aclId); + if (aclReaders == null) + { + aclReaders = buildReaders(aclId); + readersCache.put(aclId, aclReaders); + } + return aclReaders; + } + + /** + * Builds the set of authorities who can read the given ACL. No caching is done here. + * + * @return an unmodifiable set of authorities + */ + private Set buildReaders(Long aclId) + { + AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); + if (acl == null) + { + return Collections.emptySet(); + } + + HashSet assigned = new HashSet(); + HashSet readers = new HashSet(); + + for (AccessControlEntry ace : acl.getEntries()) + { + assigned.add(ace.getAuthority()); + } + + for (String authority : assigned) + { + UnconditionalAclTest test = new UnconditionalAclTest(getPermissionReference(PermissionService.READ)); + UnconditionalAclTest rmTest = new UnconditionalAclTest(getPermissionReference(RMPermissionModel.READ_RECORDS)); + if (test.evaluate(authority, aclId) || rmTest.evaluate(authority, aclId)) + { + readers.add(authority); + } + } + + return Collections.unmodifiableSet(readers); + } + + /** + * @param aclId + * @return set of authorities with read permission on the ACL + */ + private Set buildReadersDenied(Long aclId) + { + HashSet assigned = new HashSet(); + HashSet denied = new HashSet(); + AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); + + if (acl == null) + { + return denied; + } + + for (AccessControlEntry ace : acl.getEntries()) + { + assigned.add(ace.getAuthority()); + } + + for(String authority : assigned) + { + UnconditionalDeniedAclTest test = new UnconditionalDeniedAclTest(getPermissionReference(PermissionService.READ)); + if(test.evaluate(authority, aclId)) + { + denied.add(authority); + } + } + + return denied; + } + + private AccessStatus canRead(Long aclId) + { + Set authorities = getAuthorisations(); + + // test denied + + if(anyDenyDenies) + { + + Set aclReadersDenied = readersDeniedCache.get(aclId); + if(aclReadersDenied == null) + { + aclReadersDenied = buildReadersDenied(aclId); + readersDeniedCache.put(aclId, aclReadersDenied); + } + + for(String auth : aclReadersDenied) + { + if(authorities.contains(auth)) + { + return AccessStatus.DENIED; + } + } + + } + + + + // test acl readers + Set aclReaders = getReaders(aclId); + + for(String auth : aclReaders) + { + if(authorities.contains(auth)) + { + return AccessStatus.ALLOWED; + } + } + + return AccessStatus.DENIED; + } + + // + // SUPPORT CLASSES + // + + /** + * Support class to test the permission on a node. + * + * Not fixed up for deny as should not be used + * + * @author Andy Hind + */ + private class NodeTest + { + /* + * The required permission. + */ + PermissionReference required; + + /* + * Granters of the permission + */ + Set granters; + + /* + * The additional permissions required at the node level. + */ + Set nodeRequirements = new HashSet(); + + /* + * The additional permissions required on the parent. + */ + Set parentRequirements = new HashSet(); + + /* + * The permissions required on all children . + */ + Set childrenRequirements = new HashSet(); + + /* + * The type name of the node. + */ + QName typeQName; + + /* + * The aspects set on the node. + */ + Set aspectQNames; + + /* + * Constructor just gets the additional requirements + */ + NodeTest(PermissionReference required, QName typeQName, Set aspectQNames) + { + this.required = required; + this.typeQName = typeQName; + this.aspectQNames = aspectQNames; + + // Set the required node permissions + if (required.equals(getPermissionReference(ALL_PERMISSIONS))) + { + nodeRequirements = modelDAO.getRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), typeQName, aspectQNames, RequiredPermission.On.NODE); + } + else + { + nodeRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.NODE); + } + + parentRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.PARENT); + + childrenRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.CHILDREN); + + // Find all the permissions that grant the allowed permission + // All permissions are treated specially. + granters = new LinkedHashSet(128, 1.0f); + granters.addAll(modelDAO.getGrantingPermissions(required)); + granters.add(getAllPermissionReference()); + granters.add(OLD_ALL_PERMISSIONS_REFERENCE); + } + + /** + * External hook point + * @return true if allowed + */ + boolean evaluate(Set authorisations, NodeRef nodeRef) + { + Set> denied = new HashSet>(); + return evaluate(authorisations, nodeRef, denied, null); + } + + /** + * Internal hook point for recursion + * @return true if allowed + */ + boolean evaluate(Set authorisations, NodeRef nodeRef, Set> denied, MutableBoolean recursiveIn) + { + // Do we defer our required test to a parent (yes if not null) + MutableBoolean recursiveOut = null; + + Set> locallyDenied = new HashSet>(); + locallyDenied.addAll(denied); + locallyDenied.addAll(getDenied(nodeRef)); + + // Start out true and "and" all other results + boolean success = true; + + // Check the required permissions but not for sets they rely on + // their underlying permissions + if (modelDAO.checkPermission(required)) + { + if (parentRequirements.contains(required)) + { + if (checkGlobalPermissions(authorisations) || checkRequired(authorisations, nodeRef, locallyDenied)) + { + // No need to do the recursive test as it has been found + if (recursiveIn != null) + { + recursiveIn.setValue(true); + } + } + else + { + // Much cheaper to do this as we go then check all the + // stack values for each parent + recursiveOut = new MutableBoolean(false); + } + } + else + { + // We have to do the test as no parent will help us out + success &= hasSinglePermission(authorisations, nodeRef); + } + if (!success) + { + return false; + } + } + + // Check the other permissions required on the node + for (PermissionReference pr : nodeRequirements) + { + // Build a new test + NodeTest nt = new NodeTest(pr, typeQName, aspectQNames); + success &= nt.evaluate(authorisations, nodeRef, locallyDenied, null); + if (!success) + { + return false; + } + } + + // Check the permission required of the parent + + if (success) + { + ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef); + if (car.getParentRef() != null) + { + + NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(car.getChildRef()); + if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) + { + + locallyDenied.addAll(getDenied(car.getParentRef())); + for (PermissionReference pr : parentRequirements) + { + if (pr.equals(required)) + { + // Recursive permission + success &= this.evaluate(authorisations, car.getParentRef(), locallyDenied, recursiveOut); + if ((recursiveOut != null) && recursiveOut.getValue()) + { + if (recursiveIn != null) + { + recursiveIn.setValue(true); + } + } + } + else + { + NodeTest nt = new NodeTest(pr, typeQName, aspectQNames); + success &= nt.evaluate(authorisations, car.getParentRef(), locallyDenied, null); + } + + if (!success) + { + return false; + } + } + } + } + } + + if ((recursiveOut != null) && (!recursiveOut.getValue())) + { + // The required authentication was not resolved in recursion + return false; + } + + // Check permissions required of children + if (childrenRequirements.size() > 0) + { + List childAssocRefs = nodeService.getChildAssocs(nodeRef); + for (PermissionReference pr : childrenRequirements) + { + for (ChildAssociationRef child : childAssocRefs) + { + success &= (hasPermission(child.getChildRef(), pr) == AccessStatus.ALLOWED); + if (!success) + { + return false; + } + } + } + } + + return success; + } + + boolean hasSinglePermission(Set authorisations, NodeRef nodeRef) + { + nodeRef = tenantService.getName(nodeRef); + + Serializable key = generateKey(authorisations, nodeRef, this.required, CacheType.SINGLE_PERMISSION_GLOBAL); + + AccessStatus status = accessCache.get(key); + if (status != null) + { + return status == AccessStatus.ALLOWED; + } + + // Check global permission + + if (checkGlobalPermissions(authorisations)) + { + accessCache.put(key, AccessStatus.ALLOWED); + return true; + } + + Set> denied = new HashSet>(); + + return hasSinglePermission(authorisations, nodeRef, denied); + + } + + boolean hasSinglePermission(Set authorisations, NodeRef nodeRef, Set> denied) + { + nodeRef = tenantService.getName(nodeRef); + + // Add any denied permission to the denied list - these can not + // then + // be used to given authentication. + // A -> B -> C + // If B denies all permissions to any - allowing all permissions + // to + // andy at node A has no effect + + denied.addAll(getDenied(nodeRef)); + + // Cache non denied + Serializable key = null; + if (denied.size() == 0) + { + key = generateKey(authorisations, nodeRef, this.required, CacheType.SINGLE_PERMISSION); + } + if (key != null) + { + AccessStatus status = accessCache.get(key); + if (status != null) + { + return status == AccessStatus.ALLOWED; + } + } + + // If the current node allows the permission we are done + // The test includes any parent or ancestor requirements + if (checkRequired(authorisations, nodeRef, denied)) + { + if (key != null) + { + accessCache.put(key, AccessStatus.ALLOWED); + } + return true; + } + + // Permissions are only evaluated up the primary parent chain + // TODO: Do not ignore non primary permissions + ChildAssociationRef car = nodeService.getPrimaryParent(nodeRef); + + // Build the next element of the evaluation chain + if (car.getParentRef() != null) + { + NodePermissionEntry nodePermissions = permissionsDaoComponent.getPermissions(nodeRef); + if ((nodePermissions == null) || (nodePermissions.inheritPermissions())) + { + if (hasSinglePermission(authorisations, car.getParentRef(), denied)) + { + if (key != null) + { + accessCache.put(key, AccessStatus.ALLOWED); + } + return true; + } + else + { + if (key != null) + { + accessCache.put(key, AccessStatus.DENIED); + } + return false; + } + } + else + { + if (key != null) + { + accessCache.put(key, AccessStatus.DENIED); + } + return false; + } + } + else + { + if (key != null) + { + accessCache.put(key, AccessStatus.DENIED); + } + return false; + } + } + + /** + * Check if we have a global permission + * + * @return true if allowed + */ + private boolean checkGlobalPermissions(Set authorisations) + { + for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) + { + if (isGranted(pe, authorisations, null)) + { + return true; + } + } + return false; + } + + /** + * Get the list of permissions denied for this node. + * + * @return the list of denied permissions + */ + Set> getDenied(NodeRef nodeRef) + { + Set> deniedSet = new HashSet>(); + + // Loop over all denied permissions + NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef); + if (nodeEntry != null) + { + for (PermissionEntry pe : nodeEntry.getPermissionEntries()) + { + if (pe.isDenied()) + { + // All the sets that grant this permission must be + // denied + // Note that granters includes the orginal permission + Set granters = modelDAO.getGrantingPermissions(pe.getPermissionReference()); + for (PermissionReference granter : granters) + { + deniedSet.add(new Pair(pe.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = modelDAO.getGranteePermissions(pe.getPermissionReference()); + for (PermissionReference grantee : grantees) + { + deniedSet.add(new Pair(pe.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (pe.getPermissionReference().equals(getAllPermissionReference()) || pe.getPermissionReference().equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : modelDAO.getAllPermissions(nodeRef)) + { + deniedSet.add(new Pair(pe.getAuthority(), deny)); + } + } + } + } + } + return deniedSet; + } + + /** + * Check that a given authentication is available on a node + * + * @return true if the check is required + */ + boolean checkRequired(Set authorisations, NodeRef nodeRef, Set> denied) + { + NodePermissionEntry nodeEntry = permissionsDaoComponent.getPermissions(nodeRef); + + // No permissions set - short cut to deny + if (nodeEntry == null) + { + return false; + } + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (PermissionEntry pe : nodeEntry.getPermissionEntries()) + { + if (isGranted(pe, authorisations, denied)) + { + return true; + } + } + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isGranted(PermissionEntry pe, Set authorisations, Set> denied) + { + // If the permission entry denies then we just deny + if (pe.isDenied()) + { + return false; + } + + // The permission is allowed but we deny it as it is in the denied + // set + + if (denied != null) + { + Pair specific = new Pair(pe.getAuthority(), required); + if (denied.contains(specific)) + { + return false; + } + } + + // any deny denies + + if (anyDenyDenies) + { + if (denied != null) + { + for (String auth : authorisations) + { + Pair specific = new Pair(auth, required); + if (denied.contains(specific)) + { + return false; + } + for (PermissionReference perm : granters) + { + specific = new Pair(auth, perm); + if (denied.contains(specific)) + { + return false; + } + } + } + } + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authorisations.contains(pe.getAuthority()) && granters.contains(pe.getPermissionReference())) + { + { + return true; + } + } + + // Default deny + return false; + } + + } + + /** + * Test a permission in the context of the new ACL implementation. All components of the ACL are in the object - + * there is no need to walk up the parent chain. Parent conditions cna not be applied as there is no context to do + * this. Child conditions can not be applied as there is no context to do this + * + * @author andyh + */ + + private class AclTest + { + /* + * The required permission. + */ + PermissionReference required; + + /* + * Granters of the permission + */ + Set granters; + + /* + * The additional permissions required at the node level. + */ + Set nodeRequirements = new HashSet(); + + /* + * The type name of the node. + */ + QName typeQName; + + /* + * The aspects set on the node. + */ + Set aspectQNames; + + /* + * Constructor just gets the additional requirements + */ + AclTest(PermissionReference required, QName typeQName, Set aspectQNames) + { + this.required = required; + this.typeQName = typeQName; + this.aspectQNames = aspectQNames; + + // Set the required node permissions + if (required.equals(getPermissionReference(ALL_PERMISSIONS))) + { + nodeRequirements = modelDAO.getRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), typeQName, aspectQNames, RequiredPermission.On.NODE); + } + else + { + nodeRequirements = modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.NODE); + } + + if (modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.PARENT).size() > 0) + { + throw new IllegalStateException("Parent permissions can not be checked for an acl"); + } + + if (modelDAO.getRequiredPermissions(required, typeQName, aspectQNames, RequiredPermission.On.CHILDREN).size() > 0) + { + throw new IllegalStateException("Child permissions can not be checked for an acl"); + } + + // Find all the permissions that grant the allowed permission + // All permissions are treated specially. + granters = new LinkedHashSet(128, 1.0f); + granters.addAll(modelDAO.getGrantingPermissions(required)); + granters.add(getAllPermissionReference()); + granters.add(OLD_ALL_PERMISSIONS_REFERENCE); + } + + /** + * Internal hook point for recursion + * + * @param authorisations + * @param nodeRef + * @param denied + * @param recursiveIn + * @return true if granted + */ + boolean evaluate(Set authorisations, Long aclId, PermissionContext context) + { + // Start out true and "and" all other results + boolean success = true; + + // Check the required permissions but not for sets they rely on + // their underlying permissions + if (modelDAO.checkPermission(required)) + { + + // We have to do the test as no parent will help us out + success &= hasSinglePermission(authorisations, aclId, context); + + if (!success) + { + return false; + } + } + + // Check the other permissions required on the node + for (PermissionReference pr : nodeRequirements) + { + // Build a new test + AclTest nt = new AclTest(pr, typeQName, aspectQNames); + success &= nt.evaluate(authorisations, aclId, context); + if (!success) + { + return false; + } + } + + return success; + } + + boolean hasSinglePermission(Set authorisations, Long aclId, PermissionContext context) + { + // Check global permission + + if (checkGlobalPermissions(authorisations)) + { + return true; + } + + return checkRequired(authorisations, aclId, context); + + } + + /** + * Check if we have a global permission + * + * @param authorisations + * @return true if granted + */ + private boolean checkGlobalPermissions(Set authorisations) + { + for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) + { + if (isGranted(pe, authorisations)) + { + return true; + } + } + return false; + } + + /** + * Check that a given authentication is available on a node + * + * @param authorisations + * @param nodeRef + * @param denied + * @return true if a check is required + */ + boolean checkRequired(Set authorisations, Long aclId, PermissionContext context) + { + AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); + + if (acl == null) + { + return false; + } + + if(anyDenyDenies) + { + Set> allowed = new HashSet>(); + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (AccessControlEntry ace : acl.getEntries()) + { + if (isDenied(ace, authorisations, allowed, context)) + { + return false; + } + } + } + + Set> denied = new HashSet>(); + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (AccessControlEntry ace : acl.getEntries()) + { + if (isGranted(ace, authorisations, denied, context)) + { + return true; + } + } + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isGranted(AccessControlEntry ace, Set authorisations, Set> denied, PermissionContext context) + { + // If the permission entry denies then we just deny + if (ace.getAccessStatus() == AccessStatus.DENIED) + { + denied.add(new Pair(ace.getAuthority(), ace.getPermission())); + + Set granters = modelDAO.getGrantingPermissions(ace.getPermission()); + for (PermissionReference granter : granters) + { + denied.add(new Pair(ace.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = modelDAO.getGranteePermissions(ace.getPermission()); + for (PermissionReference grantee : grantees) + { + denied.add(new Pair(ace.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : modelDAO.getAllPermissions(context.getType(), context.getAspects())) + { + denied.add(new Pair(ace.getAuthority(), deny)); + } + } + + return false; + } + + // The permission is allowed but we deny it as it is in the denied + // set + + if (denied != null) + { + Pair specific = new Pair(ace.getAuthority(), required); + if (denied.contains(specific)) + { + return false; + } + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authorisations.contains(ace.getAuthority()) && granters.contains(ace.getPermission())) + { + { + return true; + } + } + + // Default deny + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isDenied(AccessControlEntry ace, Set authorisations, Set> allowed, PermissionContext context) + { + // If the permission entry denies then we just deny + if (ace.getAccessStatus() == AccessStatus.ALLOWED) + { + allowed.add(new Pair(ace.getAuthority(), ace.getPermission())); + + Set granters = modelDAO.getGrantingPermissions(ace.getPermission()); + for (PermissionReference granter : granters) + { + allowed.add(new Pair(ace.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = modelDAO.getGranteePermissions(ace.getPermission()); + for (PermissionReference grantee : grantees) + { + allowed.add(new Pair(ace.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : modelDAO.getAllPermissions(context.getType(), context.getAspects())) + { + allowed.add(new Pair(ace.getAuthority(), deny)); + } + } + + return false; + } + + // The permission is denied but we allow it as it is in the allowed + // set + + if (allowed != null) + { + Pair specific = new Pair(ace.getAuthority(), required); + if (allowed.contains(specific)) + { + return false; + } + } + + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authorisations.contains(ace.getAuthority()) && granters.contains(ace.getPermission())) + { + { + return true; + } + } + + // Default allow + return false; + } + + + private boolean isGranted(PermissionEntry pe, Set authorisations) + { + // If the permission entry denies then we just deny + if (pe.isDenied()) + { + return false; + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (granters.contains(pe.getPermissionReference()) && authorisations.contains(pe.getAuthority())) + { + { + return true; + } + } + + // Default deny + return false; + } + + } + + /** + * Ignores type and aspect requirements on the node + * + */ + private class UnconditionalAclTest + { + /* + * The required permission. + */ + PermissionReference required; + + /* + * Granters of the permission + */ + Set granters; + + /* + * The additional permissions required at the node level. + */ + Set nodeRequirements = new HashSet(); + + /* + * Constructor just gets the additional requirements + */ + UnconditionalAclTest(PermissionReference required) + { + this.required = required; + + // Set the required node permissions + if (required.equals(getPermissionReference(ALL_PERMISSIONS))) + { + nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE); + } + else + { + nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.NODE); + } + + if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) + { + throw new IllegalStateException("Parent permissions can not be checked for an acl"); + } + + if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) + { + throw new IllegalStateException("Child permissions can not be checked for an acl"); + } + + // Find all the permissions that grant the allowed permission + // All permissions are treated specially. + granters = new LinkedHashSet(128, 1.0f); + granters.addAll(modelDAO.getGrantingPermissions(required)); + granters.add(getAllPermissionReference()); + granters.add(OLD_ALL_PERMISSIONS_REFERENCE); + } + + /** + * Internal hook point for recursion + * + * @param authorisations + * @param nodeRef + * @param denied + * @param recursiveIn + * @return true if granted + */ + boolean evaluate(String authority, Long aclId) + { + // Start out true and "and" all other results + boolean success = true; + + // Check the required permissions but not for sets they rely on + // their underlying permissions + //if (modelDAO.checkPermission(required)) + //{ + + // We have to do the test as no parent will help us out + success &= hasSinglePermission(authority, aclId); + + if (!success) + { + return false; + } + //} + + // Check the other permissions required on the node + for (PermissionReference pr : nodeRequirements) + { + // Build a new test + UnconditionalAclTest nt = new UnconditionalAclTest(pr); + success &= nt.evaluate(authority, aclId); + if (!success) + { + return false; + } + } + + return success; + } + + boolean hasSinglePermission(String authority, Long aclId) + { + // Check global permission + + if (checkGlobalPermissions(authority)) + { + return true; + } + + if(aclId == null) + { + return false; + } + else + { + return checkRequired(authority, aclId); + } + + } + + /** + * Check if we have a global permission + * + * @param authorisations + * @return true if granted + */ + private boolean checkGlobalPermissions(String authority) + { + for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) + { + if (isGranted(pe, authority)) + { + return true; + } + } + return false; + } + + /** + * Check that a given authentication is available on a node + * + * @param authorisations + * @param nodeRef + * @param denied + * @return true if a check is required + */ + boolean checkRequired(String authority, Long aclId) + { + AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); + + if (acl == null) + { + return false; + } + + Set> denied = new HashSet>(); + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (AccessControlEntry ace : acl.getEntries()) + { + if (isGranted(ace, authority, denied)) + { + return true; + } + } + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isGranted(AccessControlEntry ace, String authority, Set> denied) + { + // If the permission entry denies then we just deny + if (ace.getAccessStatus() == AccessStatus.DENIED) + { + denied.add(new Pair(ace.getAuthority(), ace.getPermission())); + + Set granters = modelDAO.getGrantingPermissions(ace.getPermission()); + for (PermissionReference granter : granters) + { + denied.add(new Pair(ace.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = modelDAO.getGranteePermissions(ace.getPermission()); + for (PermissionReference grantee : grantees) + { + denied.add(new Pair(ace.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : modelDAO.getAllPermissions()) + { + denied.add(new Pair(ace.getAuthority(), deny)); + } + } + + return false; + } + + // The permission is allowed but we deny it as it is in the denied + // set + + if (denied != null) + { + Pair specific = new Pair(ace.getAuthority(), required); + if (denied.contains(specific)) + { + return false; + } + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) + { + { + return true; + } + } + + // Default deny + return false; + } + + private boolean isGranted(PermissionEntry pe, String authority) + { + // If the permission entry denies then we just deny + if (pe.isDenied()) + { + return false; + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) + { + { + return true; + } + } + + // Default deny + return false; + } + } + + /** + * Ignores type and aspect requirements on the node + * + */ + private class UnconditionalDeniedAclTest + { + /* + * The required permission. + */ + PermissionReference required; + + /* + * Granters of the permission + */ + Set granters; + + /* + * The additional permissions required at the node level. + */ + Set nodeRequirements = new HashSet(); + + /* + * Constructor just gets the additional requirements + */ + UnconditionalDeniedAclTest(PermissionReference required) + { + this.required = required; + + // Set the required node permissions + if (required.equals(getPermissionReference(ALL_PERMISSIONS))) + { + nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(getPermissionReference(PermissionService.FULL_CONTROL), RequiredPermission.On.NODE); + } + else + { + nodeRequirements = modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.NODE); + } + + if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.PARENT).size() > 0) + { + throw new IllegalStateException("Parent permissions can not be checked for an acl"); + } + + if (modelDAO.getUnconditionalRequiredPermissions(required, RequiredPermission.On.CHILDREN).size() > 0) + { + throw new IllegalStateException("Child permissions can not be checked for an acl"); + } + + // Find all the permissions that grant the allowed permission + // All permissions are treated specially. + granters = new LinkedHashSet(128, 1.0f); + granters.addAll(modelDAO.getGrantingPermissions(required)); + granters.add(getAllPermissionReference()); + granters.add(OLD_ALL_PERMISSIONS_REFERENCE); + } + + /** + * Internal hook point for recursion + * + * @param authorisations + * @param nodeRef + * @param denied + * @param recursiveIn + * @return true if granted + */ + boolean evaluate(String authority, Long aclId) + { + // Start out true and "and" all other results + boolean success = true; + + // Check the required permissions but not for sets they rely on + // their underlying permissions + //if (modelDAO.checkPermission(required)) + //{ + + // We have to do the test as no parent will help us out + success &= hasSinglePermission(authority, aclId); + + if (!success) + { + return false; + } + //} + + // Check the other permissions required on the node + for (PermissionReference pr : nodeRequirements) + { + // Build a new test + UnconditionalDeniedAclTest nt = new UnconditionalDeniedAclTest(pr); + success &= nt.evaluate(authority, aclId); + if (!success) + { + return false; + } + } + + return success; + } + + boolean hasSinglePermission(String authority, Long aclId) + { + // Check global permission + + if (checkGlobalPermissions(authority)) + { + return true; + } + + if(aclId == null) + { + return false; + } + else + { + return checkRequired(authority, aclId); + } + + } + + /** + * Check if we have a global permission + * + * @param authorisations + * @return true if granted + */ + private boolean checkGlobalPermissions(String authority) + { + for (PermissionEntry pe : modelDAO.getGlobalPermissionEntries()) + { + if (isDenied(pe, authority)) + { + return true; + } + } + return false; + } + + /** + * Check that a given authentication is available on a node + * + * @param authorisations + * @param nodeRef + * @param denied + * @return true if a check is required + */ + boolean checkRequired(String authority, Long aclId) + { + AccessControlList acl = aclDaoComponent.getAccessControlList(aclId); + + if (acl == null) + { + return false; + } + + Set> allowed = new HashSet>(); + + // Check if each permission allows - the first wins. + // We could have other voting style mechanisms here + for (AccessControlEntry ace : acl.getEntries()) + { + if (isDenied(ace, authority, allowed)) + { + return true; + } + } + return false; + } + + /** + * Is a permission granted + * + * @param pe - + * the permissions entry to consider + * @param granters - + * the set of granters + * @param authorisations - + * the set of authorities + * @param denied - + * the set of denied permissions/authority pais + * @return true if granted + */ + private boolean isDenied(AccessControlEntry ace, String authority, Set> allowed) + { + // If the permission entry denies then we just deny + if (ace.getAccessStatus() == AccessStatus.ALLOWED) + { + allowed.add(new Pair(ace.getAuthority(), ace.getPermission())); + + Set granters = modelDAO.getGrantingPermissions(ace.getPermission()); + for (PermissionReference granter : granters) + { + allowed.add(new Pair(ace.getAuthority(), granter)); + } + + // All the things granted by this permission must be + // denied + Set grantees = modelDAO.getGranteePermissions(ace.getPermission()); + for (PermissionReference grantee : grantees) + { + allowed.add(new Pair(ace.getAuthority(), grantee)); + } + + // All permission excludes all permissions available for + // the node. + if (ace.getPermission().equals(getAllPermissionReference()) || ace.getPermission().equals(OLD_ALL_PERMISSIONS_REFERENCE)) + { + for (PermissionReference deny : modelDAO.getAllPermissions()) + { + allowed.add(new Pair(ace.getAuthority(), deny)); + } + } + + return false; + } + + // The permission is allowed but we deny it as it is in the denied + // set + + if (allowed != null) + { + Pair specific = new Pair(ace.getAuthority(), required); + if (allowed.contains(specific)) + { + return false; + } + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (authority.equals(ace.getAuthority()) && granters.contains(ace.getPermission())) + { + { + return true; + } + } + + // Default deny + return false; + } + + private boolean isDenied(PermissionEntry pe, String authority) + { + // If the permission entry denies then we just deny + if (pe.isAllowed()) + { + return false; + } + + // If the permission has a match in both the authorities and + // granters list it is allowed + // It applies to the current user and it is granted + if (granters.contains(pe.getPermissionReference()) && authority.equals(pe.getAuthority())) + { + { + return true; + } + } + + // Default deny + return false; + } + } + + + private static class MutableBoolean + { + private boolean value; + + MutableBoolean(boolean value) + { + this.value = value; + } + + void setValue(boolean value) + { + this.value = value; + } + + boolean getValue() + { + return value; + } + } + + /** + * This methods checks whether the specified nodeRef instance is a version nodeRef (ie. in the 'version' store) + * + * @param nodeRef - version nodeRef + * @return true if version nodeRef false otherwise + */ + private boolean isVersionNodeRef(NodeRef nodeRef) + { + return nodeRef.getStoreRef().getProtocol().equals(VersionModel.STORE_PROTOCOL); + } + + /** + * Converts specified version nodeRef (eg. versionStore://...) to versioned nodeRef (eg. workspace://SpacesStore/...) + * + * @param nodeRef - always version nodeRef (ie. in the 'version' store) + * @return versioned nodeRef (ie.in the 'live' store) + */ + @SuppressWarnings("deprecation") + private NodeRef convertVersionNodeRefToVersionedNodeRef(NodeRef versionNodeRef) + { + Map properties = nodeService.getProperties(versionNodeRef); + + NodeRef nodeRef = null; + + // Switch VersionStore depending on configured impl + if (versionNodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID)) + { + // V2 version store (eg. workspace://version2Store) + nodeRef = (NodeRef)properties.get(Version2Model.PROP_QNAME_FROZEN_NODE_REF); + } + else if (versionNodeRef.getStoreRef().getIdentifier().equals(VersionModel.STORE_ID)) + { + // Deprecated V1 version store (eg. workspace://lightWeightVersionStore) + nodeRef = new NodeRef((String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_PROTOCOL), + (String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_STORE_ID), + (String) properties.get(VersionModel.PROP_QNAME_FROZEN_NODE_ID)); + } + + return nodeRef; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getAuthorisations() + { + // Use TX cache + @SuppressWarnings("unchecked") + Set auths = (Set) AlfrescoTransactionSupport.getResource("MyAuthCache"); + Authentication auth = AuthenticationUtil.getRunAsAuthentication(); + if (auths != null) + { + if (auth == null || !auths.contains(((User)auth.getPrincipal()).getUsername())) + { + auths = null; + } + } + if (auths == null) + { + auths = getCoreAuthorisations(auth); + AlfrescoTransactionSupport.bindResource("MyAuthCache", auths); + } + return Collections.unmodifiableSet(auths); + } + +} From 6500aa47ce17a441f81cfb4d2f529a4f0b6936de Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Tue, 14 Aug 2012 10:13:33 +0000 Subject: [PATCH 2/8] RM: Fix unit test git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40383 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../test/service/DispositionServiceImplTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java index fdeefe7976..45e037c538 100644 --- a/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java +++ b/rm-server/test/java/org/alfresco/module/org_alfresco_module_rm/test/service/DispositionServiceImplTest.java @@ -982,7 +982,7 @@ public class DispositionServiceImplTest extends BaseRMTestCase { // Publish the updates PublishUpdatesJobExecuter updater = (PublishUpdatesJobExecuter)applicationContext.getBean("publishUpdatesJobExecuter"); - updater.execute(); + updater.executeImpl(); // Check the record has been updated DispositionAction dispositionAction = dispositionService.getNextDispositionAction(testRM386Record); From 4b0a7bf084bd810701c0e1974ed24153c9e523ab Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Tue, 21 Aug 2012 11:00:25 +0000 Subject: [PATCH 3/8] Updated ReadMe.txt git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40656 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- ReadMe.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ReadMe.txt b/ReadMe.txt index 2871640006..2ba16d6e66 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -28,15 +28,16 @@ Deploying the RM AMPs - Set the envoronment variables TOMCAT_HOME and APP_TOMCAT_HOME to the home directory of the repository and share Tomcat instances respectively. NOTE: these can be the same tomcat instance, but it is recommended that two are used. - Configure your repository Tomcat so that your repository.properties settings will be successfully picked up when Alfresco is started. - - Run "gradlew installAmp" in the root directory. This will use the MMT to apply the RM AMPs to the Alfresco and Share WARs respectively. The modified WARs will then be copied to the set Tomcat instances, cleaning any exisiting exploded WARs. + - Run "gradlew deployAmp" in the root directory. This will use the MMT to apply the RM AMPs to the Alfresco and Share WARs respectively. The modified WARs will then be copied to the set Tomcat instances, cleaning any exisiting exploded WARs. - Start Tomcat(s). For users of the Alfresco DevEnv - Create a normal project using "create-project". - - Manually check out RM code into the "code" directory as described above. Don't use the checkout script provided. + - Check out RM code into the "code" directory (checkout https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD) - The devEnv will automatically set the TOMCAT_HOME and APP_TOMCAT_HOME environment variables to point to the Tomcat instances created by the use-tomcat6 and use-app-tomcat6 scipts. Magic! - - You can use the dev-context.xml generated for you to configure the repository. Place it in /shared/alfresco/extension. + - Copy the JDBC driver to /lib/ + - You can use the dev-context.xml generated for you to configure the repository. Place it in /shared/classes/alfresco/extension and /code/rm-server/config/alfresco/extension. Summary Of Available Gradle Tasks From 60815cf587b380afd7c557e9aba1c1dc97bad640 Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Tue, 21 Aug 2012 16:58:04 +0000 Subject: [PATCH 4/8] RM-439: Folders have no File to... option git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40696 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../module/org_alfresco_module_rm/rm-ui-evaluators-context.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml index d451f291b6..72a35ea07a 100644 --- a/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml +++ b/rm-server/config/alfresco/module/org_alfresco_module_rm/rm-ui-evaluators-context.xml @@ -270,6 +270,7 @@ RECORD + RECORD_FOLDER From 69f7413b6ad169be1e6fa9df27df798d285a4042 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 23 Aug 2012 00:35:51 +0000 Subject: [PATCH 5/8] RM-482: RM .classpath files have local library references * hard coded projects have been removed from archive * use "gradle eclipse" task to generate repository and share eclipse project with local references * updated ReadMe.txt git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40784 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- ReadMe.txt | 4 +- build.gradle | 3 + rm-server/.classpath | 297 ------------------ rm-server/.project | 16 - .../.settings/org.eclipse.jdt.core.prefs | 2 +- rm-server/build.gradle | 16 + 6 files changed, 23 insertions(+), 315 deletions(-) delete mode 100644 rm-server/.classpath delete mode 100644 rm-server/.project diff --git a/ReadMe.txt b/ReadMe.txt index 2ba16d6e66..25f74c40ba 100644 --- a/ReadMe.txt +++ b/ReadMe.txt @@ -19,6 +19,7 @@ Initial Setup Using Eclipse + - Run "gradlew eclipse". This will generate the eclipse project files. - Start Eclipse in the usual way. Note: make sure the WAR dependancies have been exploded before opening Eclispe. - Import projects found in rm-server and rm-share directories. @@ -51,4 +52,5 @@ Summary Of Available Gradle Tasks - installAmp : installs the AMP into a copy of the projects dependant WAR using the MMT. NOTE: the installed WAR can be found in build/dist. - deployAmp : depolys the project AMP to the configured Tomcat instance. - - fetchWarFile : fetches the dependant Alfresco WAR files \ No newline at end of file + - fetchWarFile : fetches the dependant Alfresco WAR files + - eclipse : generates eclipse projects for repository and share projects \ No newline at end of file diff --git a/build.gradle b/build.gradle index da926c470b..7994f02eee 100644 --- a/build.gradle +++ b/build.gradle @@ -101,6 +101,9 @@ subprojects { compile fileTree(dir: explodedLibsDir, include: '*.jar') } + // make sure existing eclipse projects are cleared + tasks.eclipse.dependsOn(cleanEclipse) + /** --- Compile tasks --- */ // make sure that the dependancies have been unpacked before compiling the Java diff --git a/rm-server/.classpath b/rm-server/.classpath deleted file mode 100644 index d5a9143db6..0000000000 --- a/rm-server/.classpath +++ /dev/null @@ -1,297 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rm-server/.project b/rm-server/.project deleted file mode 100644 index 47a2d689a4..0000000000 --- a/rm-server/.project +++ /dev/null @@ -1,16 +0,0 @@ - - - rm-server - - - - org.eclipse.jdt.core.javanature - - - - org.eclipse.jdt.core.javabuilder - - - - - diff --git a/rm-server/.settings/org.eclipse.jdt.core.prefs b/rm-server/.settings/org.eclipse.jdt.core.prefs index 286c5e127c..f4b0d88ec6 100644 --- a/rm-server/.settings/org.eclipse.jdt.core.prefs +++ b/rm-server/.settings/org.eclipse.jdt.core.prefs @@ -1,5 +1,5 @@ # -#Tue Jul 10 13:39:51 EST 2012 +#Thu Aug 23 10:22:16 EST 2012 org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.compliance=1.6 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve diff --git a/rm-server/build.gradle b/rm-server/build.gradle index 1934834862..497848cc37 100644 --- a/rm-server/build.gradle +++ b/rm-server/build.gradle @@ -26,6 +26,22 @@ task fetchSOLR(type:Copy) { task useSOLR << { } +eclipse { + classpath { + file { + withXml { + + Map map = new HashMap() + map.put('kind', 'src') + map.put('path', configDir) + + def node = it.asNode() + node.appendNode('classpathentry', map) + } + } + } +} + task resetDatabase << { // Loading the properties in all possible locations ant.property(file: System.properties.getProperty('DB_PROPERTIES') ) From fc7079edfa742dede1687a9917ecdbfcb90579fe Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 23 Aug 2012 00:49:17 +0000 Subject: [PATCH 6/8] RM: Remove .settings dir for Eclipse projects * See RM-482 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40785 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- rm-server/.settings/org.eclipse.jdt.core.prefs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 rm-server/.settings/org.eclipse.jdt.core.prefs diff --git a/rm-server/.settings/org.eclipse.jdt.core.prefs b/rm-server/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index f4b0d88ec6..0000000000 --- a/rm-server/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -# -#Thu Aug 23 10:22:16 EST 2012 -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.source=1.6 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error From 4806ecab33f4d6061773b223d38472c1804ed7c1 Mon Sep 17 00:00:00 2001 From: Roy Wetherall Date: Thu, 23 Aug 2012 00:54:39 +0000 Subject: [PATCH 7/8] RM: Set svn:ignore for generated eclipse project files * See RM-482 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40786 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 From 76ef6c3f33e383c1dbcb46d0a82d2a602baadc0d Mon Sep 17 00:00:00 2001 From: Tuna Aksoy Date: Sun, 26 Aug 2012 20:54:37 +0000 Subject: [PATCH 8/8] It was not possible to edit/delete a custom meta data git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX@40904 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../RecordsManagementAdminServiceImpl.java | 18 +++++++----- .../script/CustomPropertyDefinitionPost.java | 3 +- .../script/CustomPropertyDefinitionPut.java | 29 ++++++++++--------- 3 files changed, 28 insertions(+), 22 deletions(-) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java index 0a876180cd..789ee3bb1e 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/RecordsManagementAdminServiceImpl.java @@ -83,6 +83,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; import org.springframework.extensions.surf.util.ParameterCheck; +import org.springframework.extensions.surf.util.URLDecoder; /** * Records Management AdminService Implementation. @@ -765,20 +766,23 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin if (newName == null) return propQName; QName newPropQName = getQNameForClientId(newName); - propDefn = dictionaryService.getProperty(newPropQName); - if (propDefn != null) + if (newPropQName != null) { - // The requested QName is already in use - String propIdAsString = newPropQName.toPrefixString(namespaceService); - throw new PropertyAlreadyExistsMetadataException(propIdAsString); + propDefn = dictionaryService.getProperty(newPropQName); + if (propDefn != null) + { + // The requested QName is already in use + String propIdAsString = newPropQName.toPrefixString(namespaceService); + throw new PropertyAlreadyExistsMetadataException(propIdAsString); + } } NodeRef modelRef = getCustomModelRef(propQName.getNamespaceURI()); M2Model deserializedModel = readCustomContentModel(modelRef); M2Property targetProperty = findProperty(propQName, deserializedModel); - targetProperty.setName(newName); - targetProperty.setTitle(newName); + targetProperty.setName(new StringBuilder().append(RecordsManagementCustomModel.RM_CUSTOM_PREFIX).append(QName.NAMESPACE_PREFIX).append(newName).toString()); + targetProperty.setTitle(URLDecoder.decode(newName)); writeCustomContentModel(modelRef, deserializedModel); if (logger.isInfoEnabled()) diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java index bf529306db..1ddace840a 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPost.java @@ -33,6 +33,7 @@ import org.alfresco.service.namespace.QName; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; +import org.springframework.extensions.surf.util.URLDecoder; import org.springframework.extensions.webscripts.Cache; import org.springframework.extensions.webscripts.Status; import org.springframework.extensions.webscripts.WebScriptException; @@ -152,7 +153,7 @@ public class CustomPropertyDefinitionPost extends BaseCustomPropertyWebScript String customisableElement = (String)params.get(PARAM_ELEMENT); QName customisableType = mapToTypeQName(customisableElement); - String label = (String)params.get(PARAM_LABEL); + String label = URLDecoder.decode((String)params.get(PARAM_LABEL)); //According to the wireframes, type here can only be date|text|number Serializable serializableParam = params.get(PARAM_DATATYPE); diff --git a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java index 2dc8f1155a..71f11e7c68 100644 --- a/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java +++ b/rm-server/source/java/org/alfresco/module/org_alfresco_module_rm/script/CustomPropertyDefinitionPut.java @@ -136,26 +136,27 @@ public class CustomPropertyDefinitionPut extends BaseCustomPropertyWebScript "Could not find property definition for: " + propId); } + if (params.containsKey(PARAM_CONSTRAINT_REF)) + { + String constraintRef = (String)params.get(PARAM_CONSTRAINT_REF); + + if (constraintRef == null) + { + result = rmAdminService.removeCustomPropertyDefinitionConstraints(propQName); + } + else + { + QName constraintRefQName = QName.createQName(constraintRef, namespaceService); + result = rmAdminService.setCustomPropertyDefinitionConstraint(propQName, constraintRefQName); + } + } + if (params.containsKey(PARAM_LABEL)) { String label = (String)params.get(PARAM_LABEL); result = rmAdminService.updateCustomPropertyDefinitionName(propQName, label); } - if (params.containsKey(PARAM_CONSTRAINT_REF)) - { - String constraintRef = (String)params.get(PARAM_CONSTRAINT_REF); - - if (constraintRef == null) - { - result = rmAdminService.removeCustomPropertyDefinitionConstraints(propQName); - } - else - { - QName constraintRefQName = QName.createQName(constraintRef, namespaceService); - result = rmAdminService.setCustomPropertyDefinitionConstraint(propQName, constraintRefQName); - } - } return result; }