/* * #%L * Alfresco Records Management Module * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * - * 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 <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.module.org_alfresco_module_rm.capability; import java.util.Map; import org.alfresco.module.org_alfresco_module_rm.capability.impl.ViewRecordsCapability; import org.alfresco.module.org_alfresco_module_rm.caveat.RMCaveatConfigComponent; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.repository.AssociationRef; 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.AccessStatus; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.util.Pair; import org.aopalliance.intercept.MethodInvocation; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import net.sf.acegisecurity.vote.AccessDecisionVoter; /** * Common security functions. * * TODO move methods to the appropriate services * * @author Roy Wetherall * @since 2.0 */ public class RMSecurityCommon implements ApplicationContextAware { /** No set value */ protected static final int NOSET_VALUE = -100; /** Logger */ private static Log logger = LogFactory.getLog(RMSecurityCommon.class); /** Services */ //This is the internal NodeService -- no permission checks protected NodeService nodeService; protected PermissionService permissionService; protected RMCaveatConfigComponent caveatConfigComponent; private FilePlanService filePlanService; /** Application context */ protected ApplicationContext applicationContext; /** * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext) */ @Override public void setApplicationContext(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } /** * @param nodeService node service */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param permissionService permission service */ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } /** * @param caveatConfigComponent caveat config service */ public void setCaveatConfigComponent(RMCaveatConfigComponent caveatConfigComponent) { this.caveatConfigComponent = caveatConfigComponent; } /** * @return FilePlanService file plan service */ protected FilePlanService getFilePlanService() { if (filePlanService == null) { filePlanService = (FilePlanService)applicationContext.getBean("filePlanService"); } return filePlanService; } /** * Sets a value into the transaction cache * * @param prefix * @param nodeRef * @param value * @return */ protected int setTransactionCache(String prefix, NodeRef nodeRef, int value) { String user = AuthenticationUtil.getRunAsUser(); AlfrescoTransactionSupport.bindResource(prefix + nodeRef.toString() + user, Integer.valueOf(value)); return value; } /** * Gets a value from the transaction cache * * @param prefix * @param nodeRef * @return */ protected int getTransactionCache(String prefix, NodeRef nodeRef) { int result = NOSET_VALUE; String user = AuthenticationUtil.getRunAsUser(); Integer value = (Integer)AlfrescoTransactionSupport.getResource(prefix + nodeRef.toString() + user); if (value != null) { result = value.intValue(); } return result; } /** * Check for RM read * * @param nodeRef * @return */ 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) result = checkRead(nodeRef, false); } return result; } /** * Check for RM read * * @param nodeRef * @param allowDMRead * @return */ public int checkRead(NodeRef nodeRef, boolean allowDMRead) { int result = AccessDecisionVoter.ACCESS_ABSTAIN; if (nodeService.hasAspect(nodeRef, RecordsManagementModel.ASPECT_FILE_PLAN_COMPONENT)) { result = checkRmRead(nodeRef); } else if (allowDMRead) { // 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()) { logger.debug("\t\tPermission is denied"); Thread.dumpStack(); } result = AccessDecisionVoter.ACCESS_DENIED; } else { result = AccessDecisionVoter.ACCESS_GRANTED; } } return result; } /** * Core RM read check * * @param nodeRef node reference * @return int see {@link AccessDecisionVoter} */ public int checkRmRead(NodeRef nodeRef) { int result = AccessDecisionVoter.ACCESS_ABSTAIN; Map<Pair<String, NodeRef>, Integer> transactionCache = TransactionalResourceHelper.getMap("rm.security.checkRMRead"); Pair<String, NodeRef> key = new Pair<String, NodeRef>(AuthenticationUtil.getRunAsUser(), nodeRef); if (transactionCache.containsKey(key)) { result = transactionCache.get(key); } else { if (permissionService.hasPermission(nodeRef, RMPermissionModel.READ_RECORDS) == AccessStatus.DENIED) { if (logger.isDebugEnabled()) { logger.debug("\t\tUser does not have read record permission on node, access denied. (nodeRef=" + nodeRef.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")"); } result = AccessDecisionVoter.ACCESS_DENIED; } else { // Get the file plan for the node NodeRef filePlan = getFilePlanService().getFilePlan(nodeRef); if (filePlan != null && hasViewCapability(filePlan) == AccessStatus.DENIED) { if (logger.isDebugEnabled()) { logger.debug("\t\tUser does not have view records capability permission on node, access denied. (filePlan=" + filePlan.toString() + ", user=" + AuthenticationUtil.getRunAsUser() + ")"); } result = AccessDecisionVoter.ACCESS_DENIED; } else if (!caveatConfigComponent.hasAccess(nodeRef)) { result = AccessDecisionVoter.ACCESS_DENIED; } else { result = AccessDecisionVoter.ACCESS_GRANTED; } } // cache result transactionCache.put(key, result); } return result; } /** * Helper method to determine whether the current user has view capability on the file plan * * @param filePlan file plan * @return {@link AccessStatus} */ private AccessStatus hasViewCapability(NodeRef filePlan) { Map<Pair<String, NodeRef>, AccessStatus> transactionCache = TransactionalResourceHelper.getMap("rm.security.hasViewCapability"); Pair<String, NodeRef> key = new Pair<String, NodeRef>(AuthenticationUtil.getRunAsUser(), filePlan); if (transactionCache.containsKey(key)) { return transactionCache.get(key); } else { AccessStatus result = permissionService.hasPermission(filePlan, ViewRecordsCapability.NAME); transactionCache.put(key, result); return result; } } @SuppressWarnings("rawtypes") protected NodeRef getTestNode(MethodInvocation invocation, Class[] params, int position, boolean parent) { NodeRef testNodeRef = null; if (position < 0) { if (logger.isDebugEnabled()) { logger.debug("\tNothing to test permission against."); } testNodeRef = null; } else if (StoreRef.class.isAssignableFrom(params[position])) { if (invocation.getArguments()[position] != null) { if (logger.isDebugEnabled()) { logger.debug("\tPermission test against the store - using permissions on the root node"); } StoreRef storeRef = (StoreRef) invocation.getArguments()[position]; if (nodeService.exists(storeRef)) { testNodeRef = nodeService.getRootNode(storeRef); } } } else if (NodeRef.class.isAssignableFrom(params[position])) { testNodeRef = (NodeRef) invocation.getArguments()[position]; if (parent) { testNodeRef = nodeService.getPrimaryParent(testNodeRef).getParentRef(); if (logger.isDebugEnabled()) { if (nodeService.exists(testNodeRef)) { logger.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); } else { logger.debug("\tPermission test for parent on non-existing node " + testNodeRef); } logger.debug("\tPermission test for parent on node " + nodeService.getPath(testNodeRef)); } } else { if (logger.isDebugEnabled()) { if (nodeService.exists(testNodeRef)) { logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); } else { logger.debug("\tPermission test on non-existing node " + testNodeRef); } } } } else if (ChildAssociationRef.class.isAssignableFrom(params[position])) { if (invocation.getArguments()[position] != null) { if (parent) { testNodeRef = ((ChildAssociationRef) invocation.getArguments()[position]).getParentRef(); } else { testNodeRef = ((ChildAssociationRef) invocation.getArguments()[position]).getChildRef(); } if (logger.isDebugEnabled()) { if (nodeService.exists(testNodeRef)) { logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); } else { logger.debug("\tPermission test on non-existing node " + testNodeRef); } } } } else if (AssociationRef.class.isAssignableFrom(params[position]) && invocation.getArguments()[position] != null) { if (parent) { testNodeRef = ((AssociationRef) invocation.getArguments()[position]).getSourceRef(); } else { testNodeRef = ((AssociationRef) invocation.getArguments()[position]).getTargetRef(); } if (logger.isDebugEnabled()) { if (nodeService.exists(testNodeRef)) { logger.debug("\tPermission test on node " + nodeService.getPath(testNodeRef)); } else { logger.debug("\tPermission test on non-existing node " + testNodeRef); } } } return testNodeRef; } }