jdk/src/java.base/share/classes/sun/security/acl/AclImpl.java
changeset 28773 0b8da4d7cda6
parent 25859 3317bb8137f4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/java.base/share/classes/sun/security/acl/AclImpl.java	Tue Feb 03 14:09:20 2015 -0800
@@ -0,0 +1,408 @@
+/*
+ * 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() {
+        StringBuilder sb = new StringBuilder();
+        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");
+    }
+}