/*
* 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 extends CMISAccessControlEntry> 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 extends CMISPermissionMapping> 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()));
}
}
}
}
}
}