Path: blob/master/src/java.base/share/classes/javax/security/auth/PrivateCredentialPermission.java
41159 views
/*1* Copyright (c) 1999, 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 javax.security.auth;2627import java.util.*;28import java.io.IOException;29import java.text.MessageFormat;30import java.security.Permission;31import java.security.PermissionCollection;32import java.security.Principal;33import sun.security.util.ResourcesMgr;3435/**36* This class is used to protect access to private Credentials37* belonging to a particular {@code Subject}. The {@code Subject}38* is represented by a Set of Principals.39*40* <p> The target name of this {@code Permission} specifies41* a Credential class name, and a Set of Principals.42* The only valid value for this Permission's actions is, "read".43* The target name must abide by the following syntax:44*45* <pre>46* CredentialClass {PrincipalClass "PrincipalName"}*47* </pre>48*49* For example, the following permission grants access to the50* com.sun.PrivateCredential owned by Subjects which have51* a com.sun.Principal with the name, "duke". Note that although52* this example, as well as all the examples below, do not contain53* Codebase, SignedBy, or Principal information in the grant statement54* (for simplicity reasons), actual policy configurations should55* specify that information when appropriate.56*57* <pre>58*59* grant {60* permission javax.security.auth.PrivateCredentialPermission61* "com.sun.PrivateCredential com.sun.Principal \"duke\"",62* "read";63* };64* </pre>65*66* If CredentialClass is "*", then access is granted to67* all private Credentials belonging to the specified68* {@code Subject}.69* If "PrincipalName" is "*", then access is granted to the70* specified Credential owned by any {@code Subject} that has the71* specified {@code Principal} (the actual PrincipalName doesn't matter).72* For example, the following grants access to the73* a.b.Credential owned by any {@code Subject} that has74* an a.b.Principal.75*76* <pre>77* grant {78* permission javax.security.auth.PrivateCredentialPermission79* "a.b.Credential a.b.Principal "*"",80* "read";81* };82* </pre>83*84* If both the PrincipalClass and "PrincipalName" are "*",85* then access is granted to the specified Credential owned by86* any {@code Subject}.87*88* <p> In addition, the PrincipalClass/PrincipalName pairing may be repeated:89*90* <pre>91* grant {92* permission javax.security.auth.PrivateCredentialPermission93* "a.b.Credential a.b.Principal "duke" c.d.Principal "dukette"",94* "read";95* };96* </pre>97*98* The above grants access to the private Credential, "a.b.Credential",99* belonging to a {@code Subject} with at least two associated Principals:100* "a.b.Principal" with the name, "duke", and "c.d.Principal", with the name,101* "dukette".102*103* @since 1.4104*/105public final class PrivateCredentialPermission extends Permission {106107@java.io.Serial108private static final long serialVersionUID = 5284372143517237068L;109110private static final CredOwner[] EMPTY_PRINCIPALS = new CredOwner[0];111112/**113* @serial114*/115private String credentialClass;116117/**118* @serial The Principals associated with this permission.119* The set contains elements of type,120* {@code PrivateCredentialPermission.CredOwner}.121*/122@SuppressWarnings("serial") // Not statically typed as Serializable123private Set<Principal> principals; // ignored - kept around for compatibility124private transient CredOwner[] credOwners;125126/**127* @serial128*/129private boolean testing = false;130131/**132* Create a new {@code PrivateCredentialPermission}133* with the specified {@code credentialClass} and Principals.134*/135PrivateCredentialPermission(String credentialClass,136Set<Principal> principals) {137138super(credentialClass);139this.credentialClass = credentialClass;140141synchronized(principals) {142if (principals.size() == 0) {143this.credOwners = EMPTY_PRINCIPALS;144} else {145this.credOwners = new CredOwner[principals.size()];146int index = 0;147Iterator<Principal> i = principals.iterator();148while (i.hasNext()) {149Principal p = i.next();150this.credOwners[index++] = new CredOwner151(p.getClass().getName(),152p.getName());153}154}155}156}157158/**159* Creates a new {@code PrivateCredentialPermission}160* with the specified {@code name}. The {@code name}161* specifies both a Credential class and a {@code Principal} Set.162*163* @param name the name specifying the Credential class and164* {@code Principal} Set.165*166* @param actions the actions specifying that the Credential can be read.167*168* @throws IllegalArgumentException if {@code name} does not conform169* to the correct syntax or if {@code actions} is not "read".170*/171public PrivateCredentialPermission(String name, String actions) {172super(name);173174if (!"read".equalsIgnoreCase(actions))175throw new IllegalArgumentException176(ResourcesMgr.getString("actions.can.only.be.read."));177init(name);178}179180/**181* Returns the Class name of the Credential associated with this182* {@code PrivateCredentialPermission}.183*184* @return the Class name of the Credential associated with this185* {@code PrivateCredentialPermission}.186*/187public String getCredentialClass() {188return credentialClass;189}190191/**192* Returns the {@code Principal} classes and names193* associated with this {@code PrivateCredentialPermission}.194* The information is returned as a two-dimensional array (array[x][y]).195* The 'x' value corresponds to the number of {@code Principal}196* class and name pairs. When (y==0), it corresponds to197* the {@code Principal} class value, and when (y==1),198* it corresponds to the {@code Principal} name value.199* For example, array[0][0] corresponds to the class name of200* the first {@code Principal} in the array. array[0][1]201* corresponds to the {@code Principal} name of the202* first {@code Principal} in the array.203*204* @return the {@code Principal} class and names associated205* with this {@code PrivateCredentialPermission}.206*/207public String[][] getPrincipals() {208209if (credOwners == null || credOwners.length == 0) {210return new String[0][0];211}212213String[][] pArray = new String[credOwners.length][2];214for (int i = 0; i < credOwners.length; i++) {215pArray[i][0] = credOwners[i].principalClass;216pArray[i][1] = credOwners[i].principalName;217}218return pArray;219}220221/**222* Checks if this {@code PrivateCredentialPermission} implies223* the specified {@code Permission}.224*225* <p>226*227* This method returns true if:228* <ul>229* <li> {@code p} is an instanceof PrivateCredentialPermission and230* <li> the target name for {@code p} is implied by this object's231* target name. For example:232* <pre>233* [* P1 "duke"] implies [a.b.Credential P1 "duke"].234* [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"].235* [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"].236* </pre>237* </ul>238*239* @param p the {@code Permission} to check against.240*241* @return true if this {@code PrivateCredentialPermission} implies242* the specified {@code Permission}, false if not.243*/244public boolean implies(Permission p) {245246if (p == null || !(p instanceof PrivateCredentialPermission))247return false;248249PrivateCredentialPermission that = (PrivateCredentialPermission)p;250251if (!impliesCredentialClass(credentialClass, that.credentialClass))252return false;253254return impliesPrincipalSet(credOwners, that.credOwners);255}256257/**258* Checks two {@code PrivateCredentialPermission} objects for259* equality. Checks that {@code obj} is a260* {@code PrivateCredentialPermission},261* and has the same credential class as this object,262* as well as the same Principals as this object.263* The order of the Principals in the respective Permission's264* target names is not relevant.265*266* @param obj the object we are testing for equality with this object.267*268* @return true if obj is a {@code PrivateCredentialPermission},269* has the same credential class as this object,270* and has the same Principals as this object.271*/272public boolean equals(Object obj) {273if (obj == this)274return true;275276if (! (obj instanceof PrivateCredentialPermission))277return false;278279PrivateCredentialPermission that = (PrivateCredentialPermission)obj;280281return (this.implies(that) && that.implies(this));282}283284/**285* Returns the hash code value for this object.286*287* @return a hash code value for this object.288*/289public int hashCode() {290return this.credentialClass.hashCode();291}292293/**294* Returns the "canonical string representation" of the actions.295* This method always returns the String, "read".296*297* @return the actions (always returns "read").298*/299public String getActions() {300return "read";301}302303/**304* Return a homogeneous collection of PrivateCredentialPermissions305* in a {@code PermissionCollection}.306* No such {@code PermissionCollection} is defined,307* so this method always returns {@code null}.308*309* @return null in all cases.310*/311public PermissionCollection newPermissionCollection() {312return null;313}314315private void init(String name) {316317if (name == null || name.trim().isEmpty()) {318throw new IllegalArgumentException("invalid empty name");319}320321ArrayList<CredOwner> pList = new ArrayList<>();322StringTokenizer tokenizer = new StringTokenizer(name, " ", true);323String principalClass = null;324String principalName = null;325326if (testing)327System.out.println("whole name = " + name);328329// get the Credential Class330credentialClass = tokenizer.nextToken();331if (testing)332System.out.println("Credential Class = " + credentialClass);333334if (tokenizer.hasMoreTokens() == false) {335MessageFormat form = new MessageFormat(ResourcesMgr.getString336("permission.name.name.syntax.invalid."));337Object[] source = {name};338throw new IllegalArgumentException339(form.format(source) + ResourcesMgr.getString340("Credential.Class.not.followed.by.a.Principal.Class.and.Name"));341}342343while (tokenizer.hasMoreTokens()) {344345// skip delimiter346tokenizer.nextToken();347348// get the Principal Class349principalClass = tokenizer.nextToken();350if (testing)351System.out.println(" Principal Class = " + principalClass);352353if (tokenizer.hasMoreTokens() == false) {354MessageFormat form = new MessageFormat(ResourcesMgr.getString355("permission.name.name.syntax.invalid."));356Object[] source = {name};357throw new IllegalArgumentException358(form.format(source) + ResourcesMgr.getString359("Principal.Class.not.followed.by.a.Principal.Name"));360}361362// skip delimiter363tokenizer.nextToken();364365// get the Principal Name366principalName = tokenizer.nextToken();367368if (!principalName.startsWith("\"")) {369MessageFormat form = new MessageFormat(ResourcesMgr.getString370("permission.name.name.syntax.invalid."));371Object[] source = {name};372throw new IllegalArgumentException373(form.format(source) + ResourcesMgr.getString374("Principal.Name.must.be.surrounded.by.quotes"));375}376377if (!principalName.endsWith("\"")) {378379// we have a name with spaces in it --380// keep parsing until we find the end quote,381// and keep the spaces in the name382383while (tokenizer.hasMoreTokens()) {384principalName = principalName + tokenizer.nextToken();385if (principalName.endsWith("\""))386break;387}388389if (!principalName.endsWith("\"")) {390MessageFormat form = new MessageFormat391(ResourcesMgr.getString392("permission.name.name.syntax.invalid."));393Object[] source = {name};394throw new IllegalArgumentException395(form.format(source) + ResourcesMgr.getString396("Principal.Name.missing.end.quote"));397}398}399400if (testing)401System.out.println("\tprincipalName = '" + principalName + "'");402403principalName = principalName.substring404(1, principalName.length() - 1);405406if (principalClass.equals("*") &&407!principalName.equals("*")) {408throw new IllegalArgumentException(ResourcesMgr.getString409("PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value"));410}411412if (testing)413System.out.println("\tprincipalName = '" + principalName + "'");414415pList.add(new CredOwner(principalClass, principalName));416}417418this.credOwners = new CredOwner[pList.size()];419pList.toArray(this.credOwners);420}421422private boolean impliesCredentialClass(String thisC, String thatC) {423424// this should never happen425if (thisC == null || thatC == null)426return false;427428if (testing)429System.out.println("credential class comparison: " +430thisC + "/" + thatC);431432if (thisC.equals("*"))433return true;434435/**436* XXX let's not enable this for now --437* if people want it, we'll enable it later438*/439/*440if (thisC.endsWith("*")) {441String cClass = thisC.substring(0, thisC.length() - 2);442return thatC.startsWith(cClass);443}444*/445446return thisC.equals(thatC);447}448449private boolean impliesPrincipalSet(CredOwner[] thisP, CredOwner[] thatP) {450451// this should never happen452if (thisP == null || thatP == null)453return false;454455if (thatP.length == 0)456return true;457458if (thisP.length == 0)459return false;460461for (int i = 0; i < thisP.length; i++) {462boolean foundMatch = false;463for (int j = 0; j < thatP.length; j++) {464if (thisP[i].implies(thatP[j])) {465foundMatch = true;466break;467}468}469if (!foundMatch) {470return false;471}472}473return true;474}475476/**477* Reads this object from a stream (i.e., deserializes it)478*479* @param s the {@code ObjectInputStream} from which data is read480* @throws IOException if an I/O error occurs481* @throws ClassNotFoundException if a serialized class cannot be loaded482*/483@java.io.Serial484private void readObject(java.io.ObjectInputStream s) throws485IOException,486ClassNotFoundException {487488s.defaultReadObject();489490// perform new initialization from the permission name491492if (getName().indexOf(' ') == -1 && getName().indexOf('"') == -1) {493494// name only has a credential class specified495credentialClass = getName();496credOwners = EMPTY_PRINCIPALS;497498} else {499500// perform regular initialization501init(getName());502}503}504505/**506* @serial include507*/508static class CredOwner implements java.io.Serializable {509510@java.io.Serial511private static final long serialVersionUID = -5607449830436408266L;512513/**514* @serial515*/516String principalClass;517/**518* @serial519*/520String principalName;521522CredOwner(String principalClass, String principalName) {523this.principalClass = principalClass;524this.principalName = principalName;525}526527public boolean implies(Object obj) {528if (obj == null || !(obj instanceof CredOwner))529return false;530531CredOwner that = (CredOwner)obj;532533if (principalClass.equals("*") ||534principalClass.equals(that.principalClass)) {535536if (principalName.equals("*") ||537principalName.equals(that.principalName)) {538return true;539}540}541542/**543* XXX no code yet to support a.b.*544*/545546return false;547}548549public String toString() {550MessageFormat form = new MessageFormat(ResourcesMgr.getString551("CredOwner.Principal.Class.class.Principal.Name.name"));552Object[] source = {principalClass, principalName};553return (form.format(source));554}555}556}557558559