diff --git a/src/java.base/share/classes/java/lang/System.java b/src/java.base/share/classes/java/lang/System.java
index 39ecb2a6436ca..5544165588aa2 100644
--- a/src/java.base/share/classes/java/lang/System.java
+++ b/src/java.base/share/classes/java/lang/System.java
@@ -2687,6 +2687,11 @@ public void copyToSegmentRaw(String string, MemorySegment segment, long offset)
public boolean bytesCompatible(String string, Charset charset) {
return string.bytesCompatible(charset);
}
+
+ @Override
+ public boolean allowSecurityManager() {
+ return System.allowSecurityManager();
+ }
});
}
}
diff --git a/src/java.base/share/classes/javax/security/auth/Subject.java b/src/java.base/share/classes/javax/security/auth/Subject.java
index 60e6d5fe30ea2..a16cb2119d5bd 100644
--- a/src/java.base/share/classes/javax/security/auth/Subject.java
+++ b/src/java.base/share/classes/javax/security/auth/Subject.java
@@ -35,6 +35,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionException;
+import jdk.internal.access.SharedSecrets;
import sun.security.util.ResourcesMgr;
/**
@@ -90,6 +91,40 @@
* {@code Principal} implementations associated with Subjects
* must implement {@code Serializable}.
*
+ *
Deprecated Methods and Replacements
+ *
+ * The following methods in this class for user-based authorization
+ * that are dependent on Security Manager APIs are deprecated for removal:
+ *
+ * - {@link #getSubject(AccessControlContext)}
+ *
- {@link #doAs(Subject, PrivilegedAction)}
+ *
- {@link #doAs(Subject, PrivilegedExceptionAction)}
+ *
- {@link #doAsPrivileged(Subject, PrivilegedAction, AccessControlContext)}
+ *
- {@link #doAsPrivileged(Subject, PrivilegedExceptionAction, AccessControlContext)}
+ *
+ * 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.
+ *
+ * These methods behave differently depending on
+ * whether a security manager is allowed or disallowed:
+ *
+* - 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.
+ *
- 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)} method.
+ *
+ *
* @since 1.4
* @see java.security.Principal
* @see java.security.DomainCombiner
@@ -258,7 +293,9 @@ public boolean isReadOnly() {
/**
* Get the {@code Subject} associated with the provided
- * {@code AccessControlContext}.
+ * {@code AccessControlContext}. When a security manager is
+ * not allowed, this method is not supported
+ * and throws an {@code UnsupportedOperationException}.
*
* The {@code AccessControlContext} may contain many
* Subjects (from nested {@code doAs} calls).
@@ -273,6 +310,9 @@ public boolean isReadOnly() {
* 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,20 +342,27 @@ public static Subject getSubject(final AccessControlContext acc) {
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 SCOPED_SUBJECT =
+ ScopedValue.newInstance();
+
/**
* Returns the current subject.
*
@@ -325,13 +372,11 @@ public Subject run() {
* 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.
+ *
+ * If a security manager is not allowed, the
+ * current subject binds to the period of the execution of the current
+ * thread. Otherwise, it is associated with the current
+ * {@code AccessControlContext}.
*
* @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 +385,30 @@ public Subject run() {
*/
@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.
+ *
If a security manager is not allowed,
+ * this method launches {@code action} and binds {@code subject} to the
+ * period of its execution.
+ *
Otherwise, 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}.
*
* @param subject the {@code Subject} that the specified {@code action}
* will run as. This parameter may be {@code null}.
@@ -375,22 +427,33 @@ public static Subject current() {
public static T callAs(final Subject subject,
final Callable action) throws CompletionException {
Objects.requireNonNull(action);
- try {
- PrivilegedExceptionAction 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 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}.
*
- * This method first retrieves the current Thread's
+ *
If a security manager is not allowed,
+ * this method launches {@code action} and binds {@code subject} to the
+ * period of its execution.
+ *
Otherwise, this method first retrieves the current Thread's
* {@code AccessControlContext} via
* {@code AccessController.getContext},
* and then instantiates a new {@code AccessControlContext}
@@ -444,20 +507,38 @@ public static T doAs(final Subject subject,
Objects.requireNonNull(action,
ResourcesMgr.getString("invalid.null.action.provided"));
- // 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));
+ 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));
+ }
}
/**
* Perform work as a particular {@code Subject}.
*
- * This method first retrieves the current Thread's
+ *
If a security manager is not allowed,
+ * this method launches {@code action} and binds {@code subject} to the
+ * period of its execution.
+ *
Otherwise, this method first retrieves the current Thread's
* {@code AccessControlContext} via
* {@code AccessController.getContext},
* and then instantiates a new {@code AccessControlContext}
@@ -517,19 +598,39 @@ public static T doAs(final Subject subject,
Objects.requireNonNull(action,
ResourcesMgr.getString("invalid.null.action.provided"));
- // 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));
+ 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));
+ }
}
/**
* Perform privileged work as a particular {@code Subject}.
*
- * This method behaves exactly as {@code Subject.doAs},
+ *
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.
+ *
Otherwise, 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
@@ -583,23 +684,41 @@ public static T doAsPrivileged(final Subject subject,
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);
-
- // call doPrivileged and push this new context on the stack
- return java.security.AccessController.doPrivileged
- (action,
- createContext(subject, callerAcc));
+ 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));
+ }
}
/**
* Perform privileged work as a particular {@code Subject}.
*
- * This method behaves exactly as {@code Subject.doAs},
+ *
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.
+ *
Otherwise, 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
@@ -659,16 +778,33 @@ public static T doAsPrivileged(final Subject subject,
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);
-
- // call doPrivileged and push this new context on the stack
- return java.security.AccessController.doPrivileged
- (action,
- createContext(subject, callerAcc));
+ 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));
+ }
}
@SuppressWarnings("removal")
diff --git a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
index 93815009a543f..669db761497b4 100644
--- a/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
+++ b/src/java.base/share/classes/jdk/internal/access/JavaLangAccess.java
@@ -586,4 +586,10 @@ StackWalker newStackWalkerInstance(Set options,
* Are the string bytes compatible with the given charset?
*/
boolean bytesCompatible(String string, Charset charset);
+
+ /**
+ * Is a security manager already set or allowed to be set
+ * (using -Djava.security.manager=allow)?
+ */
+ boolean allowSecurityManager();
}
diff --git a/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java b/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java
index e04a375c998b0..8a310725abde7 100644
--- a/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java
+++ b/src/java.management/share/classes/com/sun/jmx/remote/internal/ServerNotifForwarder.java
@@ -346,7 +346,7 @@ public void terminate() {
@SuppressWarnings("removal")
private Subject getSubject() {
- return Subject.getSubject(AccessController.getContext());
+ return Subject.current();
}
private void checkState() throws IOException {
diff --git a/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerFileAccessController.java b/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerFileAccessController.java
index d694d9cce31dd..33dcd75e53006 100644
--- a/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerFileAccessController.java
+++ b/src/java.management/share/classes/com/sun/jmx/remote/security/MBeanServerFileAccessController.java
@@ -27,7 +27,6 @@
import java.io.FileInputStream;
import java.io.IOException;
-import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
@@ -301,13 +300,11 @@ private static Properties propertiesFromFile(String fname)
}
private synchronized void checkAccess(AccessType requiredAccess, String arg) {
- @SuppressWarnings("removal")
- final AccessControlContext acc = AccessController.getContext();
@SuppressWarnings("removal")
final Subject s =
AccessController.doPrivileged(new PrivilegedAction<>() {
public Subject run() {
- return Subject.getSubject(acc);
+ return Subject.current();
}
});
if (s == null) return; /* security has not been enabled */
diff --git a/test/jdk/javax/management/monitor/ThreadPoolAccTest.java b/test/jdk/javax/management/monitor/ThreadPoolAccTest.java
index 8218a58b4109e..4d6686f681ac8 100644
--- a/test/jdk/javax/management/monitor/ThreadPoolAccTest.java
+++ b/test/jdk/javax/management/monitor/ThreadPoolAccTest.java
@@ -33,7 +33,6 @@
* @run main ThreadPoolAccTest
*/
-import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Date;
import java.util.Set;
@@ -67,7 +66,7 @@ public String getString() {
return "";
}
private void setPrincipal() {
- Subject subject = Subject.getSubject(AccessController.getContext());
+ Subject subject = Subject.current();
Set principals = subject.getPrincipals(JMXPrincipal.class);
principal = principals.iterator().next().getName();
}
diff --git a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/SimpleStandard.java b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/SimpleStandard.java
index bc503b3e29169..7b7376a12ca04 100644
--- a/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/SimpleStandard.java
+++ b/test/jdk/javax/management/remote/mandatory/passwordAuthenticator/SimpleStandard.java
@@ -40,8 +40,6 @@
* - the "getNbResets()" method.
*/
-import java.security.AccessControlContext;
-import java.security.AccessController;
import java.security.Principal;
import java.util.Set;
import javax.management.AttributeChangeNotification;
@@ -152,8 +150,7 @@ public int getNbResets() {
* type JMXPrincipal and refers to the "monitorRole" identity.
*/
private void checkSubject() {
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
Set principals = subject.getPrincipals();
Principal principal = (Principal) principals.iterator().next();
if (!(principal instanceof JMXPrincipal))
diff --git a/test/jdk/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java b/test/jdk/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java
index 72415a33cc51b..6b77abc248728 100644
--- a/test/jdk/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java
+++ b/test/jdk/javax/management/remote/mandatory/subjectDelegation/SimpleStandard.java
@@ -40,8 +40,6 @@
* - the "getNbResets()" method.
*/
-import java.security.AccessControlContext;
-import java.security.AccessController;
import java.security.Principal;
import java.util.Set;
import javax.management.AttributeChangeNotification;
@@ -150,8 +148,7 @@ public int getNbResets() {
* type JMXPrincipal and refers to the principalName identity.
*/
private void checkSubject(String op) {
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
Set principals = subject.getPrincipals();
Principal principal = (Principal) principals.iterator().next();
if (!(principal instanceof JMXPrincipal))
diff --git a/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java b/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java
new file mode 100755
index 0000000000000..6681bb8481f89
--- /dev/null
+++ b/test/jdk/javax/security/auth/Subject/CallAsWithScopedValue.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8296244
+ * @enablePreview
+ * @summary Implement Subject.current and Subject.callAs using scoped values
+ * @run main/othervm -Djava.security.manager=allow CallAsWithScopedValue false
+ * @run main/othervm -Djava.security.manager=disallow CallAsWithScopedValue true
+ */
+import com.sun.security.auth.UserPrincipal;
+
+import javax.security.auth.Subject;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.StructuredTaskScope;
+
+public class CallAsWithScopedValue {
+
+ private static Map results = new ConcurrentHashMap();
+
+ public static void main(String[] args) throws Exception {
+
+ boolean usv = Boolean.parseBoolean(args[0]);
+
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new UserPrincipal("Duke"));
+
+ // Always observable in the same thread
+ Subject.callAs(subject, () -> check(0, Subject.current(), "Duke"));
+
+ // Observable in the same thread in ACC mode, but not in the SV mode
+ Subject.callAs(subject, () -> {
+ Thread.ofPlatform().start(() -> check(1, Subject.current(), usv ? null : "Duke")).join();
+ return null;
+ });
+
+ // Never observable in a new virtual thread
+ Subject.callAs(subject, () -> {
+ Thread.ofVirtual().start(() -> check(2, Subject.current(), null)).join();
+ return null;
+ });
+
+ // Observable in structured concurrency in SV mode, but not in ACC mode
+ Subject.callAs(subject, () -> {
+ try (var scope = new StructuredTaskScope<>()) {
+ scope.fork(() -> check(3, Subject.current(), usv ? "Duke" : null));
+ scope.join();
+ }
+ return null;
+ });
+
+ // Suggested way to pass the current subject into arbitrary
+ // threads. Grab one using current() and explicitly pass it
+ // into the new thread.
+ Subject.callAs(subject, () -> {
+ Subject current = Subject.current();
+ Thread.ofPlatform().start(() -> {
+ Subject.callAs(current, () -> check(4, Subject.current(), "Duke"));
+ }).join();
+ return null;
+ });
+
+ if (results.size() != 5 || results.containsValue(false)) {
+ System.out.println(results);
+ throw new RuntimeException("Failed");
+ }
+ }
+
+ static String check(int type, Subject current, String expected) {
+ String actual;
+ if (current == null) {
+ actual = null;
+ } else {
+ var set = current.getPrincipals(UserPrincipal.class);
+ actual = set.isEmpty()
+ ? null
+ : set.iterator().next().getName();
+ }
+ results.put(type, Objects.equals(actual, expected));
+ return actual;
+ }
+}
diff --git a/test/jdk/javax/security/auth/Subject/Compat.java b/test/jdk/javax/security/auth/Subject/Compat.java
new file mode 100755
index 0000000000000..ccd203fd2dfc3
--- /dev/null
+++ b/test/jdk/javax/security/auth/Subject/Compat.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2021, 2022, 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.
+ *
+ * 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.
+ */
+import com.sun.security.auth.UserPrincipal;
+
+import javax.security.auth.Subject;
+import javax.security.auth.SubjectDomainCombiner;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedExceptionAction;
+import java.util.Objects;
+
+/*
+ * @test
+ * @run main/othervm -Djava.security.manager=allow Compat
+ */
+public class Compat {
+
+// static PrivilegedAction action
+// = () -> AccessController.getContext();
+
+ static PrivilegedExceptionAction action
+ = () -> AccessController.getContext();
+
+ static boolean failed = false;
+
+ public static void main(String[] args) throws Exception {
+ main0(null);
+ var t = new Thread(() -> {
+ try {
+ main0(null);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ t.start();
+ t.join();
+ }
+ public static void main0(String[] args) throws Exception {
+ System.out.println(">>> bare run");
+ run(null);
+ System.out.println(">>> run inside");
+ Subject subject = makeSubject("three");
+ Subject.doAs(subject, (PrivilegedExceptionAction extends Object>)
+ () -> run("three"));
+ if (failed) {
+ throw new RuntimeException();
+ }
+ }
+
+ public static Void run(String from) throws Exception {
+ Subject subject = makeSubject("one");
+ var a1 = Subject.doAs(subject, action);
+ Subject subject2 = makeSubject("two");
+ var a2 = Subject.doAs(subject2, action);
+
+ test("from ether", AccessController.getContext(), from);
+ test("from a1", a1, "one");
+ test("from a2", a2, "two");
+
+ var a3 = Subject.doAsPrivileged(subject, action, a1);
+ test("doAsPriv with one and a1", a3, "one");
+
+ var a4 = Subject.doAsPrivileged(subject, action, a2);
+ test("doAsPriv with one and a2", a4, "one");
+
+ var a5 = Subject.doAsPrivileged(null, action, a2);
+ test("doAsPriv with null and a2", a5, null);
+
+ var a6 = Subject.doAs(null, action);
+ test("doAsPriv with null and this", a6, null);
+
+ var ax = new AccessControlContext(a2, new SubjectDomainCombiner(subject));
+ test("a2 plus subject", ax, "one");
+
+ ax = AccessController.doPrivileged(action, a2);
+ test("doPriv on a2", ax, "two");
+
+ ax = AccessController.doPrivilegedWithCombiner(action);
+ test("doPrivWC", ax, from == null ? null : from);
+
+ ax = AccessController.doPrivilegedWithCombiner(action, a2);
+ test("doPrivWC on a2", ax, from == null ? "two" : from);
+ return null;
+ }
+
+ static Subject makeSubject(String name) {
+ Subject subject = new Subject();
+ subject.getPrincipals().add(new UserPrincipal(name));
+ return subject;
+ }
+
+ static String getSubject(AccessControlContext acc) {
+ var subj = Subject.getSubject(acc);
+ if (subj == null) return null;
+ var princ = subj.getPrincipals(UserPrincipal.class);
+ return (princ == null || princ.isEmpty())
+ ? null
+ : princ.iterator().next().getName();
+ }
+
+ static void test(String label, AccessControlContext acc, String expected) {
+ var actual = getSubject(acc);
+ System.out.println(label + ": " + actual);
+ if (!Objects.equals(actual, expected)) {
+ System.out.println(" Expect " + expected + ", but see " + actual);
+ failed = true;
+ }
+ }
+}
diff --git a/test/jdk/javax/security/auth/Subject/CurrentSubject.java b/test/jdk/javax/security/auth/Subject/CurrentSubject.java
index 67e19012d1f27..c1b0b4ffd301b 100644
--- a/test/jdk/javax/security/auth/Subject/CurrentSubject.java
+++ b/test/jdk/javax/security/auth/Subject/CurrentSubject.java
@@ -22,28 +22,24 @@
*/
import javax.security.auth.Subject;
-import java.security.AccessController;
import java.security.Principal;
import java.security.PrivilegedAction;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.security.PrivilegedExceptionAction;
/*
* @test
* @bug 8267108
* @summary confirm current subject specification
- * @run main/othervm CurrentSubject
+ * @run main/othervm -Djava.security.manager=allow CurrentSubject
+ * @run main/othervm -Djava.security.manager=disallow CurrentSubject
*/
public class CurrentSubject {
- static transient boolean failed = false;
- static CountDownLatch cl = new CountDownLatch(1);
- static AtomicInteger count = new AtomicInteger();
+ static boolean failed = false;
public static void main(String[] args) throws Exception {
// At the beginning, current subject is null
test("", null);
- cl.await();
if (failed) {
throw new Exception("Failed");
}
@@ -57,12 +53,6 @@ public static void main(String[] args) throws Exception {
*/
synchronized static void check(String label, Subject expected) {
Subject cas = Subject.current();
- Subject accs = Subject.getSubject(AccessController.getContext());
- if (cas != accs) {
- failed = true;
- System.out.println(label + ": current " + s2s(cas)
- + " but getSubject is " + s2s(accs));
- }
Subject interested = cas;
if (interested != expected) {
failed = true;
@@ -89,31 +79,23 @@ static Void test(String name, Subject expected) {
// run with a new subject, inside current subject will be the new subject
Subject.callAs(another, () -> test(name + 'c', another));
Subject.doAs(another, (PrivilegedAction) () -> test(name + 'd', another));
+ Subject.doAsPrivileged(another, (PrivilegedAction) () -> test(name + 'e', another), null);
+ try {
+ Subject.doAs(another, (PrivilegedExceptionAction) () -> test(name + 'f', another));
+ Subject.doAsPrivileged(another, (PrivilegedExceptionAction) () -> test(name + 'g', another), null);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
// run with null, inside current subject will be null
Subject.callAs(null, () -> test(name + 'C', null));
Subject.doAs(null, (PrivilegedAction) () -> test(name + 'D', null));
- // new thread, inside current subject is unchanged
- count.incrementAndGet();
- new Thread(() -> {
- try {
- test(name + 't', expected);
- try {
- Thread.sleep(500);
- } catch (Exception e) {
- throw new AssertionError(e);
- }
- // by this time, parent thread should have exited the
- // action and current subject reset, but here
- // current subject unchanged.
- test(name + 'T', expected);
- } finally {
- var n = count.decrementAndGet();
- if (n == 0) {
- cl.countDown();
- }
- assert n >= 0;
- }
- }).start();
+ Subject.doAsPrivileged(null, (PrivilegedAction) () -> test(name + 'E', null), null);
+ try {
+ Subject.doAs(null, (PrivilegedExceptionAction) () -> test(name + 'F', null));
+ Subject.doAsPrivileged(null, (PrivilegedExceptionAction) () -> test(name + 'G', null), null);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
// Now it's reset to original
check(" ".repeat(name.length()) + "<- " + name, expected);
diff --git a/test/jdk/javax/security/auth/Subject/FromACC.java b/test/jdk/javax/security/auth/Subject/FromACC.java
index 07216cb228b70..c954d86202c7f 100644
--- a/test/jdk/javax/security/auth/Subject/FromACC.java
+++ b/test/jdk/javax/security/auth/Subject/FromACC.java
@@ -32,7 +32,6 @@
* @bug 8267108
* @summary confirm current installed subject specification
* @run main/othervm -Djava.security.manager=allow FromACC
- * @run main/othervm -Djava.security.manager=disallow FromACC
*/
public class FromACC {
public static void main(String[] args) throws Exception {
diff --git a/test/jdk/javax/security/auth/Subject/UnsupportedSV.java b/test/jdk/javax/security/auth/Subject/UnsupportedSV.java
new file mode 100644
index 0000000000000..ad85b54be2b50
--- /dev/null
+++ b/test/jdk/javax/security/auth/Subject/UnsupportedSV.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2023, 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.
+ *
+ * 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.
+ */
+
+/*
+ * @test
+ * @bug 8296244
+ * @library /test/lib
+ * @summary Implement Subject.current and Subject.callAs using scoped values
+ * @run main/othervm -Djava.security.manager=disallow UnsupportedSV t1
+ * @run main/othervm -Djava.security.manager=allow UnsupportedSV t2
+ */
+import com.sun.security.auth.UserPrincipal;
+import jdk.test.lib.Utils;
+
+import javax.security.auth.Subject;
+import javax.security.auth.SubjectDomainCombiner;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+public class UnsupportedSV {
+ public static void main(String[] args) throws Exception {
+ switch (args[0]) {
+ case "t1" -> t1();
+ case "t2" -> t2();
+ }
+ }
+
+ // ScopedValue-based implementation is used
+ static void t1() throws Exception {
+ AccessControlContext acc = AccessController.getContext();
+ Utils.runAndCheckException(() -> Subject.getSubject(acc),
+ UnsupportedOperationException.class);
+
+ Subject s = new Subject();
+ s.getPrincipals().add(new UserPrincipal("Duke"));
+
+ // TODO: Still has no way to reject the following code.
+ // Here, AccessController::getContext returns a plan ACC without
+ // the subject inside.
+ AccessControlContext acc2 = Subject.callAs(s, AccessController::getContext);
+ Subject ns = AccessController.doPrivileged(
+ (PrivilegedAction) Subject::current, acc2);
+ System.out.println(ns);
+ }
+
+ // When a security manager is set, ScopedValue-based implementation
+ // will not be used
+ static void t2() {
+ AccessControlContext acc = AccessController.getContext();
+ Subject.getSubject(acc);
+ }
+}
diff --git a/test/jdk/javax/security/auth/Subject/doAs/NestedActions.java b/test/jdk/javax/security/auth/Subject/doAs/NestedActions.java
index e3a9abb45815f..2730d0bbad2fe 100644
--- a/test/jdk/javax/security/auth/Subject/doAs/NestedActions.java
+++ b/test/jdk/javax/security/auth/Subject/doAs/NestedActions.java
@@ -26,9 +26,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.security.AccessControlContext;
import java.security.AccessControlException;
-import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
@@ -282,8 +280,7 @@ class Utils {
static void readFile(String filename) {
System.out.println("ReadFromFileAction: try to read " + filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
System.out.println("principals = " + subject.getPrincipals());
try (FileInputStream fis = new FileInputStream(filename)) {
// do nothing
@@ -294,8 +291,7 @@ static void readFile(String filename) {
static void writeFile(String filename) {
System.out.println("WriteToFileAction: try to write to " + filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
System.out.println("principals = " + subject.getPrincipals());
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(filename))) {
@@ -325,8 +321,7 @@ class WriteToFileAction implements PrivilegedAction {
@Override
public Object run() {
Utils.writeFile(filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
return Subject.doAs(subject, nextAction);
}
@@ -350,8 +345,7 @@ class ReadFromFileAction implements PrivilegedAction {
public Object run() {
Utils.readFile(filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
ReadPropertyAction readProperty = new ReadPropertyAction();
if (anotherSubject != null) {
return Subject.doAs(anotherSubject, readProperty);
@@ -369,8 +363,7 @@ public java.lang.Object run() {
System.out.println("ReadPropertyAction: "
+ "try to read 'java.class.path' property");
- AccessControlContext acc = AccessController.getContext();
- Subject s = Subject.getSubject(acc);
+ Subject s = Subject.current();
System.out.println("principals = " + s.getPrincipals());
System.out.println("java.class.path = "
+ System.getProperty("java.class.path"));
@@ -390,8 +383,7 @@ public WriteToFileNegativeAction(String filename) {
@Override
public Object run() {
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
System.out.println("principals = " + subject.getPrincipals());
try {
@@ -422,8 +414,7 @@ public ReadFromFileNegativeAction(String filename) {
@Override
public Object run() {
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
System.out.println("principals = " + subject.getPrincipals());
try {
@@ -449,8 +440,7 @@ class ReadPropertyNegativeAction implements PrivilegedAction {
public java.lang.Object run() {
System.out.println("Try to read 'java.class.path' property");
- AccessControlContext acc = AccessController.getContext();
- Subject s = Subject.getSubject(acc);
+ Subject s = Subject.current();
System.out.println("principals = " + s.getPrincipals());
try {
@@ -480,8 +470,7 @@ class WriteToFileExceptionAction implements PrivilegedExceptionAction {
@Override
public Object run() throws Exception {
Utils.writeFile(filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
ReadFromFileExceptionAction readFromFile =
new ReadFromFileExceptionAction(filename);
return Subject.doAs(subject, readFromFile);
@@ -500,8 +489,7 @@ class ReadFromFileExceptionAction implements PrivilegedExceptionAction {
@Override
public Object run() throws Exception {
Utils.readFile(filename);
- AccessControlContext acc = AccessController.getContext();
- Subject subject = Subject.getSubject(acc);
+ Subject subject = Subject.current();
ReadPropertyExceptionAction readProperty =
new ReadPropertyExceptionAction();
return Subject.doAs(subject, readProperty);
@@ -515,8 +503,7 @@ class ReadPropertyExceptionAction implements PrivilegedExceptionAction {
public java.lang.Object run() throws Exception {
System.out.println("Try to read 'java.class.path' property");
- AccessControlContext acc = AccessController.getContext();
- Subject s = Subject.getSubject(acc);
+ Subject s = Subject.current();
System.out.println("principals = " + s.getPrincipals());
try {