/* * Copyright (C) 2005-2010 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.cmis.acl; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.cmis.CMISAccessControlEntry; import org.alfresco.cmis.CMISAccessControlReport; import org.alfresco.cmis.CMISAccessControlService; import org.alfresco.cmis.CMISAclCapabilityEnum; import org.alfresco.cmis.CMISAclPropagationEnum; import org.alfresco.cmis.CMISAclSupportedPermissionEnum; import org.alfresco.cmis.CMISAllowedActionEnum; import org.alfresco.cmis.CMISConstraintException; import org.alfresco.cmis.CMISDictionaryService; import org.alfresco.cmis.CMISPermissionDefinition; import org.alfresco.cmis.CMISPermissionMapping; import org.alfresco.cmis.CMISTypeDefinition; import org.alfresco.cmis.mapping.CMISMapping; import org.alfresco.opencmis.CMISAccessControlFormatEnum; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.PermissionReference; import org.alfresco.repo.security.permissions.impl.AccessPermissionImpl; import org.alfresco.repo.security.permissions.impl.ModelDAO; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AccessPermission; 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; /** * @author andyh */ public class CMISAccessControlServiceImpl implements CMISAccessControlService { private CMISAclCapabilityEnum aclCapabilityEnum; private CMISAclSupportedPermissionEnum aclSupportedPermissionEnum; private CMISAclPropagationEnum aclPropagationEnum; private ModelDAO permissionModelDao; private PermissionService permissionService; private CMISMapping cmisMapping; private NodeService nodeService; private CMISDictionaryService cmisDictionaryService; /** * @param aclCapabilityEnum * the aclCapabilityEnum to set */ public void setAclCapabilityEnum(CMISAclCapabilityEnum aclCapabilityEnum) { this.aclCapabilityEnum = aclCapabilityEnum; } /** * Sets the acl supported permission enum. * * @param aclSupportedPermissionEnum * the aclSupportedPermissionEnum to set */ public void setAclSupportedPermissionEnum(CMISAclSupportedPermissionEnum aclSupportedPermissionEnum) { this.aclSupportedPermissionEnum = aclSupportedPermissionEnum; } /** * @param aclPropagationEnum * the aclPropagationEnum to set */ public void setAclPropagationEnum(CMISAclPropagationEnum aclPropagationEnum) { this.aclPropagationEnum = aclPropagationEnum; } /** * @param permissionModelDao * the permissionModelDao to set */ public void setPermissionModelDao(ModelDAO permissionModelDao) { this.permissionModelDao = permissionModelDao; } /** * @param permissionService * the permissionService to set */ public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } /** * Sets the cmis mapping. * * @param cmisMapping * the cmis mapping */ public void setCMISMapping(CMISMapping cmisMapping) { this.cmisMapping = cmisMapping; } /** * @param nodeService * the nodeService to set */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } /** * @param cmisDictionaryService * the cmisDictionaryService to set */ public void setCMISDictionaryService(CMISDictionaryService cmisDictionaryService) { this.cmisDictionaryService = cmisDictionaryService; } /* * (non-Javadoc) * @see org.alfresco.cmis.CMISAccessControlService#applyAcl(org.alfresco.service.cmr.repository.NodeRef, * java.util.List) */ public CMISAccessControlReport applyAcl(NodeRef nodeRef, List acesToApply) throws CMISConstraintException { Set acesToAdd = new LinkedHashSet(acesToApply); List acesExisting = getAcl(nodeRef, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS).getAccessControlEntries(); List acesToRemove = new ArrayList(acesExisting.size()); for (CMISAccessControlEntry accessControlEntry : acesExisting) { // Only pay attention to existing direct entries if (accessControlEntry.getDirect() && !acesToAdd.remove(accessControlEntry)) { acesToRemove.add(accessControlEntry); } } return applyAcl(nodeRef, acesToRemove, new ArrayList(acesToAdd), CMISAclPropagationEnum.PROPAGATE, CMISAccessControlFormatEnum.REPOSITORY_SPECIFIC_PERMISSIONS); } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#applyAcl(org.alfresco.service.cmr.repository.NodeRef, * java.util.List, java.util.List, org.alfresco.cmis.CMISAclPropagationEnum) */ public CMISAccessControlReport applyAcl(NodeRef nodeRef, List acesToRemove, List acesToAdd, CMISAclPropagationEnum propagation, CMISAccessControlFormatEnum format) throws CMISConstraintException { if (propagation == CMISAclPropagationEnum.OBJECT_ONLY) { throw new CMISConstraintException("Unsupported ACL propagation mode: " + propagation); } // Check controllable ACL QName type = nodeService.getType(nodeRef); CMISTypeDefinition cmisType = cmisDictionaryService.findTypeForClass(type); if (false == cmisType.isControllableACL()) { throw new CMISConstraintException("ACLs are not supported for type: " + cmisType.getDisplayName()); } // TODO: Check valid permissions. We do not check this internally. Ignore for now ... if (acesToRemove != null) { Set permissions = permissionService.getAllSetPermissions(nodeRef); for (CMISAccessControlEntry entry : acesToRemove) { String alfrescoPermission = cmisMapping.getSetPermission(compressPermission(entry.getPermission())); AccessPermission toCheck = new AccessPermissionImpl(alfrescoPermission, AccessStatus.ALLOWED, entry.getPrincipalId(), 0); if (false == permissions.contains(toCheck)) { throw new CMISConstraintException("No matching ACE found to delete"); } permissionService.deletePermission(nodeRef, entry.getPrincipalId(), alfrescoPermission); } } if (acesToAdd != null) { for (CMISAccessControlEntry entry : acesToAdd) { String alfrescoPermission = cmisMapping.getSetPermission(compressPermission(entry.getPermission())); permissionService.setPermission(nodeRef, entry.getPrincipalId(), alfrescoPermission, true); } } return getAcl(nodeRef, format); } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#getAcl(org.alfresco.service.cmr.repository.NodeRef, * org.alfresco.cmis.CMISAccessControlFormatEnum) */ public CMISAccessControlReport getAcl(NodeRef nodeRef, CMISAccessControlFormatEnum format) { CMISAccessControlReportImpl merge = new CMISAccessControlReportImpl(); // Need to compact deny to mask correctly Set permissions = permissionService.getAllSetPermissions(nodeRef); ArrayList ordered = new ArrayList(); AccessPermissionComparator comparator = new AccessPermissionComparator(); for (AccessPermission current : permissions) { int index = Collections.binarySearch(ordered, current, comparator); if (index < 0) { ordered.add(-index - 1, current); } } for (AccessPermission entry : ordered) { if (entry.getAccessStatus() == AccessStatus.ALLOWED) { //answer.addEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), expandPermission(cmisMapping.getReportedPermission(getPermission(entry.getPermission()), // format)), entry.getPosition())); merge.addEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), entry.getPermission(), entry.getPosition())); } else if (entry.getAccessStatus() == AccessStatus.DENIED) { //answer.removeEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), expandPermission(cmisMapping.getReportedPermission(getPermission(entry.getPermission()), // format)), entry.getPosition())); merge.removeEntry(new CMISAccessControlEntryImpl(entry.getAuthority(), entry.getPermission(), entry.getPosition())); } } CMISAccessControlReportImpl answer = new CMISAccessControlReportImpl(); for(CMISAccessControlEntry entry : merge.getAccessControlEntries()) { CMISAccessControlEntryImpl impl = (CMISAccessControlEntryImpl)entry; PermissionReference permissionReference = permissionModelDao.getPermissionReference(null, impl.getPermission()); Set longForms = permissionModelDao.getGranteePermissions(permissionReference); HashSet shortForms = new HashSet(); for(PermissionReference longForm : longForms) { shortForms.add(getPermission(longForm)); } for(Pair toAdd : cmisMapping.getReportedPermissions(impl.getPermission(), shortForms, permissionModelDao.hasFull(permissionReference), impl.getDirect(), format)) { answer.addEntry(new CMISAccessControlEntryImpl(impl.getPrincipalId(), expandPermission(toAdd.getFirst()), impl.getPosition(), toAdd.getSecond())); } } return answer; } private String getPermission(PermissionReference permissionReference) { if (permissionModelDao.isUnique(permissionReference)) { return permissionReference.getName(); } else { return permissionReference.toString(); } } private String expandPermission(String permission) { if (permission.equals(CMIS_ALL_PERMISSION)) { return permission; } else if (permission.equals(CMIS_READ_PERMISSION)) { return permission; } else if (permission.equals(CMIS_WRITE_PERMISSION)) { return permission; } else if (permission.startsWith("{")) { return permission; } else { PermissionReference permissionReference = permissionModelDao.getPermissionReference(null, permission); return permissionReference.toString(); } } private String compressPermission(String permission) { int sepIndex; if (permission.equals(CMIS_ALL_PERMISSION) || permission.equals(CMIS_READ_PERMISSION) || permission.equals(CMIS_WRITE_PERMISSION) || !permission.startsWith("{") || (sepIndex = permission.lastIndexOf('.')) == -1) { return permission; } return permission.substring(sepIndex + 1); } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#getAclCapability() */ public CMISAclCapabilityEnum getAclCapability() { return aclCapabilityEnum; } /** * Set the acl capability enum. * * @param aclCapabilityEnum */ public void setAclCapability(CMISAclCapabilityEnum aclCapabilityEnum) { this.aclCapabilityEnum = aclCapabilityEnum; } /* * (non-Javadoc) * @see org.alfresco.cmis.CMISAccessControlService#getSupportedPermissions() */ public CMISAclSupportedPermissionEnum getSupportedPermissions() { return this.aclSupportedPermissionEnum; } /** * Sets the supported permissions. * * @param aclSupportedPermissionEnum * the supported permissions */ public void setSupportedPermissions(CMISAclSupportedPermissionEnum aclSupportedPermissionEnum) { this.aclSupportedPermissionEnum = aclSupportedPermissionEnum; } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#getAclPropagation() */ public CMISAclPropagationEnum getAclPropagation() { return aclPropagationEnum; } /** * Set the acl propagation enum. * * @param aclPropagationEnum */ public void setAclPropagation(CMISAclPropagationEnum aclPropagationEnum) { this.aclPropagationEnum = aclPropagationEnum; } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#getPermissionMappings() */ public List getPermissionMappings() { ArrayList mappings = new ArrayList(); for(CMISAllowedActionEnum e : EnumSet.allOf(CMISAllowedActionEnum.class)) { Map> enumMappings = e.getPermissionMapping(); for(String key : enumMappings.keySet()) { List list = enumMappings.get(key); CMISPermissionMappingImpl mapping = new CMISPermissionMappingImpl(key, list); mappings.add(mapping); } } return mappings; } /* * (non-Javadoc) * * @see org.alfresco.cmis.CMISAccessControlService#getRepositoryPermissions() */ public List getRepositoryPermissions() { ArrayList answer = new ArrayList(); PermissionReference allPermission = permissionModelDao.getPermissionReference(null, PermissionService.ALL_PERMISSIONS); Set all = permissionModelDao.getAllExposedPermissions(); for (PermissionReference pr : all) { addPermissionDefinition(answer, pr); } // Add All addPermissionDefinition(answer, allPermission); // Add CMIS permissions answer.add(new CMISPermissionDefinitionImpl(CMIS_ALL_PERMISSION)); answer.add(new CMISPermissionDefinitionImpl(CMIS_READ_PERMISSION)); answer.add(new CMISPermissionDefinitionImpl(CMIS_WRITE_PERMISSION)); return answer; } /* * (non-Javadoc) * @see org.alfresco.cmis.CMISAccessControlService#getPrincipalAnonymous() */ public String getPrincipalAnonymous() { return AuthenticationUtil.getGuestUserName(); } /* * (non-Javadoc) * @see org.alfresco.cmis.CMISAccessControlService#getPrincipalAnyone() */ public String getPrincipalAnyone() { return PermissionService.ALL_AUTHORITIES; } private void addPermissionDefinition(ArrayList list, PermissionReference pr) { CMISPermissionDefinitionImpl def = new CMISPermissionDefinitionImpl(getPermissionString(pr)); list.add(def); } private String getPermissionString(PermissionReference pr) { StringBuilder builder = new StringBuilder(); builder.append(pr.getQName().toString()); builder.append("."); builder.append(pr.getName()); return builder.toString(); } public static class AccessPermissionComparator implements Comparator { /* * (non-Javadoc) * * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */ public int compare(AccessPermission left, AccessPermission right) { if (left.getPosition() != right.getPosition()) { return right.getPosition() - left.getPosition(); } else { if (left.getAccessStatus() != right.getAccessStatus()) { return (left.getAccessStatus() == AccessStatus.DENIED) ? -1 : 1; } else { int compare = left.getAuthority().compareTo(right.getAuthority()); if (compare != 0) { return compare; } else { return (left.getPermission().compareTo(right.getPermission())); } } } } } }