/* * ModeShape (http://www.modeshape.org) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.modeshape.jcr.security.acl; import java.security.Principal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import javax.jcr.RepositoryException; import javax.jcr.security.AccessControlEntry; import javax.jcr.security.AccessControlException; import javax.jcr.security.AccessControlList; import javax.jcr.security.Privilege; import org.modeshape.jcr.AccessControlManagerImpl; import org.modeshape.jcr.security.SecurityContext; import org.modeshape.jcr.security.SimplePrincipal; /** * Resources based Access Control List implementation. ACLs are stored per node in a special child node called * <bold>mode:acl</bold>. This node has a list of <bold> mode:{$principal_name}</bold> child nodes which has multi-value property * permissions. Permissions are defined by JCR specifications. * * <pre> * {node} {mode:AccessControllable} * +mode:acl {mode:Acl} * +user-name {mode:permission} * -permissions {String} * </pre> * * @author kulikov */ public class JcrAccessControlList implements AccessControlList { // ACL entries private HashMap<Principal, AccessControlEntryImpl> principals = new HashMap<Principal, AccessControlEntryImpl>(); // path to the node to which this ACL belongs private final String path; /** * Creates default Access Control List. * * @param acm access control manager instance * @return Access Control List with all permissions granted to everyone. */ public static JcrAccessControlList defaultAcl( AccessControlManagerImpl acm ) { JcrAccessControlList acl = new JcrAccessControlList("/"); try { acl.principals.put(SimplePrincipal.EVERYONE, new AccessControlEntryImpl(SimplePrincipal.EVERYONE, acm.privileges())); } catch (AccessControlException e) { // will never happen } return acl; } /** * Creates new empty access control list. * * @param path the path to which this access list is applied. */ public JcrAccessControlList( String path ) { this.path = path; } /** * Checks entries of this access control list. * * @return true if this access list does not have entries and false otherwise. */ public boolean isEmpty() { return principals.isEmpty(); } @Override public AccessControlEntry[] getAccessControlEntries() { AccessControlEntry[] list = new AccessControlEntry[principals.values().size()]; principals.values().toArray(list); return list; } @Override public boolean addAccessControlEntry( Principal principal, Privilege[] privileges ) throws AccessControlException, RepositoryException { if (privileges == null || privileges.length == 0) { throw new AccessControlException("Invalid privilege array"); } if (principal.getName().equals("unknown")) { throw new AccessControlException("Unknown principal"); } // Just new entry if (!principals.containsKey(principal)) { principals.put(principal, new AccessControlEntryImpl(principal, privileges)); return true; } // there is entry for the given principal so just add missing privileges AccessControlEntryImpl ace = principals.get(principal); return ace.addIfNotPresent(privileges); } @Override public void removeAccessControlEntry( AccessControlEntry accessControlEntry ) throws AccessControlException, RepositoryException { AccessControlEntry entry = principals.remove(accessControlEntry.getPrincipal()); if (entry == null) throw new AccessControlException("Invalid access control entry"); } /** * Tests privileges relatively to the given security context. * * @param sc security context carrying information about principals * @param privileges privileges for test * @return true when access list grants all given privileges within given security context. */ public boolean hasPrivileges( SecurityContext sc, Privilege[] privileges ) { for (AccessControlEntryImpl ace : principals.values()) { // check access list for everyone if (ace.getPrincipal().getName().equals(SimplePrincipal.EVERYONE.getName())) { if (ace.hasPrivileges(privileges)) { return true; } } // check user principal if (ace.getPrincipal().getName().equals(username(sc.getUserName()))) { if (ace.hasPrivileges(privileges)) { return true; } } // check group/role principal if (sc.hasRole(ace.getPrincipal().getName())) { if (ace.hasPrivileges(privileges)) { return true; } } } return false; } /** * Lists all privileges defined by this access list for the given user. * * @param context the security context of the user; never null * @return list of privilege objects. */ public Privilege[] getPrivileges( SecurityContext context ) { ArrayList<Privilege> privs = new ArrayList<Privilege>(); for (AccessControlEntryImpl ace : principals.values()) { // add privileges granted for everyone if (ace.getPrincipal().equals(SimplePrincipal.EVERYONE)) { privs.addAll(Arrays.asList(ace.getPrivileges())); } // add privileges granted for given user if (ace.getPrincipal().getName().equals(username(context.getUserName()))) { privs.addAll(Arrays.asList(ace.getPrivileges())); } // add privileges granted for given role if (context.hasRole(ace.getPrincipal().getName())) { privs.addAll(Arrays.asList(ace.getPrivileges())); } } Privilege[] res = new Privilege[privs.size()]; privs.toArray(res); return res; } public boolean hasEntry( String name ) { AccessControlEntry[] entries = this.getAccessControlEntries(); for (int i = 0; i < entries.length; i++) { if (entries[i].getPrincipal().getName().equals(name)) { return true; } } return false; } @Override public boolean equals( Object other ) { if (other == null) { return false; } if (!(other instanceof JcrAccessControlList)) { return false; } return ((JcrAccessControlList)other).path.equals(path); } @Override public int hashCode() { return this.path.hashCode(); } @Override public String toString() { final StringBuilder sb = new StringBuilder("ACL["); sb.append(path).append(", "); if (principals.isEmpty()) { sb.append(" <is empty>"); } else { for (Iterator<AccessControlEntryImpl> entryIterator = principals.values().iterator(); entryIterator.hasNext(); ) { sb.append(entryIterator.next()); if (entryIterator.hasNext()) { sb.append(","); } } } sb.append(']'); return sb.toString(); } /** * Removes brackets enclosing given user name * * @param username the user name * @return user name without brackets. */ private String username( String username ) { return (username.startsWith("<") && username.endsWith(">")) ? username.substring(1, username.length() - 1) : username; } }