Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8327134

Alternate implementation of user-based authorization Subject APIs that doesn't depend on Security Manager APIs

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 23
    • security-libs
    • None
    • behavioral
    • medium
    • Hide
      If a Security Manager is allowed, there is no behavior change.

      If a Security Manager is not allowed, no permission granting and access control is involved and the only information associated with an `AccessControlContext` was the subject. Therefore, it is safe to store the subject in a scoped value and it's equally retrievable.

      However, some use cases behave differently. The following code changes are needed for both applications and libraries if they need to continue working when a Security Manager is not allowed:

      1. User code that calls `Subject::getSubject` on the current `AccessControlContext` (i.e. freshly returned by `AccessController::getContext`) needs to be modified to use the `current` method. If `Subject::getSubject` is called on a stored `AccessControlContext`, then the code needs to be modified such that instead of storing an `AccessControlContext` object they pass the `Subject` object directly into `callAs`. This has been demonstrated in a newly added test.

      2. Instead of using the standard `Subject::doAs` method to launch an action, user code may explicitly create a new `SubjectDomainCombiner`, bind it into a new `AccessControlContext`, and call `AccessController::doPrivileged` to launch an action. User should modify the code to use `doAs` (or `callAs`). Note that a binary corpus search of maven showed only 4 artifacts using `SubjectDomainCombiner`.

      3. If user code depends on the automatic inheritance of subject in a newly created platform thread, they should either modify the code to using structured concurrency or explicitly pass the current subject into the new thread.

      The workaround for each of these cases will be documented in the release note.

      If for some reason, user cannot modify their code in time, they can add
      `-Djava.security.manager=allow` on the command line as a workaround. This allows the old implementation to be used even if a Security Manager is never set. Please note that in a future version when the Security Manager is completely removed this workaround would fail.

      Show
      If a Security Manager is allowed, there is no behavior change. If a Security Manager is not allowed, no permission granting and access control is involved and the only information associated with an `AccessControlContext` was the subject. Therefore, it is safe to store the subject in a scoped value and it's equally retrievable. However, some use cases behave differently. The following code changes are needed for both applications and libraries if they need to continue working when a Security Manager is not allowed: 1. User code that calls `Subject::getSubject` on the current `AccessControlContext` (i.e. freshly returned by `AccessController::getContext`) needs to be modified to use the `current` method. If `Subject::getSubject` is called on a stored `AccessControlContext`, then the code needs to be modified such that instead of storing an `AccessControlContext` object they pass the `Subject` object directly into `callAs`. This has been demonstrated in a newly added test. 2. Instead of using the standard `Subject::doAs` method to launch an action, user code may explicitly create a new `SubjectDomainCombiner`, bind it into a new `AccessControlContext`, and call `AccessController::doPrivileged` to launch an action. User should modify the code to use `doAs` (or `callAs`). Note that a binary corpus search of maven showed only 4 artifacts using `SubjectDomainCombiner`. 3. If user code depends on the automatic inheritance of subject in a newly created platform thread, they should either modify the code to using structured concurrency or explicitly pass the current subject into the new thread. The workaround for each of these cases will be documented in the release note. If for some reason, user cannot modify their code in time, they can add `-Djava.security.manager=allow` on the command line as a workaround. This allows the old implementation to be used even if a Security Manager is never set. Please note that in a future version when the Security Manager is completely removed this workaround would fail.
    • Java API
    • SE

      Summary

      Provide a different implementation of the Subject API when the Security Manager is not allowed.

      Problem

      The Security Manager API was deprecated for removal in JDK 17 and JAAS still stores a subject in an AccessControlContext object. AccessControlContext is one of the Security Manager APIs that is deprecated for removal.

      In JDK 18, we added replacement APIs (Subject.callAs and Subject.current) for Subject.doAs and Subject.getSubject which had no API dependencies on the deprecated Security Manager APIs. However, the implementations of these replacement APIs are currently wrappers which call the deprecated APIs. It was always our intention that this was a temporary measure and that we needed to provide an alternate mechanism before we further degraded the Security Manager APIs.

      Thus, in order for these JAAS APIs to continue working after the Security Manager implementation is removed and the deprecated APIs are degraded, we need to provide a different mechanism to pass the subject to the action. However, it is also important to preserve compatibility with the deprecated APIs until they are degraded in a future release.

      Solution

      We are proposing a new alternate Subject implementation that will store the subject in a scoped value object when Subject::callAs is called. When the action, or callable, is executed, the subject can be subsequently retrieved using the Subject::current method.

      This new implementation will only be used when a Security Manager is not allowed, which means it is not set and not allowed to be set dynamically. Applications using JAAS with a Security Manager can continue to use the existing Subject APIs that use AccessControlContext and will experience no compatibility issues. An application using JAAS when a Security Manager is allowed, which means it is either already set or allowed to be set dynamically, will by default use the new implementation but can revert to using the existing APIs that use AccessControlContext by adding
      -Djava.security.manager=allow to the command line.

      When a Security Manager is not allowed the existing Subject::doAs methods will also behave similarly to callAs and store the subject in a scoped value object. This ensures that an application calling doAs can interoperate correctly with a library calling current. However, the existing Subject::getSubject method will throw an UnsupportedOperationException since it takes an AccessControlContext argument.

      This solution provides a good balance between compatibility and forward migration. It allows applications to use the new implementation but also allows them to revert to the previous implementation if they encounter some compatibility issues or need more time to migrate to the replacement APIs. It also gives them an advance warning that some of the deprecated APIs (Subject::getSubject specifically) will no longer be functional without Security Manager support.

      Specification

      Changes are made to the Subject class and some of its methods. The methods work differently depending on if a Security Manager is allowed.

      The specification does not directly reference the ScopedValue type (still in preview) but the description of its thread inheritance behavior matches the feature of ScopedValue.

       /**
        * ....
      + * <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 mostly equivalent to {@code getSubject(AccessController.getContext())}
      + * and {@code callAs} is similar to {@code doAs} except that the
      + * input type and exceptions thrown are slightly different.
      + *
      + * <p><b><a id="sm-allowed">These methods behave differently depending on
      + * whether a security manager is allowed or disallowed</a></b>:
      + * <ul>
      + * <li>If a security manager is allowed, which means it is either already set
      + * or allowed to be set dynamically, a {@code Subject} object is associated
      + * with an {@code AccessControlContext} through a {@code doAs} or
      + * {@code callAs} call, and the subject can then be retrieved using the
      + * {@code getSubject(AccessControlContext)} or {@code current} method.
      + * <li>If a security manager is not allowed, which means it
      + * {@linkplain System#setSecurityManager is not set and not allowed to be set
      + * dynamically}, 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.
      + * </ul>
      + *
        * ...
        */
       public final class Subject implements java.io.Serializable {
      
           /**
            * Get the {@code Subject} associated with the provided
      -     * {@code AccessControlContext}.
      +     * {@code AccessControlContext}. This method is intended to be used with
      +     * a security manager. It throws an {@code UnsupportedOperationException}
      +     * if a security manager is not allowed.
            *
            * ...
            *
      +     * @throws UnsupportedOperationException if a security manager is
      +     *          not allowed
      +     *
            * ...
            */
      
           public static Subject getSubject(final AccessControlContext acc) ...
      
           /**
            * Returns the current subject.
            * ...
            *
      -     * @implNote
      -     * 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}. This
      -     * behavior is subject to change in a future version.
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>, this
      +     * method is equivalent to calling {@link #getSubject} with the current
      +     * {@code AccessControlContext}.
      +     *
      +     * <p> If a security manager is not allowed, this method returns the
      +     * {@code Subject} bound to the period of the execution of the current
      +     * thread.
            *
            * ...
            */
           public static Subject current() ...
      
           /**
            * Executes a {@code Callable} with {@code subject} as the
            * current subject.
            *
      -     * @implNote
      -     * 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}. This behavior is subject
      -     * to change in a future version.
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>,
      +     * this method first retrieves the current Thread's
      +     * {@code AccessControlContext} via
      +     * {@code AccessController.getContext},
      +     * and then instantiates a new {@code AccessControlContext}
      +     * using the retrieved context along with a new
      +     * {@code SubjectDomainCombiner} (constructed using
      +     * the provided {@code Subject}).
      +     * Finally, this method invokes {@code AccessController.doPrivileged},
      +     * passing it the provided {@code PrivilegedAction},
      +     * as well as the newly constructed {@code AccessControlContext}.
      +     *
      +     * <p> If a security manager is not allowed,
      +     * this method launches {@code action} and binds {@code subject} to the
      +     * period of its execution.
            *...
            */
           public static <T> T callAs(final Subject subject,
                   final Callable<T> action) throws CompletionException ...
      
           /**
            * Perform work as a particular {@code Subject}.
            *
      -     * <p> This method first retrieves the current Thread's
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>,
      +     * this method first retrieves the current Thread's
            * {@code AccessControlContext} via
            * {@code AccessController.getContext},
            * ...
            * passing it the provided {@code PrivilegedAction},
            * as well as the newly constructed {@code AccessControlContext}.
            *
      +     * <p> If a security manager is not allowed,
      +     * this method launches {@code action} and binds {@code subject} to the
      +     * period of its execution.
      +     *
            *...
            */
           public static <T> T doAs(final Subject subject,
                              final java.security.PrivilegedAction<T> action) ...
      
           /**
            * Perform work as a particular {@code Subject}.
            *
      -     * <p> This method first retrieves the current Thread's
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>,
      +     * this method first retrieves the current Thread's
            * {@code AccessControlContext} via
            * {@code AccessController.getContext},
            * ...
            * passing it the provided {@code PrivilegedExceptionAction},
            * as well as the newly constructed {@code AccessControlContext}.
            *
      +     * <p> If a security manager is not allowed,
      +     * this method launches {@code action} and binds {@code subject} to the
      +     * period of its execution.
      +
            *...
            */
           public static <T> T doAs(final Subject subject,
                              final java.security.PrivilegedExceptionAction<T> action)
                              throws java.security.PrivilegedActionException ...
      
           /**
            * Perform privileged work as a particular {@code Subject}.
            *
      -     * <p> This method behaves exactly as {@code Subject.doAs},
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>,
      +     * this method behaves exactly as {@code Subject.doAs},
            * except that instead of retrieving the current Thread's
            * {@code AccessControlContext}, it uses the provided
            * ...
            * this method instantiates a new {@code AccessControlContext}
            * with an empty collection of ProtectionDomains.
            *
      +     * <p> If a security manager is not allowed,
      +     * this method ignores the {@code acc} argument, launches {@code action},
      +     * and binds {@code subject} to the period of its execution.
      +     *
            * ...
            */
           public static <T> T doAsPrivileged(final Subject subject,
                              final java.security.PrivilegedAction<T> action,
                              final java.security.AccessControlContext acc) ...
      
           /**
            * Perform privileged work as a particular {@code Subject}.
            *
      -     * <p> This method behaves exactly as {@code Subject.doAs},
      +     * <p> If a security manager is <a href=#sm-allowed>allowed</a>,
      +     * this method behaves exactly as {@code Subject.doAs},
            * except that instead of retrieving the current Thread's
            * {@code AccessControlContext}, it uses the provided
            * ...
            * this method instantiates a new {@code AccessControlContext}
            * with an empty collection of ProtectionDomains.
            *
      +     * <p> If a security manager is not allowed,
      +     * this method ignores the {@code acc} argument, launches {@code action},
      +     * and binds {@code subject} to the period of its execution.
      +     *
            * ...
            */
           public static <T> T doAsPrivileged(final Subject subject,
                              final java.security.PrivilegedExceptionAction<T> action,
                              final java.security.AccessControlContext acc)
                              throws java.security.PrivilegedActionException ...

            weijun Weijun Wang
            mullan Sean Mullan
            Sean Mullan
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: