Path: blob/master/src/java.base/share/classes/java/security/BasicPermission.java
41152 views
/*1* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.security;2627import java.io.IOException;28import java.io.ObjectInputStream;29import java.io.ObjectOutputStream;30import java.io.ObjectStreamField;31import java.util.Enumeration;32import java.util.Hashtable;33import java.util.concurrent.ConcurrentHashMap;3435/**36* The BasicPermission class extends the Permission class, and37* can be used as the base class for permissions that want to38* follow the same naming convention as BasicPermission.39* <P>40* The name for a BasicPermission is the name of the given permission41* (for example, "exit",42* "setFactory", "print.queueJob", etc). The naming43* convention follows the hierarchical property naming convention.44* An asterisk may appear by itself, or if immediately preceded by a "."45* may appear at the end of the name, to signify a wildcard match.46* For example, "*" and "java.*" signify a wildcard match, while "*java", "a*b",47* and "java*" do not.48* <P>49* The action string (inherited from Permission) is unused.50* Thus, BasicPermission is commonly used as the base class for51* "named" permissions52* (ones that contain a name but no actions list; you either have the53* named permission or you don't.)54* Subclasses may implement actions on top of BasicPermission,55* if desired.56*57* @see java.security.Permission58* @see java.security.Permissions59* @see java.security.PermissionCollection60* @see java.lang.SecurityManager61*62* @author Marianne Mueller63* @author Roland Schemers64* @since 1.265*/6667public abstract class BasicPermission extends Permission68implements java.io.Serializable69{7071@java.io.Serial72private static final long serialVersionUID = 6279438298436773498L;7374// does this permission have a wildcard at the end?75private transient boolean wildcard;7677// the name without the wildcard on the end78private transient String path;7980// is this permission the old-style exitVM permission (pre JDK 1.6)?81private transient boolean exitVM;8283/**84* initialize a BasicPermission object. Common to all constructors.85*/86private void init(String name) {87if (name == null)88throw new NullPointerException("name can't be null");8990int len = name.length();9192if (len == 0) {93throw new IllegalArgumentException("name can't be empty");94}9596char last = name.charAt(len - 1);9798// Is wildcard or ends with ".*"?99if (last == '*' && (len == 1 || name.charAt(len - 2) == '.')) {100wildcard = true;101if (len == 1) {102path = "";103} else {104path = name.substring(0, len - 1);105}106} else {107if (name.equals("exitVM")) {108wildcard = true;109path = "exitVM.";110exitVM = true;111} else {112path = name;113}114}115}116117/**118* Creates a new BasicPermission with the specified name.119* Name is the symbolic name of the permission, such as120* "setFactory",121* "print.queueJob", or "topLevelWindow", etc.122*123* @param name the name of the BasicPermission.124*125* @throws NullPointerException if {@code name} is {@code null}.126* @throws IllegalArgumentException if {@code name} is empty.127*/128public BasicPermission(String name) {129super(name);130init(name);131}132133134/**135* Creates a new BasicPermission object with the specified name.136* The name is the symbolic name of the BasicPermission, and the137* actions String is currently unused.138*139* @param name the name of the BasicPermission.140* @param actions ignored.141*142* @throws NullPointerException if {@code name} is {@code null}.143* @throws IllegalArgumentException if {@code name} is empty.144*/145public BasicPermission(String name, String actions) {146super(name);147init(name);148}149150/**151* Checks if the specified permission is "implied" by152* this object.153* <P>154* More specifically, this method returns true if:155* <ul>156* <li> {@code p}'s class is the same as this object's class, and157* <li> {@code p}'s name equals or (in the case of wildcards)158* is implied by this object's159* name. For example, "a.b.*" implies "a.b.c".160* </ul>161*162* @param p the permission to check against.163*164* @return true if the passed permission is equal to or165* implied by this permission, false otherwise.166*/167@Override168public boolean implies(Permission p) {169if ((p == null) || (p.getClass() != getClass()))170return false;171172BasicPermission that = (BasicPermission) p;173174if (this.wildcard) {175if (that.wildcard) {176// one wildcard can imply another177return that.path.startsWith(path);178} else {179// make sure ap.path is longer so a.b.* doesn't imply a.b180return (that.path.length() > this.path.length()) &&181that.path.startsWith(this.path);182}183} else {184if (that.wildcard) {185// a non-wildcard can't imply a wildcard186return false;187}188else {189return this.path.equals(that.path);190}191}192}193194/**195* Checks two BasicPermission objects for equality.196* Checks that {@code obj}'s class is the same as this object's class197* and has the same name as this object.198*199* @param obj the object we are testing for equality with this object.200* @return true if {@code obj}'s class is the same as this object's class201* and has the same name as this BasicPermission object, false otherwise.202*/203@Override204public boolean equals(Object obj) {205if (obj == this)206return true;207208if ((obj == null) || (obj.getClass() != getClass()))209return false;210211BasicPermission bp = (BasicPermission) obj;212213return getName().equals(bp.getName());214}215216217/**218* Returns the hash code value for this object.219* The hash code used is the hash code of the name, that is,220* {@code getName().hashCode()}, where {@code getName} is221* from the Permission superclass.222*223* @return a hash code value for this object.224*/225@Override226public int hashCode() {227return this.getName().hashCode();228}229230/**231* Returns the canonical string representation of the actions,232* which currently is the empty string "", since there are no actions for233* a BasicPermission.234*235* @return the empty string "".236*/237@Override238public String getActions() {239return "";240}241242/**243* Returns a new PermissionCollection object for storing BasicPermission244* objects.245*246* <p>BasicPermission objects must be stored in a manner that allows them247* to be inserted in any order, but that also enables the248* PermissionCollection {@code implies} method249* to be implemented in an efficient (and consistent) manner.250*251* @return a new PermissionCollection object suitable for252* storing BasicPermissions.253*/254@Override255public PermissionCollection newPermissionCollection() {256return new BasicPermissionCollection(this.getClass());257}258259/**260* readObject is called to restore the state of the BasicPermission from261* a stream.262*263* @param s the {@code ObjectInputStream} from which data is read264* @throws IOException if an I/O error occurs265* @throws ClassNotFoundException if a serialized class cannot be loaded266*/267@java.io.Serial268private void readObject(ObjectInputStream s)269throws IOException, ClassNotFoundException270{271s.defaultReadObject();272// init is called to initialize the rest of the values.273init(getName());274}275276/**277* Returns the canonical name of this BasicPermission.278* All internal invocations of getName should invoke this method, so279* that the pre-JDK 1.6 "exitVM" and current "exitVM.*" permission are280* equivalent in equals/hashCode methods.281*282* @return the canonical name of this BasicPermission.283*/284final String getCanonicalName() {285return exitVM ? "exitVM.*" : getName();286}287}288289/**290* A BasicPermissionCollection stores a collection291* of BasicPermission permissions. BasicPermission objects292* must be stored in a manner that allows them to be inserted in any293* order, but enable the implies function to evaluate the implies294* method in an efficient (and consistent) manner.295*296* A BasicPermissionCollection handles comparing a permission like "a.b.c.d.e"297* with a Permission such as "a.b.*", or "*".298*299* @see java.security.Permission300* @see java.security.Permissions301*302*303* @author Roland Schemers304*305* @serial include306*/307308final class BasicPermissionCollection309extends PermissionCollection310implements java.io.Serializable311{312313@java.io.Serial314private static final long serialVersionUID = 739301742472979399L;315316/**317* Key is name, value is permission. All permission objects in318* collection must be of the same type.319* Not serialized; see serialization section at end of class.320*/321private transient ConcurrentHashMap<String, Permission> perms;322323/**324* This is set to {@code true} if this BasicPermissionCollection325* contains a BasicPermission with '*' as its permission name.326*327* @see #serialPersistentFields328*/329private boolean all_allowed;330331/**332* The class to which all BasicPermissions in this333* BasicPermissionCollection belong.334*335* @see #serialPersistentFields336*/337private Class<?> permClass;338339/**340* Create an empty BasicPermissionCollection object.341*342*/343public BasicPermissionCollection(Class<?> clazz) {344perms = new ConcurrentHashMap<>(11);345all_allowed = false;346permClass = clazz;347}348349/**350* Adds a permission to the BasicPermissions. The key for the hash is351* permission.path.352*353* @param permission the Permission object to add.354*355* @throws IllegalArgumentException if the permission is not a356* BasicPermission, or if357* the permission is not of the358* same Class as the other359* permissions in this collection.360*361* @throws SecurityException if this BasicPermissionCollection object362* has been marked readonly363*/364@Override365public void add(Permission permission) {366if (!(permission instanceof BasicPermission basicPermission))367throw new IllegalArgumentException("invalid permission: "+368permission);369if (isReadOnly())370throw new SecurityException("attempt to add a Permission to a readonly PermissionCollection");371372// make sure we only add new BasicPermissions of the same class373// Also check null for compatibility with deserialized form from374// previous versions.375if (permClass == null) {376// adding first permission377permClass = basicPermission.getClass();378} else {379if (basicPermission.getClass() != permClass)380throw new IllegalArgumentException("invalid permission: " +381permission);382}383384String canonName = basicPermission.getCanonicalName();385perms.put(canonName, permission);386387// No sync on all_allowed; staleness OK388if (!all_allowed) {389if (canonName.equals("*"))390all_allowed = true;391}392}393394/**395* Check and see if this set of permissions implies the permissions396* expressed in "permission".397*398* @param permission the Permission object to compare399*400* @return true if "permission" is a proper subset of a permission in401* the set, false if not.402*/403@Override404public boolean implies(Permission permission) {405if (!(permission instanceof BasicPermission basicPermission))406return false;407408// random subclasses of BasicPermission do not imply each other409if (basicPermission.getClass() != permClass)410return false;411412// short circuit if the "*" Permission was added413if (all_allowed)414return true;415416// strategy:417// Check for full match first. Then work our way up the418// path looking for matches on a.b..*419420String path = basicPermission.getCanonicalName();421//System.out.println("check "+path);422423Permission x = perms.get(path);424425if (x != null) {426// we have a direct hit!427return x.implies(permission);428}429430// work our way up the tree...431int last, offset;432433offset = path.length()-1;434435while ((last = path.lastIndexOf('.', offset)) != -1) {436437path = path.substring(0, last+1) + "*";438//System.out.println("check "+path);439440x = perms.get(path);441442if (x != null) {443return x.implies(permission);444}445offset = last -1;446}447448// we don't have to check for "*" as it was already checked449// at the top (all_allowed), so we just return false450return false;451}452453/**454* Returns an enumeration of all the BasicPermission objects in the455* container.456*457* @return an enumeration of all the BasicPermission objects.458*/459@Override460public Enumeration<Permission> elements() {461return perms.elements();462}463464// Need to maintain serialization interoperability with earlier releases,465// which had the serializable field:466//467// @serial the Hashtable is indexed by the BasicPermission name468//469// private Hashtable permissions;470/**471* @serialField permissions java.util.Hashtable472* The BasicPermissions in this BasicPermissionCollection.473* All BasicPermissions in the collection must belong to the same class.474* The Hashtable is indexed by the BasicPermission name; the value475* of the Hashtable entry is the permission.476* @serialField all_allowed boolean477* This is set to {@code true} if this BasicPermissionCollection478* contains a BasicPermission with '*' as its permission name.479* @serialField permClass java.lang.Class480* The class to which all BasicPermissions in this481* BasicPermissionCollection belongs.482*/483@java.io.Serial484private static final ObjectStreamField[] serialPersistentFields = {485new ObjectStreamField("permissions", Hashtable.class),486new ObjectStreamField("all_allowed", Boolean.TYPE),487new ObjectStreamField("permClass", Class.class),488};489490/*491* @serialData Default fields.492*/493494/**495* Writes the contents of the perms field out as a Hashtable for496* serialization compatibility with earlier releases. all_allowed497* and permClass unchanged.498*499* @param out the {@code ObjectOutputStream} to which data is written500* @throws IOException if an I/O error occurs501*/502@java.io.Serial503private void writeObject(ObjectOutputStream out) throws IOException {504// Don't call out.defaultWriteObject()505506// Copy perms into a Hashtable507Hashtable<String, Permission> permissions =508new Hashtable<>(perms.size()*2);509510permissions.putAll(perms);511512// Write out serializable fields513ObjectOutputStream.PutField pfields = out.putFields();514pfields.put("all_allowed", all_allowed);515pfields.put("permissions", permissions);516pfields.put("permClass", permClass);517out.writeFields();518}519520/**521* readObject is called to restore the state of the522* BasicPermissionCollection from a stream.523*524* @param in the {@code ObjectInputStream} from which data is read525* @throws IOException if an I/O error occurs526* @throws ClassNotFoundException if a serialized class cannot be loaded527*/528@java.io.Serial529private void readObject(java.io.ObjectInputStream in)530throws IOException, ClassNotFoundException531{532// Don't call defaultReadObject()533534// Read in serialized fields535ObjectInputStream.GetField gfields = in.readFields();536537// Get permissions538// writeObject writes a Hashtable<String, Permission> for the539// permissions key, so this cast is safe, unless the data is corrupt.540@SuppressWarnings("unchecked")541Hashtable<String, Permission> permissions =542(Hashtable<String, Permission>)gfields.get("permissions", null);543perms = new ConcurrentHashMap<>(permissions.size()*2);544perms.putAll(permissions);545546// Get all_allowed547all_allowed = gfields.get("all_allowed", false);548549// Get permClass550permClass = (Class<?>) gfields.get("permClass", null);551552if (permClass == null) {553// set permClass554Enumeration<Permission> e = permissions.elements();555if (e.hasMoreElements()) {556Permission p = e.nextElement();557permClass = p.getClass();558}559}560}561}562563564