Summary
-------
Create new JAAS APIs to be replacements of deprecated `getSubject` and `doAs`.
Problem
-------
With JEP 411, we have deprecated most APIs related to the security manager. In JDK 17, we already deprecated `Subject::getSubject` because its argument is a deprecated class `AccessControlContext`. In JDK 18, we will deprecate the `Subject::doAs` methods because they are closely related to `getSubject()` (one setter one getter) and their specifications heavily depend on deprecated classes `AccessController` and `SubjectDomainCombiner`. However, these APIs are useful independent of the Security Manager as they provide a mechanism
to transport a Subject's credentials across API boundaries. Thus, we need to create new APIs that do not depend on the Security Manager APIs -- this plan was already documented in JEP 411.
Solution
--------
Deprecate `Subject::doAs` for removal and add new methods `Subject::current` and `Subject::callAs`. The new APIs do not have API dependencies on the deprecated Security Manager APIs and are designed to offer the same capabilities as the deprecated APIs.
Specification
-------------
package javax.security.auth;
public final class Subject implements java.io.Serializable {
/**
* Get the {@code Subject} associated with the provided
* {@code AccessControlContext}.
....
* @deprecated This method depends on {@link AccessControlContext}
* which, in conjunction with
* {@linkplain SecurityManager the Security Manager}, is deprecated
- * and subject to removal in a future release. However, obtaining a
- * Subject is useful independent of the Security Manager, so a
- * replacement for this method may be added in a future release.
+ * and subject to removal in a future release. However,
+ * obtaining a Subject is useful independent of the Security Manager.
+ * Thus, a replacement API named {@link #current()} has been added
+ * which can be used to obtain the current subject.
*/
@Deprecated(since="17", forRemoval=true)
public static Subject getSubject(final AccessControlContext acc);
+
+ /**
+ * 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>
+ * When a new thread is created, its current subject is the same as
+ * the one of its parent thread, and will not change even if
+ * its parent thread's current subject is changed to another value.
+ *
+ * @implNote
+ * By default, this method returns the same value as
+ * {@code Subject.getSubject(AccessController.getContext())}. This
+ * preserves compatibility with code that may still be calling {@code doAs}
+ * which installs the subject in an {@code AccessControlContext}. However,
+ * if the system property {@systemProperty jdk.security.auth.subject.useTL}
+ * is set to {@code true}, the subject is retrieved from an inheritable
+ * {@code ThreadLocal} object. This behavior is subject to
+ * change in a future version.
+ *
+ * @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();
+ /**
+ * Executes a {@code Callable} with {@code subject} as the
+ * current subject.
+ *
+ * @implNote
+ * By default, this method calls {@link #doAs(Subject, PrivilegedExceptionAction)
+ * Subject.doAs(subject, altAction)} which stores the subject in
+ * a new {@code AccessControlContext}, where {@code altAction.run()}
+ * is equivalent to {@code action.call()} and the exception thrown is
+ * modified to match the specification of this method. This preserves
+ * compatibility with code that may still be calling
+ * {@code getSubject(AccessControlContext)} which retrieves the subject
+ * from an {@code AccessControlContext}. However,
+ * if the system property {@code jdk.security.auth.subject.useTL}
+ * is set to {@code true}, the current subject will be stored in an inheritable
+ * {@code ThreadLocal} object. This behavior is subject to change in a
+ * future version.
+ *
+ * @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;
/**
* Perform work as a particular {@code Subject}.
....
+ *
+ * @deprecated This method depends on {@link AccessControlContext}
+ * which, in conjunction with
+ * {@linkplain SecurityManager the Security Manager}, is deprecated
+ * and subject to removal in a future release. However, performing
+ * work as a Subject is useful independent of the Security Manager.
+ * Thus, a replacement API named {@link #callAs} has been added
+ * which can be used to perform the same work.
*/
@SuppressWarnings("removal")
+ @Deprecated(since="18", forRemoval=true)
public static <T> T doAs(final Subject subject,
final java.security.PrivilegedAction<T> action);
/**
* Perform work as a particular {@code Subject}.
....
+ *
+ * @deprecated This method depends on {@link AccessControlContext}
+ * which, in conjunction with
+ * {@linkplain SecurityManager the Security Manager}, is deprecated
+ * and subject to removal in a future release. However, performing
+ * work as a Subject is useful independent of the Security Manager.
+ * Thus, a replacement API named {@link #callAs} has been added
+ * which can be used to perform the same work.
*/
@SuppressWarnings("removal")
+ @Deprecated(since="18", forRemoval=true)
public static <T> T doAs(final Subject subject,
final java.security.PrivilegedExceptionAction<T> action)
throws java.security.PrivilegedActionException;
}