mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-26 22:34:27 +02:00
8296244: Alternate implementation of user-based authorization Subject APIs that doesn’t depend on Security Manager APIs
Reviewed-by: mullan
This commit is contained in:
parent
000f4d8d15
commit
d32746ef4a
17 changed files with 591 additions and 141 deletions
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1998, 2024, 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
|
||||
|
@ -35,6 +35,7 @@ import java.util.*;
|
|||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CompletionException;
|
||||
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import sun.security.util.ResourcesMgr;
|
||||
|
||||
/**
|
||||
|
@ -90,6 +91,41 @@ import sun.security.util.ResourcesMgr;
|
|||
* {@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 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
|
||||
* <a href="../../../java/lang/SecurityManager.html#set-security-manager">allowed or disallowed</a></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 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>
|
||||
*
|
||||
* @since 1.4
|
||||
* @see java.security.Principal
|
||||
* @see java.security.DomainCombiner
|
||||
|
@ -258,7 +294,9 @@ 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.
|
||||
*
|
||||
* <p> The {@code AccessControlContext} may contain many
|
||||
* Subjects (from nested {@code doAs} calls).
|
||||
|
@ -273,6 +311,9 @@ public final class Subject implements java.io.Serializable {
|
|||
* if no {@code Subject} is associated
|
||||
* with the provided {@code AccessControlContext}.
|
||||
*
|
||||
* @throws UnsupportedOperationException if a security manager is
|
||||
* not allowed
|
||||
*
|
||||
* @throws SecurityException if a security manager is installed and the
|
||||
* caller does not have an
|
||||
* {@link AuthPermission#AuthPermission(String)
|
||||
|
@ -302,36 +343,45 @@ public final class Subject implements java.io.Serializable {
|
|||
Objects.requireNonNull(acc, ResourcesMgr.getString
|
||||
("invalid.null.AccessControlContext.provided"));
|
||||
|
||||
// return the Subject from the DomainCombiner of the provided context
|
||||
return AccessController.doPrivileged
|
||||
(new java.security.PrivilegedAction<>() {
|
||||
public Subject run() {
|
||||
DomainCombiner dc = acc.getDomainCombiner();
|
||||
if (!(dc instanceof SubjectDomainCombiner)) {
|
||||
return null;
|
||||
}
|
||||
SubjectDomainCombiner sdc = (SubjectDomainCombiner)dc;
|
||||
return sdc.getSubject();
|
||||
}
|
||||
});
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
throw new UnsupportedOperationException(
|
||||
"getSubject is supported only if a security manager is allowed");
|
||||
} else {
|
||||
// return the Subject from the DomainCombiner of the provided context
|
||||
return AccessController.doPrivileged
|
||||
(new java.security.PrivilegedAction<>() {
|
||||
public Subject run() {
|
||||
DomainCombiner dc = acc.getDomainCombiner();
|
||||
if (!(dc instanceof SubjectDomainCombiner)) {
|
||||
return null;
|
||||
}
|
||||
SubjectDomainCombiner sdc = (SubjectDomainCombiner) dc;
|
||||
return sdc.getSubject();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static final ScopedValue<Subject> SCOPED_SUBJECT =
|
||||
ScopedValue.newInstance();
|
||||
|
||||
/**
|
||||
* Returns the current subject.
|
||||
* <p>
|
||||
* The current subject is installed by the {@link #callAs} method.
|
||||
*
|
||||
* <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()}.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return the current subject, or {@code null} if a current subject is
|
||||
* not installed or the current subject is set to {@code null}.
|
||||
|
@ -340,23 +390,32 @@ public final class Subject implements java.io.Serializable {
|
|||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public static Subject current() {
|
||||
return getSubject(AccessController.getContext());
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
return SCOPED_SUBJECT.orElse(null);
|
||||
} else {
|
||||
return getSubject(AccessController.getContext());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param subject the {@code Subject} that the specified {@code action}
|
||||
* will run as. This parameter may be {@code null}.
|
||||
|
@ -375,22 +434,31 @@ public final class Subject implements java.io.Serializable {
|
|||
public static <T> T callAs(final Subject subject,
|
||||
final Callable<T> action) throws CompletionException {
|
||||
Objects.requireNonNull(action);
|
||||
try {
|
||||
PrivilegedExceptionAction<T> pa = () -> action.call();
|
||||
@SuppressWarnings("removal")
|
||||
var result = doAs(subject, pa);
|
||||
return result;
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new CompletionException(e.getCause());
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
try {
|
||||
return ScopedValue.callWhere(SCOPED_SUBJECT, subject, action);
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
PrivilegedExceptionAction<T> pa = () -> action.call();
|
||||
@SuppressWarnings("removal")
|
||||
var result = doAs(subject, pa);
|
||||
return result;
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new CompletionException(e.getCause());
|
||||
} catch (Exception e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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},
|
||||
* and then instantiates a new {@code AccessControlContext}
|
||||
|
@ -401,6 +469,10 @@ public final class Subject implements java.io.Serializable {
|
|||
* 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.
|
||||
*
|
||||
* @param subject the {@code Subject} that the specified
|
||||
* {@code action} will run as. This parameter
|
||||
* may be {@code null}.
|
||||
|
@ -444,20 +516,36 @@ public final class Subject implements java.io.Serializable {
|
|||
Objects.requireNonNull(action,
|
||||
ResourcesMgr.getString("invalid.null.action.provided"));
|
||||
|
||||
// set up the new Subject-based AccessControlContext
|
||||
// for doPrivileged
|
||||
final AccessControlContext currentAcc = AccessController.getContext();
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// set up the new Subject-based AccessControlContext
|
||||
// for doPrivileged
|
||||
final AccessControlContext currentAcc = AccessController.getContext();
|
||||
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, currentAcc));
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, currentAcc));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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},
|
||||
* and then instantiates a new {@code AccessControlContext}
|
||||
|
@ -468,6 +556,10 @@ public final class Subject implements java.io.Serializable {
|
|||
* 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.
|
||||
|
||||
* @param subject the {@code Subject} that the specified
|
||||
* {@code action} will run as. This parameter
|
||||
* may be {@code null}.
|
||||
|
@ -517,19 +609,37 @@ public final class Subject implements java.io.Serializable {
|
|||
Objects.requireNonNull(action,
|
||||
ResourcesMgr.getString("invalid.null.action.provided"));
|
||||
|
||||
// set up the new Subject-based AccessControlContext for doPrivileged
|
||||
final AccessControlContext currentAcc = AccessController.getContext();
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// set up the new Subject-based AccessControlContext for doPrivileged
|
||||
final AccessControlContext currentAcc = AccessController.getContext();
|
||||
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, currentAcc));
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, currentAcc));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code AccessControlContext}. If the provided
|
||||
|
@ -537,6 +647,10 @@ public final class Subject implements java.io.Serializable {
|
|||
* 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.
|
||||
*
|
||||
* @param subject the {@code Subject} that the specified
|
||||
* {@code action} will run as. This parameter
|
||||
* may be {@code null}.
|
||||
|
@ -583,23 +697,39 @@ public final class Subject implements java.io.Serializable {
|
|||
Objects.requireNonNull(action,
|
||||
ResourcesMgr.getString("invalid.null.action.provided"));
|
||||
|
||||
// set up the new Subject-based AccessControlContext
|
||||
// for doPrivileged
|
||||
final AccessControlContext callerAcc =
|
||||
(acc == null ?
|
||||
new AccessControlContext(NULL_PD_ARRAY) :
|
||||
acc);
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// set up the new Subject-based AccessControlContext
|
||||
// for doPrivileged
|
||||
final AccessControlContext callerAcc =
|
||||
(acc == null ?
|
||||
new AccessControlContext(NULL_PD_ARRAY) :
|
||||
acc);
|
||||
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, callerAcc));
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, callerAcc));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* {@code AccessControlContext}. If the provided
|
||||
|
@ -607,6 +737,10 @@ public final class Subject implements java.io.Serializable {
|
|||
* 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.
|
||||
*
|
||||
* @param subject the {@code Subject} that the specified
|
||||
* {@code action} will run as. This parameter
|
||||
* may be {@code null}.
|
||||
|
@ -659,16 +793,33 @@ public final class Subject implements java.io.Serializable {
|
|||
Objects.requireNonNull(action,
|
||||
ResourcesMgr.getString("invalid.null.action.provided"));
|
||||
|
||||
// set up the new Subject-based AccessControlContext for doPrivileged
|
||||
final AccessControlContext callerAcc =
|
||||
(acc == null ?
|
||||
new AccessControlContext(NULL_PD_ARRAY) :
|
||||
acc);
|
||||
if (!SharedSecrets.getJavaLangAccess().allowSecurityManager()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// set up the new Subject-based AccessControlContext for doPrivileged
|
||||
final AccessControlContext callerAcc =
|
||||
(acc == null ?
|
||||
new AccessControlContext(NULL_PD_ARRAY) :
|
||||
acc);
|
||||
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, callerAcc));
|
||||
// call doPrivileged and push this new context on the stack
|
||||
return java.security.AccessController.doPrivileged
|
||||
(action,
|
||||
createContext(subject, callerAcc));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue