Skip to content

Commit c202601

Browse files
committed
HADOOP-19212 [JDK23] org.apache.hadoop.security.UserGroupInformation use of Subject needs to move to replacement APIs
1 parent 826c1f5 commit c202601

File tree

42 files changed

+590
-243
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+590
-243
lines changed

hadoop-common-project/hadoop-auth/src/main/java/org/apache/hadoop/security/authentication/client/KerberosAuthenticator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.apache.hadoop.security.authentication.server.HttpConstants;
2020
import org.apache.hadoop.security.authentication.util.AuthToken;
2121
import org.apache.hadoop.security.authentication.util.KerberosUtil;
22+
import org.apache.hadoop.util.SubjectUtil;
2223
import org.ietf.jgss.GSSContext;
2324
import org.ietf.jgss.GSSManager;
2425
import org.ietf.jgss.GSSName;
@@ -300,8 +301,7 @@ private boolean isNegotiate(HttpURLConnection conn) throws IOException {
300301
private void doSpnegoSequence(final AuthenticatedURL.Token token)
301302
throws IOException, AuthenticationException {
302303
try {
303-
AccessControlContext context = AccessController.getContext();
304-
Subject subject = Subject.getSubject(context);
304+
Subject subject = SubjectUtil.current();
305305
if (subject == null
306306
|| (!KerberosUtil.hasKerberosKeyTab(subject)
307307
&& !KerberosUtil.hasKerberosTicket(subject))) {
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package org.apache.hadoop.util;
2+
3+
import java.lang.invoke.MethodHandle;
4+
import java.lang.invoke.MethodHandles;
5+
import java.lang.invoke.MethodType;
6+
import java.lang.reflect.UndeclaredThrowableException;
7+
import java.security.PrivilegedAction;
8+
import java.security.PrivilegedActionException;
9+
import java.security.PrivilegedExceptionAction;
10+
import java.util.concurrent.Callable;
11+
import java.util.concurrent.CompletionException;
12+
13+
import javax.security.auth.Subject;
14+
15+
import org.apache.hadoop.classification.InterfaceAudience;
16+
17+
@InterfaceAudience.Private()
18+
public class SubjectUtil {
19+
private static final MethodHandle CALL_AS = lookupCallAs();
20+
private static final MethodHandle CURRENT = lookupCurrent();
21+
22+
private SubjectUtil() {
23+
}
24+
25+
private static MethodHandle lookupCallAs() {
26+
MethodHandles.Lookup lookup = MethodHandles.lookup();
27+
try {
28+
try {
29+
// Subject.doAs() is deprecated for removal and replaced by Subject.callAs().
30+
// Lookup first the new API, since for Java versions where both exist, the
31+
// new API delegates to the old API (for example Java 18, 19 and 20).
32+
// Otherwise (Java 17), lookup the old API.
33+
return lookup.findStatic(Subject.class, "callAs",
34+
MethodType.methodType(Object.class, Subject.class, Callable.class));
35+
} catch (NoSuchMethodException x) {
36+
try {
37+
// Lookup the old API.
38+
MethodType oldSignature = MethodType.methodType(Object.class, Subject.class,
39+
PrivilegedExceptionAction.class);
40+
MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature);
41+
// Convert the Callable used in the new API to the PrivilegedAction used in the
42+
// old
43+
// API.
44+
MethodType convertSignature = MethodType.methodType(PrivilegedExceptionAction.class,
45+
Callable.class);
46+
MethodHandle converter = lookup.findStatic(SubjectUtil.class, "callableToPrivilegedExceptionAction",
47+
convertSignature);
48+
return MethodHandles.filterArguments(doAs, 1, converter);
49+
} catch (NoSuchMethodException e) {
50+
throw new AssertionError(e);
51+
}
52+
}
53+
} catch (IllegalAccessException e) {
54+
throw new AssertionError(e);
55+
}
56+
}
57+
58+
private static MethodHandle lookupCurrent() {
59+
MethodHandles.Lookup lookup = MethodHandles.lookup();
60+
try {
61+
// Subject.getSubject(AccessControlContext) is deprecated for removal and
62+
// replaced by
63+
// Subject.current().
64+
// Lookup first the new API, since for Java versions where both exists, the
65+
// new API delegates to the old API (for example Java 18, 19 and 20).
66+
// Otherwise (Java 17), lookup the old API.
67+
return lookup.findStatic(Subject.class, "current", MethodType.methodType(Subject.class));
68+
} catch (NoSuchMethodException e) {
69+
MethodHandle getContext = lookupGetContext();
70+
MethodHandle getSubject = lookupGetSubject();
71+
return MethodHandles.filterReturnValue(getContext, getSubject);
72+
} catch (IllegalAccessException e) {
73+
throw new AssertionError(e);
74+
}
75+
}
76+
77+
private static MethodHandle lookupGetSubject() {
78+
MethodHandles.Lookup lookup = MethodHandles.lookup();
79+
try {
80+
Class<?> contextklass = ClassLoader.getSystemClassLoader().loadClass("java.security.AccessControlContext");
81+
return lookup.findStatic(Subject.class, "getSubject", MethodType.methodType(Subject.class, contextklass));
82+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
83+
throw new AssertionError(e);
84+
}
85+
}
86+
87+
private static MethodHandle lookupGetContext() {
88+
try {
89+
// Use reflection to work with Java versions that have and don't have
90+
// AccessController.
91+
Class<?> controllerKlass = ClassLoader.getSystemClassLoader().loadClass("java.security.AccessController");
92+
Class<?> contextklass = ClassLoader.getSystemClassLoader().loadClass("java.security.AccessControlContext");
93+
94+
MethodHandles.Lookup lookup = MethodHandles.lookup();
95+
return lookup.findStatic(controllerKlass, "getContext", MethodType.methodType(contextklass));
96+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
97+
throw new AssertionError(e);
98+
}
99+
}
100+
101+
/**
102+
* Maps to Subject.callAs() if available, otherwise maps to Subject.doAs()
103+
*
104+
* @param subject the subject this action runs as
105+
* @param action the action to run
106+
* @return the result of the action
107+
* @param <T> the type of the result
108+
* @throws CompletionException
109+
*/
110+
public static <T> T callAs(Subject subject, Callable<T> action) throws CompletionException {
111+
try {
112+
return (T) CALL_AS.invoke(subject, action);
113+
} catch (PrivilegedActionException e) {
114+
throw new CompletionException(e.getCause());
115+
} catch (Throwable t) {
116+
throw sneakyThrow(t);
117+
}
118+
}
119+
120+
/**
121+
* Maps action to a Callable, and delegates to callAs(). On older JVMs, the
122+
* action may be double wrapped (into Callable, and then back into
123+
* PrivilegedAction).
124+
*
125+
* @param subject the subject this action runs as
126+
* @param action action the action to run
127+
* @return the result of the action
128+
*/
129+
public static <T> T doAs(Subject subject, PrivilegedAction<T> action) {
130+
return callAs(subject, privilegedActionToCallable(action));
131+
}
132+
133+
/**
134+
* Maps action to a Callable, and delegates to callAs(). On older JVMs, the
135+
* action may be double wrapped (into Callable, and then back into
136+
* PrivilegedAction).
137+
*
138+
* @param subject the subject this action runs as
139+
* @param action action the action to run
140+
* @return the result of the action
141+
*/
142+
public static <T> T doAs(Subject subject, PrivilegedExceptionAction<T> action) throws PrivilegedActionException {
143+
try {
144+
return callAs(subject, privilegedExceptionActionToCallable(action));
145+
} catch (CompletionException ce) {
146+
try {
147+
Exception cause = (Exception) (ce.getCause());
148+
throw new PrivilegedActionException(cause);
149+
} catch (ClassCastException castException) {
150+
// This should never happen, as PrivilegedExceptionAction should not wrap
151+
// non-checked exceptions
152+
throw new PrivilegedActionException(new UndeclaredThrowableException(ce.getCause()));
153+
}
154+
}
155+
}
156+
157+
/**
158+
* Maps to Subject.currect() is available, otherwise maps to
159+
* Subject.getSubject()
160+
*
161+
* @return the current subject
162+
*/
163+
public static Subject current() {
164+
try {
165+
return (Subject) CURRENT.invoke();
166+
} catch (Throwable t) {
167+
throw sneakyThrow(t);
168+
}
169+
}
170+
171+
@SuppressWarnings("unused")
172+
private static <T> PrivilegedExceptionAction<T> callableToPrivilegedExceptionAction(Callable<T> callable) {
173+
return callable::call;
174+
}
175+
176+
private static <T> Callable<T> privilegedExceptionActionToCallable(PrivilegedExceptionAction<T> action) {
177+
return action::run;
178+
}
179+
180+
private static <T> Callable<T> privilegedActionToCallable(PrivilegedAction<T> action) {
181+
return action::run;
182+
}
183+
184+
@SuppressWarnings("unchecked")
185+
private static <E extends Throwable> RuntimeException sneakyThrow(Throwable e) throws E {
186+
throw (E) e;
187+
}
188+
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/crypto/key/kms/KMSClientProvider.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
import java.util.List;
7575
import java.util.Map;
7676
import java.util.Queue;
77+
import java.util.concurrent.Callable;
7778
import java.util.concurrent.ExecutionException;
7879

7980
import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension;
@@ -504,10 +505,10 @@ private HttpURLConnection createConnection(final URL url, String method)
504505
HttpURLConnection conn;
505506
try {
506507
final String doAsUser = getDoAsUser();
507-
conn = getActualUgi().doAs(new PrivilegedExceptionAction
508+
conn = getActualUgi().callAs(new Callable
508509
<HttpURLConnection>() {
509510
@Override
510-
public HttpURLConnection run() throws Exception {
511+
public HttpURLConnection call() throws Exception {
511512
DelegationTokenAuthenticatedURL authUrl =
512513
createAuthenticatedURL();
513514
return authUrl.openConnection(url, authToken, doAsUser);
@@ -1026,9 +1027,9 @@ public Token<?> getDelegationToken(final String renewer) throws IOException {
10261027
Token<?> token = null;
10271028
try {
10281029
final String doAsUser = getDoAsUser();
1029-
token = getActualUgi().doAs(new PrivilegedExceptionAction<Token<?>>() {
1030+
token = getActualUgi().callAs(new Callable<Token<?>>() {
10301031
@Override
1031-
public Token<?> run() throws Exception {
1032+
public Token<?> call() throws Exception {
10321033
// Not using the cached token here.. Creating a new token here
10331034
// everytime.
10341035
LOG.debug("Getting new token from {}, renewer:{}", url, renewer);
@@ -1065,10 +1066,10 @@ public long renewDelegationToken(final Token<?> dToken) throws IOException {
10651066
token, url, doAsUser);
10661067
final DelegationTokenAuthenticatedURL authUrl =
10671068
createAuthenticatedURL();
1068-
return getActualUgi().doAs(
1069-
new PrivilegedExceptionAction<Long>() {
1069+
return getActualUgi().callAs(
1070+
new Callable<Long>() {
10701071
@Override
1071-
public Long run() throws Exception {
1072+
public Long call() throws Exception {
10721073
return authUrl.renewDelegationToken(url, token, doAsUser);
10731074
}
10741075
}
@@ -1088,10 +1089,10 @@ public Void cancelDelegationToken(final Token<?> dToken) throws IOException {
10881089
final String doAsUser = getDoAsUser();
10891090
final DelegationTokenAuthenticatedURL.Token token =
10901091
generateDelegationToken(dToken);
1091-
return getActualUgi().doAs(
1092-
new PrivilegedExceptionAction<Void>() {
1092+
return getActualUgi().callAs(
1093+
new Callable<Void>() {
10931094
@Override
1094-
public Void run() throws Exception {
1095+
public Void call() throws Exception {
10951096
final URL url = createURL(null, null, null, null);
10961097
LOG.debug("Cancelling delegation token {} with url:{}, as:{}",
10971098
dToken, url, doAsUser);

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileContext.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import java.io.InputStream;
2323
import java.io.OutputStream;
2424
import java.net.URI;
25-
import java.security.PrivilegedExceptionAction;
2625
import java.util.ArrayList;
2726
import java.util.Arrays;
2827
import java.util.Collection;
@@ -35,6 +34,7 @@
3534
import java.util.Stack;
3635
import java.util.TreeSet;
3736
import java.util.Map.Entry;
37+
import java.util.concurrent.Callable;
3838
import java.util.concurrent.CompletableFuture;
3939

4040
import javax.annotation.Nonnull;
@@ -340,9 +340,9 @@ private static AbstractFileSystem getAbstractFileSystem(
340340
UserGroupInformation user, final URI uri, final Configuration conf)
341341
throws UnsupportedFileSystemException, IOException {
342342
try {
343-
return user.doAs(new PrivilegedExceptionAction<AbstractFileSystem>() {
343+
return user.callAs(new Callable<AbstractFileSystem>() {
344344
@Override
345-
public AbstractFileSystem run() throws UnsupportedFileSystemException {
345+
public AbstractFileSystem call() throws UnsupportedFileSystemException {
346346
return AbstractFileSystem.get(uri, conf);
347347
}
348348
});

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
import java.lang.ref.ReferenceQueue;
2626
import java.net.URI;
2727
import java.net.URISyntaxException;
28-
import java.security.PrivilegedExceptionAction;
2928
import java.util.ArrayList;
3029
import java.util.Collection;
3130
import java.util.Collections;
@@ -43,6 +42,7 @@
4342
import java.util.Set;
4443
import java.util.Stack;
4544
import java.util.TreeSet;
45+
import java.util.concurrent.Callable;
4646
import java.util.concurrent.CompletableFuture;
4747
import java.util.concurrent.Semaphore;
4848
import java.util.concurrent.atomic.AtomicLong;
@@ -271,9 +271,9 @@ public static FileSystem get(final URI uri, final Configuration conf,
271271
conf.get(CommonConfigurationKeys.KERBEROS_TICKET_CACHE_PATH);
272272
UserGroupInformation ugi =
273273
UserGroupInformation.getBestUGI(ticketCachePath, user);
274-
return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
274+
return ugi.callAs(new Callable<FileSystem>() {
275275
@Override
276-
public FileSystem run() throws IOException {
276+
public FileSystem call() throws IOException {
277277
return get(uri, conf);
278278
}
279279
});
@@ -574,9 +574,9 @@ public static FileSystem newInstance(final URI uri, final Configuration conf,
574574
conf.get(CommonConfigurationKeys.KERBEROS_TICKET_CACHE_PATH);
575575
UserGroupInformation ugi =
576576
UserGroupInformation.getBestUGI(ticketCachePath, user);
577-
return ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
577+
return ugi.callAs(new Callable<FileSystem>() {
578578
@Override
579-
public FileSystem run() throws IOException {
579+
public FileSystem call() throws IOException {
580580
return newInstance(uri, conf);
581581
}
582582
});

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFileSystem.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,14 @@
2828
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT;
2929
import static org.apache.hadoop.fs.viewfs.Constants.CONFIG_VIEWFS_TRASH_FORCE_INSIDE_MOUNT_POINT_DEFAULT;
3030

31+
import java.util.concurrent.Callable;
3132
import java.util.concurrent.ConcurrentHashMap;
3233
import java.util.concurrent.ConcurrentMap;
3334
import java.util.function.Function;
3435
import java.io.FileNotFoundException;
3536
import java.io.IOException;
3637
import java.net.URI;
3738
import java.net.URISyntaxException;
38-
import java.security.PrivilegedExceptionAction;
3939
import java.util.ArrayList;
4040
import java.util.Arrays;
4141
import java.util.Collection;
@@ -328,9 +328,9 @@ protected Function<URI, FileSystem> initAndGetTargetFs() {
328328
public FileSystem apply(final URI uri) {
329329
FileSystem fs;
330330
try {
331-
fs = ugi.doAs(new PrivilegedExceptionAction<FileSystem>() {
331+
fs = ugi.callAs(new Callable<FileSystem>() {
332332
@Override
333-
public FileSystem run() throws IOException {
333+
public FileSystem call() throws IOException {
334334
if (enableInnerCache) {
335335
synchronized (cache) {
336336
return cache.get(uri, config);

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ViewFs.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.io.IOException;
2727
import java.net.URI;
2828
import java.net.URISyntaxException;
29-
import java.security.PrivilegedExceptionAction;
3029
import java.util.ArrayList;
3130
import java.util.EnumSet;
3231
import java.util.HashSet;
@@ -35,6 +34,7 @@
3534
import java.util.Map.Entry;
3635

3736
import java.util.Set;
37+
import java.util.concurrent.Callable;
3838

3939
import org.apache.hadoop.util.Preconditions;
4040
import org.apache.hadoop.classification.InterfaceAudience;
@@ -247,10 +247,10 @@ protected Function<URI, AbstractFileSystem> initAndGetTargetFs() {
247247
public AbstractFileSystem apply(final URI uri) {
248248
AbstractFileSystem fs;
249249
try {
250-
fs = ugi.doAs(
251-
new PrivilegedExceptionAction<AbstractFileSystem>() {
250+
fs = ugi.callAs(
251+
new Callable<AbstractFileSystem>() {
252252
@Override
253-
public AbstractFileSystem run() throws IOException {
253+
public AbstractFileSystem call() throws IOException {
254254
return AbstractFileSystem.createFileSystem(uri, config);
255255
}
256256
});

0 commit comments

Comments
 (0)