/* * #%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.model.security; import java.io.Serializable; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService; import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.annotation.Behaviour; import org.alfresco.repo.policy.annotation.BehaviourBean; import org.alfresco.repo.policy.annotation.BehaviourKind; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.EqualsHelper; /** * Model security service implementation. * <p> * This service records the protected properties and aspects, ensuring that only those with the appropriate capabilities can edit them. * * @author Roy Wetherall * @since 2.1 */ @BehaviourBean public class ModelSecurityServiceImpl extends BaseBehaviourBean implements ModelSecurityService, NodeServicePolicies.BeforeAddAspectPolicy, NodeServicePolicies.BeforeRemoveAspectPolicy, NodeServicePolicies.OnUpdatePropertiesPolicy { /** Indicates whether model security is enabled or not */ private boolean enabled = true; /** Namespace service */ private NamespaceService namespaceService; /** File plan service */ private FilePlanService filePlanService; /** Map of protected properties keyed by name */ private Map<QName, ProtectedProperty> protectedProperties = new HashMap<QName, ProtectedProperty>(21); /** Map of protected aspects keyed by name */ private Map<QName, ProtectedAspect> protectedAspects= new HashMap<QName, ProtectedAspect>(21); /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#setEnabled(boolean) */ public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#isEnabled() */ public boolean isEnabled() { return enabled; } /** * @param namespaceService namespace service */ public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; } /** * @param filePlanService file plan service */ public void setFilePlanService(FilePlanService filePlanService) { this.filePlanService = filePlanService; } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#disable() */ @Override public void disable() { getBehaviour("beforeAddAspect").disable(); getBehaviour("beforeRemoveAspect").disable(); getBehaviour("onUpdateProperties").disable(); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#enable() */ @Override public void enable() { getBehaviour("beforeAddAspect").enable(); getBehaviour("beforeRemoveAspect").enable(); getBehaviour("onUpdateProperties").enable(); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#register(org.alfresco.module.org_alfresco_module_rm.model.security.ProtectedModelArtifact) */ @Override public void register(ProtectedModelArtifact artifact) { // TODO validate that the artifact has a valid property and has a capability set ... if (artifact instanceof ProtectedProperty) { protectedProperties.put(artifact.getQName(), (ProtectedProperty)artifact); } else if (artifact instanceof ProtectedAspect) { protectedAspects.put(artifact.getQName(), (ProtectedAspect)artifact); } } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#isProtectedProperty(org.alfresco.service.namespace.QName) */ @Override public boolean isProtectedProperty(QName property) { return protectedProperties.containsKey(property); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#getProtectedProperties() */ @Override public Set<QName> getProtectedProperties() { return Collections.unmodifiableSet(protectedProperties.keySet()); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#getProtectedProperty(org.alfresco.service.namespace.QName) */ @Override public ProtectedProperty getProtectedProperty(QName name) { return protectedProperties.get(name); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#canEditProtectedProperty(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @Override public boolean canEditProtectedProperty(NodeRef nodeRef, QName property) { boolean result = false; ProtectedModelArtifact artifact = getProtectedProperty(property); if (artifact == null) { result = true; } else { result = canEdit(nodeRef, artifact); } return result; } /** * Indicates whether the current user can edit protected model artifact in the context * of a given node or not. * * @param nodeRef node reference * @param artifact protected model artifact * @return boolean true if the current user can edit the protected model artifact, false otherwise */ private boolean canEdit(NodeRef nodeRef, ProtectedModelArtifact artifact) { boolean result = false; NodeRef filePlan = filePlanService.getFilePlan(nodeRef); if (filePlan != null) { for (Capability capability : artifact.getCapabilities()) { if (capability.hasPermission(nodeRef).equals(AccessStatus.ALLOWED)) { result = true; break; } } } return result; } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#isProtectedAspect(org.alfresco.service.namespace.QName) */ @Override public boolean isProtectedAspect(QName aspect) { return protectedAspects.containsKey(aspect); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#getProtectedAspects() */ @Override public Set<QName> getProtectedAspects() { return Collections.unmodifiableSet(protectedAspects.keySet()); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#getProtectedAspect(org.alfresco.service.namespace.QName) */ @Override public ProtectedAspect getProtectedAspect(QName name) { return protectedAspects.get(name); } /** * @see org.alfresco.module.org_alfresco_module_rm.model.security.ModelSecurityService#canEditProtectedAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @Override public boolean canEditProtectedAspect(NodeRef nodeRef, QName aspect) { boolean result = false; ProtectedModelArtifact artifact = getProtectedAspect(aspect); if (artifact == null) { result = true; } else { result = canEdit(nodeRef, artifact); } return result; } /** * @see org.alfresco.repo.node.NodeServicePolicies.BeforeAddAspectPolicy#beforeAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @Override @Behaviour ( kind = BehaviourKind.CLASS, isService = true, name = "beforeAddAspect" ) public void beforeAddAspect(NodeRef nodeRef, QName aspect) { if (enabled && AuthenticationUtil.getFullyAuthenticatedUser() != null && !AuthenticationUtil.isRunAsUserTheSystemUser() && isProtectedAspect(aspect) && nodeService.exists(nodeRef) && !canEditProtectedAspect(nodeRef, aspect)) { // the user can't edit the protected aspect throw new ModelAccessDeniedException( "The user " + AuthenticationUtil.getFullyAuthenticatedUser() + " does not have the permission to add the protected aspect " + aspect.toPrefixString(namespaceService) + " to the node " + nodeRef.toString()); } } /** * @see org.alfresco.repo.node.NodeServicePolicies.BeforeRemoveAspectPolicy#beforeRemoveAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName) */ @Override @Behaviour ( kind = BehaviourKind.CLASS, isService = true, name = "beforeRemoveAspect" ) public void beforeRemoveAspect(NodeRef nodeRef, QName aspect) { if (enabled && AuthenticationUtil.getFullyAuthenticatedUser() != null && !AuthenticationUtil.isRunAsUserTheSystemUser() && isProtectedAspect(aspect) && nodeService.exists(nodeRef) && !canEditProtectedAspect(nodeRef, aspect)) { // the user can't edit the protected aspect throw new ModelAccessDeniedException( "The user " + AuthenticationUtil.getFullyAuthenticatedUser() + " does not have the permission to remove the protected aspect " + aspect.toPrefixString(namespaceService) + " from the node " + nodeRef.toString()); } } /** * @see org.alfresco.repo.node.NodeServicePolicies.OnUpdatePropertiesPolicy#onUpdateProperties(org.alfresco.service.cmr.repository.NodeRef, java.util.Map, java.util.Map) */ @Override @Behaviour ( kind = BehaviourKind.CLASS, isService = true, name = "onUpdateProperties" ) public void onUpdateProperties(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after) { if (enabled && AuthenticationUtil.getFullyAuthenticatedUser() != null && !AuthenticationUtil.isRunAsUserTheSystemUser() && nodeService.exists(nodeRef)) { for (Map.Entry<QName, Serializable> entry : after.entrySet()) { QName property = entry.getKey(); if (isProtectedProperty(property)) { // always allow if this is the first time we are setting the protected property if (before == null || before.isEmpty() || before.get(property) == null) { return; } if (!EqualsHelper.nullSafeEquals(before.get(property), entry.getValue()) && !canEditProtectedProperty(nodeRef, property)) { // the user can't edit the protected property throw new ModelAccessDeniedException( "The user " + AuthenticationUtil.getFullyAuthenticatedUser() + " does not have the permission to edit the protected property " + property.toPrefixString(namespaceService) + " on the node " + nodeRef.toString()); } } } } } }