/
githubmirror
/
panama-vector
Обзор
Документация
Войти
/
githubmirror
/
panama-vector
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/java.base/share/classes/javax/security/auth/Subject.java
1 322 строки
47 KB
Sergey Bylokhov
8374327: Update copyright year to 2025 for files in java.base added/updated by commits in 2025
31 дек 2025, 12:13
31 дек 2025, 12:13
36d2c27
Код
Авторство
О чём код?
/* * Copyright (c) 1998, 2025, 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 javax.security.auth; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectStreamField; import java.security.*; import java.text.MessageFormat; import java.util.*; import java.util.concurrent.Callable; import java.util.concurrent.CompletionException; import sun.security.util.ResourcesMgr; /** * <p> A {@code Subject} represents a grouping of related information * for a single entity, such as a person. * Such information includes the Subject's identities as well as * its security-related attributes * (passwords and cryptographic keys, for example). * * <p> Subjects may potentially have multiple identities. * Each identity is represented as a {@code Principal} * within the {@code Subject}. Principals simply bind names to a * {@code Subject}. For example, a {@code Subject} that happens * to be a person, Alice, might have two Principals: * one which binds "Alice Bar", the name on her driver license, * to the {@code Subject}, and another which binds, * "999-99-9999", the number on her student identification card, * to the {@code Subject}. Both Principals refer to the same * {@code Subject} even though each has a different name. * * <p> A {@code Subject} may also own security-related attributes, * which are referred to as credentials. * Sensitive credentials that require special protection, such as * private cryptographic keys, are stored within a private credential * {@code Set}. Credentials intended to be shared, such as * public key certificates or Kerberos server tickets are stored * within a public credential {@code Set}. * * <p> To retrieve all the Principals associated with a {@code Subject}, * invoke the {@code getPrincipals} method. To retrieve * all the public or private credentials belonging to a {@code Subject}, * invoke the {@code getPublicCredentials} method or * {@code getPrivateCredentials} method, respectively. * To modify the returned {@code Set} of Principals and credentials, * use the methods defined in the {@code Set} class. * For example: * <pre> * Subject subject; * Principal principal; * Object credential; * * // add a Principal and credential to the Subject * subject.getPrincipals().add(principal); * subject.getPublicCredentials().add(credential); * </pre> * * <p> This {@code Subject} class implements {@code Serializable}. * While the Principals associated with the {@code Subject} are serialized, * the credentials associated with the {@code Subject} are not. * Note that the {@code java.security.Principal} class * does not implement {@code Serializable}. Therefore, all concrete * {@code Principal} implementations associated with Subjects * must implement {@code Serializable}. * * <h2>Deprecated Methods and Replacements</h2> * * <p> The following methods in this class for user-based authorization * that are dependent on Security Manager APIs are deprecated for removal: * <ul> * <li>{@link #getSubject(AccessControlContext)} * <li>{@link #doAs(Subject, PrivilegedAction)} * <li>{@link #doAs(Subject, PrivilegedExceptionAction)} * <li>{@link #doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)} * <li>{@link #doAsPrivileged(Subject, PrivilegedExceptionAction, AccessControlContext)} * </ul> * Methods {@link #current()} and {@link #callAs(Subject, Callable)} * are replacements for these methods, where {@code current} is equivalent to * {@code getSubject(AccessController.getContext())} (as originally specified) * and {@code callAs} is similar to {@code doAs} except that the * input type and exceptions thrown are slightly different. * * <p> A {@code doAs} or {@code callAs} call * binds a {@code Subject} object to the period of execution of an action, * and the subject can be retrieved using the {@code current} method inside * the action. This subject can be inherited by child threads if they are * started and terminate within the execution of its parent thread using * structured concurrency. * * @since 1.4 * @see java.security.Principal * @see java.security.DomainCombiner */ public final class Subject implements java.io.Serializable { @java.io.Serial private static final long serialVersionUID = -8308522755600156056L; /** * A {@code Set} that provides a view of all of this * Subject's Principals * * @serial Each element in this set is a * {@code java.security.Principal}. * The set is a {@code Subject.SecureSet}. */ @SuppressWarnings("serial") // Not statically typed as Serializable Set<Principal> principals; /** * Sets that provide a view of all of this * Subject's Credentials */ transient Set<Object> pubCredentials; transient Set<Object> privCredentials; /** * Whether this Subject is read-only * * @serial */ private volatile boolean readOnly; private static final int PRINCIPAL_SET = 1; private static final int PUB_CREDENTIAL_SET = 2; private static final int PRIV_CREDENTIAL_SET = 3; private static final ProtectionDomain[] NULL_PD_ARRAY = new ProtectionDomain[0]; /** * Create an instance of a {@code Subject} * with an empty {@code Set} of Principals and empty * Sets of public and private credentials. * * <p> The newly constructed Sets check whether this {@code Subject} * has been set read-only before permitting subsequent modifications. * These Sets also prohibit null elements, and attempts to add, query, * or remove a null element will result in a {@code NullPointerException}. */ public Subject() { this.principals = Collections.synchronizedSet (new SecureSet<>(this, PRINCIPAL_SET)); this.pubCredentials = Collections.synchronizedSet (new SecureSet<>(this, PUB_CREDENTIAL_SET)); this.privCredentials = Collections.synchronizedSet (new SecureSet<>(this, PRIV_CREDENTIAL_SET)); } /** * Create an instance of a {@code Subject} with * Principals and credentials. * * <p> The Principals and credentials from the specified Sets * are copied into newly constructed Sets. * These newly created Sets check whether this {@code Subject} * has been set read-only before permitting subsequent modifications. * These Sets also prohibit null elements, and attempts to add, query, * or remove a null element will result in a {@code NullPointerException}. * * @param readOnly true if the {@code Subject} is to be read-only, * and false otherwise. * * @param principals the {@code Set} of Principals * to be associated with this {@code Subject}. * * @param pubCredentials the {@code Set} of public credentials * to be associated with this {@code Subject}. * * @param privCredentials the {@code Set} of private credentials * to be associated with this {@code Subject}. * * @throws NullPointerException if the specified * {@code principals}, {@code pubCredentials}, * or {@code privCredentials} are {@code null}, * or a null value exists within any of these three * Sets. */ public Subject(boolean readOnly, Set<? extends Principal> principals, Set<?> pubCredentials, Set<?> privCredentials) { LinkedList<Principal> principalList = collectionNullClean(principals); LinkedList<Object> pubCredsList = collectionNullClean(pubCredentials); LinkedList<Object> privCredsList = collectionNullClean(privCredentials); this.principals = Collections.synchronizedSet( new SecureSet<>(this, PRINCIPAL_SET, principalList)); this.pubCredentials = Collections.synchronizedSet( new SecureSet<>(this, PUB_CREDENTIAL_SET, pubCredsList)); this.privCredentials = Collections.synchronizedSet( new SecureSet<>(this, PRIV_CREDENTIAL_SET, privCredsList)); this.readOnly = readOnly; } /** * Set this {@code Subject} to be read-only. * * <p> Modifications (additions and removals) to this Subject's * {@code Principal} {@code Set} and * credential Sets will be disallowed. * The {@code destroy} operation on this Subject's credentials will * still be permitted. * * <p> Subsequent attempts to modify the Subject's {@code Principal} * and credential Sets will result in an * {@code IllegalStateException} being thrown. * Also, once a {@code Subject} is read-only, * it can not be reset to being writable again. */ public void setReadOnly() { this.readOnly = true; } /** * Query whether this {@code Subject} is read-only. * * @return true if this {@code Subject} is read-only, false otherwise. */ public boolean isReadOnly() { return this.readOnly; } /** * Throws {@code UnsupportedOperationException}. A replacement API * named {@link #current()} has been added which can be used to obtain * the current subject. * * @param acc ignored * * @return n/a * * @throws UnsupportedOperationException always * * @deprecated This method used to get the subject associated with the * provided {@link AccessControlContext}, which was only useful in * conjunction with {@linkplain SecurityManager the Security Manager}, * which is no longer supported. This method has been changed to * always throw {@code UnsupportedOperationException}. A replacement * API named {@link #current()} has been added which can be used to * obtain the current subject. There is no replacement for the * Security Manager. * * @see #current() */ @SuppressWarnings("removal") @Deprecated(since="17", forRemoval=true) public static Subject getSubject(final AccessControlContext acc) { throw new UnsupportedOperationException("getSubject is not supported"); } private static final ScopedValue<Subject> SCOPED_SUBJECT = ScopedValue.newInstance(); /** * Returns the current subject. * * <p> The current subject is installed by the {@link #callAs} method. * When {@code callAs(subject, action)} is called, {@code action} is * executed with {@code subject} as its current subject which can be * retrieved by this method. After {@code action} is finished, the current * subject is reset to its previous value. The current * subject is {@code null} before the first call of {@code callAs()}. * * <p> This method returns the * {@code Subject} bound to the period of the execution of the current * thread. * * @return the current subject, or {@code null} if a current subject is * not installed or the current subject is set to {@code null}. * @see #callAs(Subject, Callable) * @since 18 */ public static Subject current() { return SCOPED_SUBJECT.isBound() ? SCOPED_SUBJECT.get() : null; } /** * Executes a {@code Callable} with {@code subject} as the * current subject. * * <p> This method launches {@code action} and binds {@code subject} to the * period of its execution. * * @param subject the {@code Subject} that the specified {@code action} * will run as. This parameter may be {@code null}. * @param action the code to be run with {@code subject} as its current * subject. Must not be {@code null}. * @param <T> the type of value returned by the {@code call} method * of {@code action} * @return the value returned by the {@code call} method of {@code action} * @throws NullPointerException if {@code action} is {@code null} * @throws CompletionException if {@code action.call()} throws an exception. * The cause of the {@code CompletionException} is set to the exception * thrown by {@code action.call()}. * @see #current() * @since 18 */ public static <T> T callAs(final Subject subject, final Callable<T> action) throws CompletionException { Objects.requireNonNull(action); try { return ScopedValue.where(SCOPED_SUBJECT, subject).call(action::call); } catch (Exception e) { throw new CompletionException(e); } } /** * Perform work as a particular {@code Subject}. * * <p> This method launches {@code action} and binds {@code subject} to the * period of its execution. * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. * * @param <T> the type of the value returned by the PrivilegedAction's * {@code run} method. * * @param action the code to be run as the specified * {@code Subject}. * * @return the value returned by the PrivilegedAction's * {@code run} method. * * @throws NullPointerException if the {@code PrivilegedAction} * is {@code null}. * * @deprecated This method originally performed the specified * {@code PrivilegedAction} with privileges enabled. Running the * action with privileges enabled was only useful in conjunction * with {@linkplain SecurityManager the Security Manager}, which is * no longer supported. This method has been changed to launch the * action as is and bind the subject to the period of its execution. * A replacement API named {@link #callAs} has been added which can * be used to perform the same work. There is no replacement for the * Security Manager. * * @see #callAs(Subject, Callable) */ @Deprecated(since="18", forRemoval=true) public static <T> T doAs(final Subject subject, final java.security.PrivilegedAction<T> action) { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); try { return callAs(subject, action::run); } catch (CompletionException ce) { var cause = ce.getCause(); if (cause instanceof RuntimeException re) { throw re; } else if (cause instanceof Error er) { throw er; } else { throw new AssertionError(ce); } } } /** * Perform work as a particular {@code Subject}. * * <p> This method launches {@code action} and binds {@code subject} to the * period of its execution. * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. * * @param <T> the type of the value returned by the * PrivilegedExceptionAction's {@code run} method. * * @param action the code to be run as the specified * {@code Subject}. * * @return the value returned by the * PrivilegedExceptionAction's {@code run} method. * * @throws PrivilegedActionException if the * {@code PrivilegedExceptionAction.run} * method throws a checked exception. * * @throws NullPointerException if the specified * {@code PrivilegedExceptionAction} is * {@code null}. * * @deprecated This method originally performed the specified * {@code PrivilegedExceptionAction} with privileges enabled. * Running the action with privileges enabled was only useful in * conjunction with {@linkplain SecurityManager the Security Manager}, * which is no longer supported. This method has been changed to * launch the action as is and bind the subject to the period of its * execution. A replacement API named {@link #callAs} has been added * which can be used to perform the same work. There is no * replacement for the Security Manager. * * @see #callAs(Subject, Callable) */ @Deprecated(since="18", forRemoval=true) public static <T> T doAs(final Subject subject, final java.security.PrivilegedExceptionAction<T> action) throws java.security.PrivilegedActionException { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); try { return callAs(subject, action::run); } catch (CompletionException ce) { var cause = ce.getCause(); if (cause instanceof RuntimeException re) { throw re; } else if (cause instanceof Error er) { throw er; } else if (cause instanceof Exception e) { throw new PrivilegedActionException(e); } else { throw new PrivilegedActionException(ce); } } } /** * Perform work as a particular {@code Subject}. * * <p> This method launches {@code action} and binds {@code subject} to * the period of its execution. * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. * * @param <T> the type of the value returned by the PrivilegedAction's * {@code run} method. * * @param action the code to be run as the specified * {@code Subject}. * * @param acc ignored * * @return the value returned by the PrivilegedAction's * {@code run} method. * * @throws NullPointerException if the {@code PrivilegedAction} * is {@code null}. * * @deprecated This method originally performed the specified * {@code PrivilegedAction} with privileges enabled and restricted * by the specified {@code AccessControlContext}. Running the * action with privileges enabled was only useful in conjunction * with {@linkplain SecurityManager the Security Manager}, which is * no longer supported. This method has been changed to ignore the * {@code AccessControlContext} and launch the action as is and bind * the subject to the period of its execution. A replacement API * named {@link #callAs} has been added which can be used to perform * the same work. There is no replacement for the Security Manager. * * @see #callAs(Subject, Callable) */ @SuppressWarnings("removal") @Deprecated(since="17", forRemoval=true) public static <T> T doAsPrivileged(final Subject subject, final java.security.PrivilegedAction<T> action, final java.security.AccessControlContext acc) { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); try { return callAs(subject, action::run); } catch (CompletionException ce) { var cause = ce.getCause(); if (cause instanceof RuntimeException re) { throw re; } else if (cause instanceof Error er) { throw er; } else { throw new AssertionError(ce); } } } /** * Perform work as a particular {@code Subject}. * * <p> This method launches {@code action} and binds {@code subject} to * the period of its execution. * * @param subject the {@code Subject} that the specified * {@code action} will run as. This parameter * may be {@code null}. * * @param <T> the type of the value returned by the * PrivilegedExceptionAction's {@code run} method. * * @param action the code to be run as the specified * {@code Subject}. * * @param acc ignored * * @return the value returned by the * PrivilegedExceptionAction's {@code run} method. * * @throws PrivilegedActionException if the * {@code PrivilegedExceptionAction.run} * method throws a checked exception. * * @throws NullPointerException if the specified * {@code PrivilegedExceptionAction} is * {@code null}. * * @deprecated This method originally performed the specified * {@code PrivilegedExceptionAction} with privileges enabled and * restricted by the specified {@code AccessControlContext}. Running * the action with privileges enabled was only useful in conjunction * with {@linkplain SecurityManager the Security Manager}, which is * no longer supported. This method has been changed to ignore the * {@code AccessControlContext} and launch the action as is and bind * the subject to the period of its execution. A replacement API * named {@link #callAs} has been added which can be used to perform * the same work. There is no replacement for the Security Manager. * * @see #callAs(Subject, Callable) */ @SuppressWarnings("removal") @Deprecated(since="17", forRemoval=true) public static <T> T doAsPrivileged(final Subject subject, final java.security.PrivilegedExceptionAction<T> action, final java.security.AccessControlContext acc) throws java.security.PrivilegedActionException { Objects.requireNonNull(action, ResourcesMgr.getString("invalid.null.action.provided")); try { return callAs(subject, action::run); } catch (CompletionException ce) { var cause = ce.getCause(); if (cause instanceof RuntimeException re) { throw re; } else if (cause instanceof Error er) { throw er; } else if (cause instanceof Exception e) { throw new PrivilegedActionException(e); } else { throw new PrivilegedActionException(ce); } } } /** * Return the {@code Set} of Principals associated with this * {@code Subject}. Each {@code Principal} represents * an identity for this {@code Subject}. * * <p> The returned {@code Set} is backed by this Subject's * internal {@code Principal} {@code Set}. Any modification * to the returned {@code Set} affects the internal * {@code Principal} {@code Set} as well. * * @return the {@code Set} of Principals associated with this * {@code Subject}. */ public Set<Principal> getPrincipals() { // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return principals; } /** * Return a {@code Set} of Principals associated with this * {@code Subject} that are instances or subclasses of the specified * {@code Class}. * * <p> The returned {@code Set} is not backed by this Subject's * internal {@code Principal} {@code Set}. A new * {@code Set} is created and returned for each method invocation. * Modifications to the returned {@code Set} * will not affect the internal {@code Principal} {@code Set}. * * @param <T> the type of the class modeled by {@code c} * * @param c the returned {@code Set} of Principals will all be * instances of this class. * * @return a {@code Set} of Principals that are instances of the * specified {@code Class}. * * @throws NullPointerException if the specified {@code Class} * is {@code null}. */ public <T extends Principal> Set<T> getPrincipals(Class<T> c) { Objects.requireNonNull(c, ResourcesMgr.getString("invalid.null.Class.provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet<>(PRINCIPAL_SET, c); } /** * Return the {@code Set} of public credentials held by this * {@code Subject}. * * <p> The returned {@code Set} is backed by this Subject's * internal public Credential {@code Set}. Any modification * to the returned {@code Set} affects the internal public * Credential {@code Set} as well. * * @return a {@code Set} of public credentials held by this * {@code Subject}. */ public Set<Object> getPublicCredentials() { // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return pubCredentials; } /** * Return the {@code Set} of private credentials held by this * {@code Subject}. * * <p> The returned {@code Set} is backed by this Subject's * internal private Credential {@code Set}. Any modification * to the returned {@code Set} affects the internal private * Credential {@code Set} as well. * * @return a {@code Set} of private credentials held by this * {@code Subject}. */ public Set<Object> getPrivateCredentials() { // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return privCredentials; } /** * Return a {@code Set} of public credentials associated with this * {@code Subject} that are instances or subclasses of the specified * {@code Class}. * * <p> The returned {@code Set} is not backed by this Subject's * internal public Credential {@code Set}. A new * {@code Set} is created and returned for each method invocation. * Modifications to the returned {@code Set} * will not affect the internal public Credential {@code Set}. * * @param <T> the type of the class modeled by {@code c} * * @param c the returned {@code Set} of public credentials will all be * instances of this class. * * @return a {@code Set} of public credentials that are instances * of the specified {@code Class}. * * @throws NullPointerException if the specified {@code Class} * is {@code null}. */ public <T> Set<T> getPublicCredentials(Class<T> c) { Objects.requireNonNull(c, ResourcesMgr.getString("invalid.null.Class.provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet<>(PUB_CREDENTIAL_SET, c); } /** * Return a {@code Set} of private credentials associated with this * {@code Subject} that are instances or subclasses of the specified * {@code Class}. * * <p> The returned {@code Set} is not backed by this Subject's * internal private Credential {@code Set}. A new * {@code Set} is created and returned for each method invocation. * Modifications to the returned {@code Set} * will not affect the internal private Credential {@code Set}. * * @param <T> the type of the class modeled by {@code c} * * @param c the returned {@code Set} of private credentials will all be * instances of this class. * * @return a {@code Set} of private credentials that are instances * of the specified {@code Class}. * * @throws NullPointerException if the specified {@code Class} * is {@code null}. */ public <T> Set<T> getPrivateCredentials(Class<T> c) { Objects.requireNonNull(c, ResourcesMgr.getString("invalid.null.Class.provided")); // always return an empty Set instead of null // so LoginModules can add to the Set if necessary return new ClassSet<>(PRIV_CREDENTIAL_SET, c); } /** * Compares the specified Object with this {@code Subject} * for equality. Returns true if the given object is also a Subject * and the two {@code Subject} instances are equivalent. * More formally, two {@code Subject} instances are * equal if their {@code Principal} and {@code Credential} * Sets are equal. * * @param o Object to be compared for equality with this * {@code Subject}. * * @return true if the specified Object is equal to this * {@code Subject}. */ @Override public boolean equals(Object o) { if (this == o) { return true; } if (o instanceof final Subject that) { // check the principal and credential sets Set<Principal> thatPrincipals; synchronized(that.principals) { // avoid deadlock from dual locks thatPrincipals = new HashSet<>(that.principals); } if (!principals.equals(thatPrincipals)) { return false; } Set<Object> thatPubCredentials; synchronized(that.pubCredentials) { // avoid deadlock from dual locks thatPubCredentials = new HashSet<>(that.pubCredentials); } if (!pubCredentials.equals(thatPubCredentials)) { return false; } Set<Object> thatPrivCredentials; synchronized(that.privCredentials) { // avoid deadlock from dual locks thatPrivCredentials = new HashSet<>(that.privCredentials); } return privCredentials.equals(thatPrivCredentials); } return false; } /** * Return the String representation of this {@code Subject}. * * @return the String representation of this {@code Subject}. */ @Override public String toString() { String s = ResourcesMgr.getString("Subject."); String suffix = ""; synchronized(principals) { for (Principal p : principals) { suffix = suffix + ResourcesMgr.getString(".Principal.") + p.toString() + ResourcesMgr.getString("NEWLINE"); } } synchronized(pubCredentials) { for (Object o : pubCredentials) { suffix = suffix + ResourcesMgr.getString(".Public.Credential.") + o.toString() + ResourcesMgr.getString("NEWLINE"); } } synchronized(privCredentials) { Iterator<Object> pI = privCredentials.iterator(); while (pI.hasNext()) { try { Object o = pI.next(); suffix += ResourcesMgr.getString (".Private.Credential.") + o.toString() + ResourcesMgr.getString("NEWLINE"); } catch (SecurityException se) { suffix += ResourcesMgr.getString (".Private.Credential.inaccessible."); break; } } } return s + suffix; } /** * {@return a hashcode for this {@code Subject}} */ @Override public int hashCode() { /* * The hashcode is derived exclusive or-ing the * hashcodes of this Subject's Principals and credentials. * * If a particular credential was destroyed * ({@code credential.hashCode()} throws an * {@code IllegalStateException}), * the hashcode for that credential is derived via: * {@code credential.getClass().toString().hashCode()}. */ int hashCode = 0; synchronized(principals) { for (Principal p : principals) { hashCode ^= p.hashCode(); } } synchronized(pubCredentials) { for (Object pubCredential : pubCredentials) { hashCode ^= getCredHashCode(pubCredential); } } return hashCode; } /** * get a credential's hashcode */ private int getCredHashCode(Object o) { try { return o.hashCode(); } catch (IllegalStateException ise) { return o.getClass().toString().hashCode(); } } /** * Writes this object out to a stream (i.e., serializes it). * * @param oos the {@code ObjectOutputStream} to which data is written * @throws IOException if an I/O error occurs */ @java.io.Serial private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { synchronized(principals) { oos.defaultWriteObject(); } } /** * Reads this object from a stream (i.e., deserializes it) * * @param s the {@code ObjectInputStream} from which data is read * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ @SuppressWarnings("unchecked") @java.io.Serial private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { ObjectInputStream.GetField gf = s.readFields(); readOnly = gf.get("readOnly", false); Set<Principal> inputPrincs = (Set<Principal>)gf.get("principals", null); Objects.requireNonNull(inputPrincs, ResourcesMgr.getString("invalid.null.input.s.")); // Rewrap the principals into a SecureSet try { LinkedList<Principal> principalList = collectionNullClean(inputPrincs); principals = Collections.synchronizedSet(new SecureSet<> (this, PRINCIPAL_SET, principalList)); } catch (NullPointerException npe) { // Sometimes people deserialize the principals set only. // Subject is not accessible, so just don't fail. principals = Collections.synchronizedSet (new SecureSet<>(this, PRINCIPAL_SET)); } // The Credential {@code Set} is not serialized, but we do not // want the default deserialization routine to set it to null. this.pubCredentials = Collections.synchronizedSet (new SecureSet<>(this, PUB_CREDENTIAL_SET)); this.privCredentials = Collections.synchronizedSet (new SecureSet<>(this, PRIV_CREDENTIAL_SET)); } /** * Tests for null-clean collections (both non-null reference and * no null elements) * * @param coll A {@code Collection} to be tested for null references * * @throws NullPointerException if the specified collection is either * {@code null} or contains a {@code null} element */ private static <E> LinkedList<E> collectionNullClean( Collection<? extends E> coll) { Objects.requireNonNull(coll, ResourcesMgr.getString("invalid.null.input.s.")); LinkedList<E> output = new LinkedList<>(); for (E e : coll) { output.add(Objects.requireNonNull(e, ResourcesMgr.getString("invalid.null.input.s."))); } return output; } /** * Prevent modifications unless caller has permission. * * @serial include */ private static class SecureSet<E> implements Set<E>, java.io.Serializable { @java.io.Serial private static final long serialVersionUID = 7911754171111800359L; /** * @serialField this$0 Subject The outer Subject instance. * @serialField elements LinkedList The elements in this set. */ @java.io.Serial private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("this$0", Subject.class), new ObjectStreamField("elements", LinkedList.class), new ObjectStreamField("which", int.class) }; Subject subject; LinkedList<E> elements; /** * @serial An integer identifying the type of objects contained * in this set. If {@code which == 1}, * this is a Principal set and all the elements are * of type {@code java.security.Principal}. * If {@code which == 2}, this is a public credential * set and all the elements are of type {@code Object}. * If {@code which == 3}, this is a private credential * set and all the elements are of type {@code Object}. */ private int which; SecureSet(Subject subject, int which) { this.subject = subject; this.which = which; this.elements = new LinkedList<>(); } SecureSet(Subject subject, int which, LinkedList<E> list) { this.subject = subject; this.which = which; this.elements = list; } public int size() { return elements.size(); } public Iterator<E> iterator() { final LinkedList<E> list = elements; return new Iterator<>() { final ListIterator<E> i = list.listIterator(0); public boolean hasNext() { return i.hasNext(); } public E next() { return i.next(); } public void remove() { if (subject.isReadOnly()) { throw new IllegalStateException(ResourcesMgr.getString ("Subject.is.read.only")); } i.remove(); } }; } public boolean add(E o) { Objects.requireNonNull(o, ResourcesMgr.getString("invalid.null.input.s.")); if (subject.isReadOnly()) { throw new IllegalStateException (ResourcesMgr.getString("Subject.is.read.only")); } switch (which) { case Subject.PRINCIPAL_SET: if (!(o instanceof Principal)) { throw new SecurityException(ResourcesMgr.getString ("attempting.to.add.an.object.which.is.not.an.instance.of.java.security.Principal.to.a.Subject.s.Principal.Set")); } break; default: // ok to add Objects of any kind to credential sets break; } // check for duplicates if (!elements.contains(o)) return elements.add(o); else { return false; } } public boolean remove(Object o) { Objects.requireNonNull(o, ResourcesMgr.getString("invalid.null.input.s.")); final Iterator<E> e = iterator(); while (e.hasNext()) { E next = e.next(); if (next.equals(o)) { e.remove(); return true; } } return false; } public boolean contains(Object o) { Objects.requireNonNull(o, ResourcesMgr.getString("invalid.null.input.s.")); final Iterator<E> e = iterator(); while (e.hasNext()) { E next = e.next(); if (next.equals(o)) { return true; } } return false; } public boolean addAll(Collection<? extends E> c) { boolean result = false; c = collectionNullClean(c); for (E item : c) { result |= this.add(item); } return result; } public boolean removeAll(Collection<?> c) { c = collectionNullClean(c); boolean modified = false; final Iterator<E> e = iterator(); while (e.hasNext()) { E next = e.next(); for (Object o : c) { if (next.equals(o)) { e.remove(); modified = true; break; } } } return modified; } public boolean containsAll(Collection<?> c) { c = collectionNullClean(c); for (Object item : c) { if (!this.contains(item)) { return false; } } return true; } public boolean retainAll(Collection<?> c) { c = collectionNullClean(c); boolean modified = false; final Iterator<E> e = iterator(); while (e.hasNext()) { E next = e.next(); if (c.contains(next) == false) { e.remove(); modified = true; } } return modified; } public void clear() { final Iterator<E> e = iterator(); while (e.hasNext()) { E next = e.next(); e.remove(); } } public boolean isEmpty() { return elements.isEmpty(); } public Object[] toArray() { return elements.toArray(); } public <T> T[] toArray(T[] a) { return elements.toArray(a); } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Set)) { return false; } Collection<?> c = (Collection<?>) o; if (c.size() != size()) { return false; } try { return containsAll(c); } catch (ClassCastException | NullPointerException unused) { return false; } } @Override public int hashCode() { int h = 0; for (E obj : this) { h += Objects.hashCode(obj); } return h; } /** * Writes this object out to a stream (i.e., serializes it). * * @param oos the {@code ObjectOutputStream} to which data is written * @throws IOException if an I/O error occurs */ @java.io.Serial private void writeObject(java.io.ObjectOutputStream oos) throws java.io.IOException { ObjectOutputStream.PutField fields = oos.putFields(); fields.put("this$0", subject); fields.put("elements", elements); fields.put("which", which); oos.writeFields(); } /** * Restores the state of this object from the stream. * * @param ois the {@code ObjectInputStream} from which data is read * @throws IOException if an I/O error occurs * @throws ClassNotFoundException if a serialized class cannot be loaded */ @SuppressWarnings("unchecked") @java.io.Serial private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ObjectInputStream.GetField fields = ois.readFields(); subject = (Subject) fields.get("this$0", null); which = fields.get("which", 0); LinkedList<E> tmp = (LinkedList<E>) fields.get("elements", null); elements = Subject.collectionNullClean(tmp); } } /** * This class implements a {@code Set} which returns only * members that are an instance of a specified Class. */ private class ClassSet<T> extends AbstractSet<T> { private final int which; private final Class<T> c; private final Set<T> set; ClassSet(int which, Class<T> c) { this.which = which; this.c = c; set = new HashSet<>(); switch (which) { case Subject.PRINCIPAL_SET: synchronized(principals) { populateSet(); } break; case Subject.PUB_CREDENTIAL_SET: synchronized(pubCredentials) { populateSet(); } break; default: synchronized(privCredentials) { populateSet(); } break; } } @SuppressWarnings("unchecked") private void populateSet() { final Iterator<?> iterator; switch(which) { case Subject.PRINCIPAL_SET: iterator = Subject.this.principals.iterator(); break; case Subject.PUB_CREDENTIAL_SET: iterator = Subject.this.pubCredentials.iterator(); break; default: iterator = Subject.this.privCredentials.iterator(); break; } while (iterator.hasNext()) { Object next = iterator.next(); if (c.isAssignableFrom(next.getClass())) { set.add((T)next); } } } @Override public int size() { return set.size(); } @Override public Iterator<T> iterator() { return set.iterator(); } @Override public boolean add(T o) { if (!c.isAssignableFrom(o.getClass())) { MessageFormat form = new MessageFormat(ResourcesMgr.getString ("attempting.to.add.an.object.which.is.not.an.instance.of.class")); Object[] source = {c.toString()}; throw new SecurityException(form.format(source)); } return set.add(o); } } }