src/java.base/share/classes/java/security/BasicPermission.java
changeset 47216 71c04702a3d5
parent 45434 4582657c7260
child 57950 4612a3cfb927
child 58678 9cf78a70fa4f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/java/security/BasicPermission.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,548 @@
+/*
+ * Copyright (c) 1997, 2015, 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 java.security;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamField;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * The BasicPermission class extends the Permission class, and
+ * can be used as the base class for permissions that want to
+ * follow the same naming convention as BasicPermission.
+ * <P>
+ * The name for a BasicPermission is the name of the given permission
+ * (for example, "exit",
+ * "setFactory", "print.queueJob", etc). The naming
+ * convention follows the  hierarchical property naming convention.
+ * An asterisk may appear by itself, or if immediately preceded by a "."
+ * may appear at the end of the name, to signify a wildcard match.
+ * For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b",
+ * and "java*" do not.
+ * <P>
+ * The action string (inherited from Permission) is unused.
+ * Thus, BasicPermission is commonly used as the base class for
+ * "named" permissions
+ * (ones that contain a name but no actions list; you either have the
+ * named permission or you don't.)
+ * Subclasses may implement actions on top of BasicPermission,
+ * if desired.
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ * @see java.security.PermissionCollection
+ * @see java.lang.SecurityManager
+ *
+ * @author Marianne Mueller
+ * @author Roland Schemers
+ * @since 1.2
+ */
+
+public abstract class BasicPermission extends Permission
+    implements java.io.Serializable
+{
+
+    private static final long serialVersionUID = 6279438298436773498L;
+
+    // does this permission have a wildcard at the end?
+    private transient boolean wildcard;
+
+    // the name without the wildcard on the end
+    private transient String path;
+
+    // is this permission the old-style exitVM permission (pre JDK 1.6)?
+    private transient boolean exitVM;
+
+    /**
+     * initialize a BasicPermission object. Common to all constructors.
+     */
+    private void init(String name) {
+        if (name == null)
+            throw new NullPointerException("name can't be null");
+
+        int len = name.length();
+
+        if (len == 0) {
+            throw new IllegalArgumentException("name can't be empty");
+        }
+
+        char last = name.charAt(len - 1);
+
+        // Is wildcard or ends with ".*"?
+        if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {
+            wildcard = true;
+            if (len == 1) {
+                path = "";
+            } else {
+                path = name.substring(0, len - 1);
+            }
+        } else {
+            if (name.equals("exitVM")) {
+                wildcard = true;
+                path = "exitVM.";
+                exitVM = true;
+            } else {
+                path = name;
+            }
+        }
+    }
+
+    /**
+     * Creates a new BasicPermission with the specified name.
+     * Name is the symbolic name of the permission, such as
+     * "setFactory",
+     * "print.queueJob", or "topLevelWindow", etc.
+     *
+     * @param name the name of the BasicPermission.
+     *
+     * @throws NullPointerException if {@code name} is {@code null}.
+     * @throws IllegalArgumentException if {@code name} is empty.
+     */
+    public BasicPermission(String name) {
+        super(name);
+        init(name);
+    }
+
+
+    /**
+     * Creates a new BasicPermission object with the specified name.
+     * The name is the symbolic name of the BasicPermission, and the
+     * actions String is currently unused.
+     *
+     * @param name the name of the BasicPermission.
+     * @param actions ignored.
+     *
+     * @throws NullPointerException if {@code name} is {@code null}.
+     * @throws IllegalArgumentException if {@code name} is empty.
+     */
+    public BasicPermission(String name, String actions) {
+        super(name);
+        init(name);
+    }
+
+    /**
+     * Checks if the specified permission is "implied" by
+     * this object.
+     * <P>
+     * More specifically, this method returns true if:
+     * <ul>
+     * <li> {@code p}'s class is the same as this object's class, and
+     * <li> {@code p}'s name equals or (in the case of wildcards)
+     *      is implied by this object's
+     *      name. For example, "a.b.*" implies "a.b.c".
+     * </ul>
+     *
+     * @param p the permission to check against.
+     *
+     * @return true if the passed permission is equal to or
+     * implied by this permission, false otherwise.
+     */
+    @Override
+    public boolean implies(Permission p) {
+        if ((p == null) || (p.getClass() != getClass()))
+            return false;
+
+        BasicPermission that = (BasicPermission) p;
+
+        if (this.wildcard) {
+            if (that.wildcard) {
+                // one wildcard can imply another
+                return that.path.startsWith(path);
+            } else {
+                // make sure ap.path is longer so a.b.* doesn't imply a.b
+                return (that.path.length() > this.path.length()) &&
+                    that.path.startsWith(this.path);
+            }
+        } else {
+            if (that.wildcard) {
+                // a non-wildcard can't imply a wildcard
+                return false;
+            }
+            else {
+                return this.path.equals(that.path);
+            }
+        }
+    }
+
+    /**
+     * Checks two BasicPermission objects for equality.
+     * Checks that {@code obj}'s class is the same as this object's class
+     * and has the same name as this object.
+     *
+     * @param obj the object we are testing for equality with this object.
+     * @return true if {@code obj}'s class is the same as this object's class
+     *  and has the same name as this BasicPermission object, false otherwise.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this)
+            return true;
+
+        if ((obj == null) || (obj.getClass() != getClass()))
+            return false;
+
+        BasicPermission bp = (BasicPermission) obj;
+
+        return getName().equals(bp.getName());
+    }
+
+
+    /**
+     * Returns the hash code value for this object.
+     * The hash code used is the hash code of the name, that is,
+     * {@code getName().hashCode()}, where {@code getName} is
+     * from the Permission superclass.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    public int hashCode() {
+        return this.getName().hashCode();
+    }
+
+    /**
+     * Returns the canonical string representation of the actions,
+     * which currently is the empty string "", since there are no actions for
+     * a BasicPermission.
+     *
+     * @return the empty string "".
+     */
+    @Override
+    public String getActions() {
+        return "";
+    }
+
+    /**
+     * Returns a new PermissionCollection object for storing BasicPermission
+     * objects.
+     *
+     * <p>BasicPermission objects must be stored in a manner that allows them
+     * to be inserted in any order, but that also enables the
+     * PermissionCollection {@code implies} method
+     * to be implemented in an efficient (and consistent) manner.
+     *
+     * @return a new PermissionCollection object suitable for
+     * storing BasicPermissions.
+     */
+    @Override
+    public PermissionCollection newPermissionCollection() {
+        return new BasicPermissionCollection(this.getClass());
+    }
+
+    /**
+     * readObject is called to restore the state of the BasicPermission from
+     * a stream.
+     */
+    private void readObject(ObjectInputStream s)
+         throws IOException, ClassNotFoundException
+    {
+        s.defaultReadObject();
+        // init is called to initialize the rest of the values.
+        init(getName());
+    }
+
+    /**
+     * Returns the canonical name of this BasicPermission.
+     * All internal invocations of getName should invoke this method, so
+     * that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are
+     * equivalent in equals/hashCode methods.
+     *
+     * @return the canonical name of this BasicPermission.
+     */
+    final String getCanonicalName() {
+        return exitVM ? "exitVM.*" : getName();
+    }
+}
+
+/**
+ * A BasicPermissionCollection stores a collection
+ * of BasicPermission permissions. BasicPermission objects
+ * must be stored in a manner that allows them to be inserted in any
+ * order, but enable the implies function to evaluate the implies
+ * method in an efficient (and consistent) manner.
+ *
+ * A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"
+ * with a Permission such as "a.b.*", or "*".
+ *
+ * @see java.security.Permission
+ * @see java.security.Permissions
+ *
+ *
+ * @author Roland Schemers
+ *
+ * @serial include
+ */
+
+final class BasicPermissionCollection
+    extends PermissionCollection
+    implements java.io.Serializable
+{
+
+    private static final long serialVersionUID = 739301742472979399L;
+
+    /**
+      * Key is name, value is permission. All permission objects in
+      * collection must be of the same type.
+      * Not serialized; see serialization section at end of class.
+      */
+    private transient ConcurrentHashMap<String, Permission> perms;
+
+    /**
+     * This is set to {@code true} if this BasicPermissionCollection
+     * contains a BasicPermission with '*' as its permission name.
+     *
+     * @see #serialPersistentFields
+     */
+    private boolean all_allowed;
+
+    /**
+     * The class to which all BasicPermissions in this
+     * BasicPermissionCollection belong.
+     *
+     * @see #serialPersistentFields
+     */
+    private Class<?> permClass;
+
+    /**
+     * Create an empty BasicPermissionCollection object.
+     *
+     */
+    public BasicPermissionCollection(Class<?> clazz) {
+        perms = new ConcurrentHashMap<>(11);
+        all_allowed = false;
+        permClass = clazz;
+    }
+
+    /**
+     * Adds a permission to the BasicPermissions. The key for the hash is
+     * permission.path.
+     *
+     * @param permission the Permission object to add.
+     *
+     * @exception IllegalArgumentException - if the permission is not a
+     *                                       BasicPermission, or if
+     *                                       the permission is not of the
+     *                                       same Class as the other
+     *                                       permissions in this collection.
+     *
+     * @exception SecurityException - if this BasicPermissionCollection object
+     *                                has been marked readonly
+     */
+    @Override
+    public void add(Permission permission) {
+        if (! (permission instanceof BasicPermission))
+            throw new IllegalArgumentException("invalid permission: "+
+                                               permission);
+        if (isReadOnly())
+            throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");
+
+        BasicPermission bp = (BasicPermission) permission;
+
+        // make sure we only add new BasicPermissions of the same class
+        // Also check null for compatibility with deserialized form from
+        // previous versions.
+        if (permClass == null) {
+            // adding first permission
+            permClass = bp.getClass();
+        } else {
+            if (bp.getClass() != permClass)
+                throw new IllegalArgumentException("invalid permission: " +
+                                                permission);
+        }
+
+        String canonName = bp.getCanonicalName();
+        perms.put(canonName, permission);
+
+        // No sync on all_allowed; staleness OK
+        if (!all_allowed) {
+            if (canonName.equals("*"))
+                all_allowed = true;
+        }
+    }
+
+    /**
+     * Check and see if this set of permissions implies the permissions
+     * expressed in "permission".
+     *
+     * @param permission the Permission object to compare
+     *
+     * @return true if "permission" is a proper subset of a permission in
+     * the set, false if not.
+     */
+    @Override
+    public boolean implies(Permission permission) {
+        if (! (permission instanceof BasicPermission))
+            return false;
+
+        BasicPermission bp = (BasicPermission) permission;
+
+        // random subclasses of BasicPermission do not imply each other
+        if (bp.getClass() != permClass)
+            return false;
+
+        // short circuit if the "*" Permission was added
+        if (all_allowed)
+            return true;
+
+        // strategy:
+        // Check for full match first. Then work our way up the
+        // path looking for matches on a.b..*
+
+        String path = bp.getCanonicalName();
+        //System.out.println("check "+path);
+
+        Permission x = perms.get(path);
+
+        if (x != null) {
+            // we have a direct hit!
+            return x.implies(permission);
+        }
+
+        // work our way up the tree...
+        int last, offset;
+
+        offset = path.length()-1;
+
+        while ((last = path.lastIndexOf('.', offset)) != -1) {
+
+            path = path.substring(0, last+1) + "*";
+            //System.out.println("check "+path);
+
+            x = perms.get(path);
+
+            if (x != null) {
+                return x.implies(permission);
+            }
+            offset = last -1;
+        }
+
+        // we don't have to check for "*" as it was already checked
+        // at the top (all_allowed), so we just return false
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of all the BasicPermission objects in the
+     * container.
+     *
+     * @return an enumeration of all the BasicPermission objects.
+     */
+    @Override
+    public Enumeration<Permission> elements() {
+        return perms.elements();
+    }
+
+    // Need to maintain serialization interoperability with earlier releases,
+    // which had the serializable field:
+    //
+    // @serial the Hashtable is indexed by the BasicPermission name
+    //
+    // private Hashtable permissions;
+    /**
+     * @serialField permissions java.util.Hashtable
+     *    The BasicPermissions in this BasicPermissionCollection.
+     *    All BasicPermissions in the collection must belong to the same class.
+     *    The Hashtable is indexed by the BasicPermission name; the value
+     *    of the Hashtable entry is the permission.
+     * @serialField all_allowed boolean
+     *   This is set to {@code true} if this BasicPermissionCollection
+     *   contains a BasicPermission with '*' as its permission name.
+     * @serialField permClass java.lang.Class
+     *   The class to which all BasicPermissions in this
+     *   BasicPermissionCollection belongs.
+     */
+    private static final ObjectStreamField[] serialPersistentFields = {
+        new ObjectStreamField("permissions", Hashtable.class),
+        new ObjectStreamField("all_allowed", Boolean.TYPE),
+        new ObjectStreamField("permClass", Class.class),
+    };
+
+    /**
+     * @serialData Default fields.
+     */
+    /*
+     * Writes the contents of the perms field out as a Hashtable for
+     * serialization compatibility with earlier releases. all_allowed
+     * and permClass unchanged.
+     */
+    private void writeObject(ObjectOutputStream out) throws IOException {
+        // Don't call out.defaultWriteObject()
+
+        // Copy perms into a Hashtable
+        Hashtable<String, Permission> permissions =
+                new Hashtable<>(perms.size()*2);
+
+        permissions.putAll(perms);
+
+        // Write out serializable fields
+        ObjectOutputStream.PutField pfields = out.putFields();
+        pfields.put("all_allowed", all_allowed);
+        pfields.put("permissions", permissions);
+        pfields.put("permClass", permClass);
+        out.writeFields();
+    }
+
+    /**
+     * readObject is called to restore the state of the
+     * BasicPermissionCollection from a stream.
+     */
+    private void readObject(java.io.ObjectInputStream in)
+         throws IOException, ClassNotFoundException
+    {
+        // Don't call defaultReadObject()
+
+        // Read in serialized fields
+        ObjectInputStream.GetField gfields = in.readFields();
+
+        // Get permissions
+        // writeObject writes a Hashtable<String, Permission> for the
+        // permissions key, so this cast is safe, unless the data is corrupt.
+        @SuppressWarnings("unchecked")
+        Hashtable<String, Permission> permissions =
+                (Hashtable<String, Permission>)gfields.get("permissions", null);
+        perms = new ConcurrentHashMap<>(permissions.size()*2);
+        perms.putAll(permissions);
+
+        // Get all_allowed
+        all_allowed = gfields.get("all_allowed", false);
+
+        // Get permClass
+        permClass = (Class<?>) gfields.get("permClass", null);
+
+        if (permClass == null) {
+            // set permClass
+            Enumeration<Permission> e = permissions.elements();
+            if (e.hasMoreElements()) {
+                Permission p = e.nextElement();
+                permClass = p.getClass();
+            }
+        }
+    }
+}