/* * Copyright (C) 2005-2007 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing */ package org.alfresco.repo.simple.permission; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.alfresco.repo.avm.util.RawServices; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.simple.permission.ACL; import org.alfresco.service.simple.permission.AuthorityCapabilityRegistry; /** * Basic implementation of a simple ACL. * @author britt */ public class ACLImpl implements ACL { private static final long serialVersionUID = -8720314753104805631L; /** * Map of capabilities to authorities allowed. */ private Map> fAllowed; /** * Map of capabilities to authorities denied. */ private Map> fDenied; /** * Should this ACL be inherited. */ private boolean fInherit; /** * String (compact) representation of ACL. */ private String fStringRep; /** * Reference to the capability registry. */ private transient AuthorityCapabilityRegistry fCapabilityRegistry; /** * Initialize a brand new one. * @param inherit Should this ACL be inherited. */ public ACLImpl(boolean inherit) { fInherit = inherit; fCapabilityRegistry = RawServices.Instance().getAuthorityCapabilityRegistry(); fAllowed = new HashMap>(); fDenied = new HashMap>(); fStringRep = null; } /** * Initialize from an external string representation. * @param rep */ public ACLImpl(String rep) { this(true); fStringRep = rep; } public ACLImpl(ACL other) { this(true); fStringRep = other.getStringRepresentation(); } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#allow(java.lang.String, java.lang.String[]) */ public void allow(String capability, String... authorities) { capability = capability.toLowerCase(); List auths = new ArrayList(); for (String auth : authorities) { auths.add(fCapabilityRegistry.normalizeAuthority(auth)); } digest(); // First remove any explicit denies. Set denied = fDenied.get(capability); if (denied != null) { for (String authority : auths) { denied.remove(authority); } } // Add the authorities to the allowed list. Set allowed = fAllowed.get(capability); if (allowed == null) { allowed = new HashSet(); fAllowed.put(capability, allowed); } for (String authority : auths) { allowed.add(authority); } } /** * Helper to decode from the string representation. */ private void digest() { if (fStringRep == null) { return; } String[] segments = fStringRep.split("\\|"); fInherit = segments[0].equals("i"); digestMap(segments[1], fAllowed); digestMap(segments[2], fDenied); fStringRep = null; } /** * Sub helper for decoding string representation. * @param string The partial string representation. * @param map The map to update. */ private void digestMap(String rep, Map> map) { String[] segments = rep.split(":"); if (segments.length == 0 || segments[0].equals("")) { // This means there are no explicit entries. return; } for (String entryRep : segments) { String[] entryRegs = entryRep.split(";"); String capability = fCapabilityRegistry.getCapabilityName(Integer.parseInt(entryRegs[0], 32)); if (capability == null) { continue; } Set authorities = new HashSet(); map.put(capability, authorities); for (int i = 1; i < entryRegs.length; ++i) { String authority = fCapabilityRegistry.getAuthorityName(Integer.parseInt(entryRegs[i], 32)); if (authority == null) { continue; } authorities.add(authority); } } } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#can(java.lang.String, boolean, java.lang.String) */ public boolean can(String authority, boolean isOwner, String capability) { authority = fCapabilityRegistry.normalizeAuthority(authority); capability = capability.toLowerCase(); digest(); AuthorityType type = AuthorityType.getAuthorityType(authority); // Admin trumps. if (type == AuthorityType.ADMIN) { return true; } // Look for denies first. Set denied = fDenied.get(capability); if (denied != null) { if (denied.contains(authority)) { return false; } for (String auth : denied) { if (fCapabilityRegistry.getContainedAuthorities(auth).contains(authority)) { return false; } } } // Now look for allows. Set allowed = fAllowed.get(capability); if (allowed != null) { if (allowed.contains(authority)) { return true; } for (String auth : allowed) { if (fCapabilityRegistry.getContainedAuthorities(auth).contains(authority)) { return true; } } } return false; } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#deny(java.lang.String, java.lang.String[]) */ public void deny(String capability, String ... authorities) { capability = capability.toLowerCase(); List auths = new ArrayList(); for (String auth : authorities) { auths.add(fCapabilityRegistry.normalizeAuthority(auth)); } digest(); // Remove corresponding explicit allows. Set allowed = fAllowed.get(capability); if (allowed != null) { for (String authority : auths) { allowed.remove(authority); } } // Now add denies. Set denied = fDenied.get(capability); if (denied == null) { denied = new HashSet(); fDenied.put(capability, denied); } for (String authority : auths) { if (AuthorityType.getAuthorityType(authority) == AuthorityType.ADMIN) { continue; } denied.add(authority); } } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#getAllowed(java.lang.String) */ public Set getAllowed(String capability) { capability = capability.toLowerCase(); digest(); Set allowed = new HashSet(); allowed.add(AuthorityType.ADMIN.getFixedString()); // Add the explicitly allowed. Set expAllowed = fAllowed.get(capability); if (expAllowed == null) { return allowed; } allowed.addAll(expAllowed); for (String authority : expAllowed) { allowed.addAll(fCapabilityRegistry.getContainedAuthorities(authority)); } // Now remove based on denials. Set denied = fDenied.get(capability); if (denied == null) { return allowed; } allowed.removeAll(denied); // Now those that are indirectly denied. for (String authority : denied) { allowed.removeAll(fCapabilityRegistry.getContainedAuthorities(authority)); } return allowed; } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#getCapabilities(java.lang.String, boolean) */ public Set getCapabilities(String authority, boolean isOwner) { authority = fCapabilityRegistry.normalizeAuthority(authority); digest(); AuthorityType type = AuthorityType.getAuthorityType(authority); if (type == AuthorityType.ADMIN) { return fCapabilityRegistry.getAllCapabilities(); } Set capabilities = new HashSet(); // First run through the allowed entries. Set containers = null; for (Map.Entry> entry : fAllowed.entrySet()) { if (entry.getValue().contains(authority)) { capabilities.add(entry.getKey()); continue; } if (containers == null) { containers = fCapabilityRegistry.getContainerAuthorities(authority); } for (String auth : containers) { if (entry.getValue().contains(auth)) { capabilities.add(entry.getKey()); break; } } } // Now go through the denials. for (Map.Entry> entry : fDenied.entrySet()) { if (!capabilities.contains(entry.getKey())) { continue; } Set denied = entry.getValue(); if (denied.contains(authority)) { capabilities.remove(entry.getKey()); continue; } if (containers == null) { containers = fCapabilityRegistry.getContainerAuthorities(authority); } for (String auth : containers) { if (denied.contains(auth)) { capabilities.remove(entry.getKey()); break; } } } return capabilities; } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#getStringRepresentation() */ public String getStringRepresentation() { if (fStringRep != null) { return fStringRep; } StringBuilder builder = new StringBuilder(); builder.append(fInherit ? 'i' : 'n'); builder.append('|'); int count = 0; for (Map.Entry> entry : fAllowed.entrySet()) { builder.append(Integer.toString(fCapabilityRegistry.getCapabilityID(entry.getKey()), 32)); for (String authority : entry.getValue()) { builder.append(';'); builder.append(Integer.toString(fCapabilityRegistry.getAuthorityID(authority), 32)); } if (count++ < fAllowed.size() - 1) { builder.append(':'); } } builder.append('|'); count = 0; for (Map.Entry> entry : fDenied.entrySet()) { builder.append(Integer.toString(fCapabilityRegistry.getCapabilityID(entry.getKey()), 32)); for (String authority : entry.getValue()) { builder.append(';'); builder.append(Integer.toString(fCapabilityRegistry.getAuthorityID(authority), 32)); } if (count++ < fDenied.size() - 1) { builder.append(':'); } } return builder.toString(); } /* (non-Javadoc) * @see org.alfresco.service.simple.permission.ACL#inherits() */ public boolean inherits() { digest(); return fInherit; } }