| /* |
| * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| * |
| * This code is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License version 2 only, as |
| * published by the Free Software Foundation. Oracle designates this |
| * particular file as subject to the "Classpath" exception as provided |
| * by Oracle in the LICENSE file that accompanied this code. |
| * |
| * This code 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 |
| * version 2 for more details (a copy is included in the LICENSE file that |
| * accompanied this code). |
| * |
| * You should have received a copy of the GNU General Public License version |
| * 2 along with this work; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| * |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| package sun.security.acl; |
| |
| import java.io.*; |
| import java.util.*; |
| import java.security.Principal; |
| import java.security.acl.*; |
| |
| /** |
| * An Access Control List (ACL) is encapsulated by this class. |
| * @author Satish Dharmaraj |
| */ |
| public class AclImpl extends OwnerImpl implements Acl { |
| // |
| // Maintain four tables. one each for positive and negative |
| // ACLs. One each depending on whether the entity is a group |
| // or principal. |
| // |
| private Hashtable<Principal, AclEntry> allowedUsersTable = |
| new Hashtable<>(23); |
| private Hashtable<Principal, AclEntry> allowedGroupsTable = |
| new Hashtable<>(23); |
| private Hashtable<Principal, AclEntry> deniedUsersTable = |
| new Hashtable<>(23); |
| private Hashtable<Principal, AclEntry> deniedGroupsTable = |
| new Hashtable<>(23); |
| private String aclName = null; |
| private Vector<Permission> zeroSet = new Vector<>(1,1); |
| |
| |
| /** |
| * Constructor for creating an empty ACL. |
| */ |
| public AclImpl(Principal owner, String name) { |
| super(owner); |
| try { |
| setName(owner, name); |
| } catch (Exception e) {} |
| } |
| |
| /** |
| * Sets the name of the ACL. |
| * @param caller the principal who is invoking this method. |
| * @param name the name of the ACL. |
| * @exception NotOwnerException if the caller principal is |
| * not on the owners list of the Acl. |
| */ |
| public void setName(Principal caller, String name) |
| throws NotOwnerException |
| { |
| if (!isOwner(caller)) |
| throw new NotOwnerException(); |
| |
| aclName = name; |
| } |
| |
| /** |
| * Returns the name of the ACL. |
| * @return the name of the ACL. |
| */ |
| public String getName() { |
| return aclName; |
| } |
| |
| /** |
| * Adds an ACL entry to this ACL. An entry associates a |
| * group or a principal with a set of permissions. Each |
| * user or group can have one positive ACL entry and one |
| * negative ACL entry. If there is one of the type (negative |
| * or positive) already in the table, a false value is returned. |
| * The caller principal must be a part of the owners list of |
| * the ACL in order to invoke this method. |
| * @param caller the principal who is invoking this method. |
| * @param entry the ACL entry that must be added to the ACL. |
| * @return true on success, false if the entry is already present. |
| * @exception NotOwnerException if the caller principal |
| * is not on the owners list of the Acl. |
| */ |
| public synchronized boolean addEntry(Principal caller, AclEntry entry) |
| throws NotOwnerException |
| { |
| if (!isOwner(caller)) |
| throw new NotOwnerException(); |
| |
| Hashtable<Principal, AclEntry> aclTable = findTable(entry); |
| Principal key = entry.getPrincipal(); |
| |
| if (aclTable.get(key) != null) |
| return false; |
| |
| aclTable.put(key, entry); |
| return true; |
| } |
| |
| /** |
| * Removes an ACL entry from this ACL. |
| * The caller principal must be a part of the owners list of the ACL |
| * in order to invoke this method. |
| * @param caller the principal who is invoking this method. |
| * @param entry the ACL entry that must be removed from the ACL. |
| * @return true on success, false if the entry is not part of the ACL. |
| * @exception NotOwnerException if the caller principal is not |
| * the owners list of the Acl. |
| */ |
| public synchronized boolean removeEntry(Principal caller, AclEntry entry) |
| throws NotOwnerException |
| { |
| if (!isOwner(caller)) |
| throw new NotOwnerException(); |
| |
| Hashtable<Principal, AclEntry> aclTable = findTable(entry); |
| Principal key = entry.getPrincipal(); |
| |
| AclEntry o = aclTable.remove(key); |
| return (o != null); |
| } |
| |
| /** |
| * This method returns the set of allowed permissions for the |
| * specified principal. This set of allowed permissions is calculated |
| * as follows: |
| * |
| * If there is no entry for a group or a principal an empty permission |
| * set is assumed. |
| * |
| * The group positive permission set is the union of all |
| * the positive permissions of each group that the individual belongs to. |
| * The group negative permission set is the union of all |
| * the negative permissions of each group that the individual belongs to. |
| * If there is a specific permission that occurs in both |
| * the postive permission set and the negative permission set, |
| * it is removed from both. The group positive and negatoive permission |
| * sets are calculated. |
| * |
| * The individial positive permission set and the individual negative |
| * permission set is then calculated. Again abscence of an entry means |
| * the empty set. |
| * |
| * The set of permissions granted to the principal is then calculated using |
| * the simple rule: Individual permissions always override the Group permissions. |
| * Specifically, individual negative permission set (specific |
| * denial of permissions) overrides the group positive permission set. |
| * And the individual positive permission set override the group negative |
| * permission set. |
| * |
| * @param user the principal for which the ACL entry is returned. |
| * @return The resulting permission set that the principal is allowed. |
| */ |
| public synchronized Enumeration<Permission> getPermissions(Principal user) { |
| |
| Enumeration<Permission> individualPositive; |
| Enumeration<Permission> individualNegative; |
| Enumeration<Permission> groupPositive; |
| Enumeration<Permission> groupNegative; |
| |
| // |
| // canonicalize the sets. That is remove common permissions from |
| // positive and negative sets. |
| // |
| groupPositive = |
| subtract(getGroupPositive(user), getGroupNegative(user)); |
| groupNegative = |
| subtract(getGroupNegative(user), getGroupPositive(user)); |
| individualPositive = |
| subtract(getIndividualPositive(user), getIndividualNegative(user)); |
| individualNegative = |
| subtract(getIndividualNegative(user), getIndividualPositive(user)); |
| |
| // |
| // net positive permissions is individual positive permissions |
| // plus (group positive - individual negative). |
| // |
| Enumeration<Permission> temp1 = |
| subtract(groupPositive, individualNegative); |
| Enumeration<Permission> netPositive = |
| union(individualPositive, temp1); |
| |
| // recalculate the enumeration since we lost it in performing the |
| // subtraction |
| // |
| individualPositive = |
| subtract(getIndividualPositive(user), getIndividualNegative(user)); |
| individualNegative = |
| subtract(getIndividualNegative(user), getIndividualPositive(user)); |
| |
| // |
| // net negative permissions is individual negative permissions |
| // plus (group negative - individual positive). |
| // |
| temp1 = subtract(groupNegative, individualPositive); |
| Enumeration<Permission> netNegative = union(individualNegative, temp1); |
| |
| return subtract(netPositive, netNegative); |
| } |
| |
| /** |
| * This method checks whether or not the specified principal |
| * has the required permission. If permission is denied |
| * permission false is returned, a true value is returned otherwise. |
| * This method does not authenticate the principal. It presumes that |
| * the principal is a valid authenticated principal. |
| * @param principal the name of the authenticated principal |
| * @param permission the permission that the principal must have. |
| * @return true of the principal has the permission desired, false |
| * otherwise. |
| */ |
| public boolean checkPermission(Principal principal, Permission permission) |
| { |
| Enumeration<Permission> permSet = getPermissions(principal); |
| while (permSet.hasMoreElements()) { |
| Permission p = permSet.nextElement(); |
| if (p.equals(permission)) |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * returns an enumeration of the entries in this ACL. |
| */ |
| public synchronized Enumeration<AclEntry> entries() { |
| return new AclEnumerator(this, |
| allowedUsersTable, allowedGroupsTable, |
| deniedUsersTable, deniedGroupsTable); |
| } |
| |
| /** |
| * return a stringified version of the |
| * ACL. |
| */ |
| public String toString() { |
| StringBuffer sb = new StringBuffer(); |
| Enumeration<AclEntry> entries = entries(); |
| while (entries.hasMoreElements()) { |
| AclEntry entry = entries.nextElement(); |
| sb.append(entry.toString().trim()); |
| sb.append("\n"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| // |
| // Find the table that this entry belongs to. There are 4 |
| // tables that are maintained. One each for postive and |
| // negative ACLs and one each for groups and users. |
| // This method figures out which |
| // table is the one that this AclEntry belongs to. |
| // |
| private Hashtable<Principal, AclEntry> findTable(AclEntry entry) { |
| Hashtable<Principal, AclEntry> aclTable = null; |
| |
| Principal p = entry.getPrincipal(); |
| if (p instanceof Group) { |
| if (entry.isNegative()) |
| aclTable = deniedGroupsTable; |
| else |
| aclTable = allowedGroupsTable; |
| } else { |
| if (entry.isNegative()) |
| aclTable = deniedUsersTable; |
| else |
| aclTable = allowedUsersTable; |
| } |
| return aclTable; |
| } |
| |
| // |
| // returns the set e1 U e2. |
| // |
| private static Enumeration<Permission> union(Enumeration<Permission> e1, |
| Enumeration<Permission> e2) { |
| Vector<Permission> v = new Vector<>(20, 20); |
| |
| while (e1.hasMoreElements()) |
| v.addElement(e1.nextElement()); |
| |
| while (e2.hasMoreElements()) { |
| Permission o = e2.nextElement(); |
| if (!v.contains(o)) |
| v.addElement(o); |
| } |
| |
| return v.elements(); |
| } |
| |
| // |
| // returns the set e1 - e2. |
| // |
| private Enumeration<Permission> subtract(Enumeration<Permission> e1, |
| Enumeration<Permission> e2) { |
| Vector<Permission> v = new Vector<>(20, 20); |
| |
| while (e1.hasMoreElements()) |
| v.addElement(e1.nextElement()); |
| |
| while (e2.hasMoreElements()) { |
| Permission o = e2.nextElement(); |
| if (v.contains(o)) |
| v.removeElement(o); |
| } |
| |
| return v.elements(); |
| } |
| |
| private Enumeration<Permission> getGroupPositive(Principal user) { |
| Enumeration<Permission> groupPositive = zeroSet.elements(); |
| Enumeration<Principal> e = allowedGroupsTable.keys(); |
| while (e.hasMoreElements()) { |
| Group g = (Group)e.nextElement(); |
| if (g.isMember(user)) { |
| AclEntry ae = allowedGroupsTable.get(g); |
| groupPositive = union(ae.permissions(), groupPositive); |
| } |
| } |
| return groupPositive; |
| } |
| |
| private Enumeration<Permission> getGroupNegative(Principal user) { |
| Enumeration<Permission> groupNegative = zeroSet.elements(); |
| Enumeration<Principal> e = deniedGroupsTable.keys(); |
| while (e.hasMoreElements()) { |
| Group g = (Group)e.nextElement(); |
| if (g.isMember(user)) { |
| AclEntry ae = deniedGroupsTable.get(g); |
| groupNegative = union(ae.permissions(), groupNegative); |
| } |
| } |
| return groupNegative; |
| } |
| |
| private Enumeration<Permission> getIndividualPositive(Principal user) { |
| Enumeration<Permission> individualPositive = zeroSet.elements(); |
| AclEntry ae = allowedUsersTable.get(user); |
| if (ae != null) |
| individualPositive = ae.permissions(); |
| return individualPositive; |
| } |
| |
| private Enumeration<Permission> getIndividualNegative(Principal user) { |
| Enumeration<Permission> individualNegative = zeroSet.elements(); |
| AclEntry ae = deniedUsersTable.get(user); |
| if (ae != null) |
| individualNegative = ae.permissions(); |
| return individualNegative; |
| } |
| } |
| |
| final class AclEnumerator implements Enumeration<AclEntry> { |
| Acl acl; |
| Enumeration<AclEntry> u1, u2, g1, g2; |
| |
| AclEnumerator(Acl acl, Hashtable<?,AclEntry> u1, Hashtable<?,AclEntry> g1, |
| Hashtable<?,AclEntry> u2, Hashtable<?,AclEntry> g2) { |
| this.acl = acl; |
| this.u1 = u1.elements(); |
| this.u2 = u2.elements(); |
| this.g1 = g1.elements(); |
| this.g2 = g2.elements(); |
| } |
| |
| public boolean hasMoreElements() { |
| return (u1.hasMoreElements() || |
| u2.hasMoreElements() || |
| g1.hasMoreElements() || |
| g2.hasMoreElements()); |
| } |
| |
| public AclEntry nextElement() |
| { |
| AclEntry o; |
| synchronized (acl) { |
| if (u1.hasMoreElements()) |
| return u1.nextElement(); |
| if (u2.hasMoreElements()) |
| return u2.nextElement(); |
| if (g1.hasMoreElements()) |
| return g1.nextElement(); |
| if (g2.hasMoreElements()) |
| return g2.nextElement(); |
| } |
| throw new NoSuchElementException("Acl Enumerator"); |
| } |
| } |