Skip to content

Commit 0b077ad

Browse files
committed
UGI
1 parent 56a538f commit 0b077ad

File tree

3 files changed

+221
-11
lines changed

3 files changed

+221
-11
lines changed

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

Lines changed: 2 additions & 4 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;
@@ -35,8 +36,6 @@
3536
import java.io.IOException;
3637
import java.net.HttpURLConnection;
3738
import java.net.URL;
38-
import java.security.AccessControlContext;
39-
import java.security.AccessController;
4039
import java.security.PrivilegedActionException;
4140
import java.security.PrivilegedExceptionAction;
4241
import java.util.HashMap;
@@ -300,8 +299,7 @@ private boolean isNegotiate(HttpURLConnection conn) throws IOException {
300299
private void doSpnegoSequence(final AuthenticatedURL.Token token)
301300
throws IOException, AuthenticationException {
302301
try {
303-
AccessControlContext context = AccessController.getContext();
304-
Subject subject = Subject.getSubject(context);
302+
Subject subject = SubjectUtil.current();
305303
if (subject == null
306304
|| (!KerberosUtil.hasKerberosKeyTab(subject)
307305
&& !KerberosUtil.hasKerberosTicket(subject))) {
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.util;
20+
21+
import java.lang.invoke.MethodHandle;
22+
import java.lang.invoke.MethodHandles;
23+
import java.lang.invoke.MethodType;
24+
import java.lang.reflect.UndeclaredThrowableException;
25+
import java.security.PrivilegedAction;
26+
import java.security.PrivilegedActionException;
27+
import java.security.PrivilegedExceptionAction;
28+
import java.util.concurrent.Callable;
29+
import java.util.concurrent.CompletionException;
30+
31+
import javax.security.auth.Subject;
32+
33+
import org.apache.hadoop.classification.InterfaceAudience.Private;
34+
35+
@Private
36+
public class SubjectUtil {
37+
private static MethodHandle CALL_AS;
38+
private static MethodHandle CURRENT;
39+
40+
static {
41+
MethodHandles.Lookup lookup = MethodHandles.lookup();
42+
try {
43+
try {
44+
// Subject.doAs() is deprecated for removal and replaced by Subject.callAs().
45+
// Lookup first the new API, since for Java versions where both exist, the
46+
// new API delegates to the old API (e.g. Java 18, 19 and 20).
47+
// Otherwise (e.g. Java 17), lookup the old API.
48+
CALL_AS = lookup.findStatic(Subject.class, "callAs",
49+
MethodType.methodType(Object.class, Subject.class, Callable.class));
50+
} catch (NoSuchMethodException x) {
51+
try {
52+
// Lookup the old API.
53+
MethodType oldSignature = MethodType.methodType(
54+
Object.class, Subject.class, PrivilegedExceptionAction.class);
55+
MethodHandle doAs = lookup.findStatic(Subject.class, "doAs", oldSignature);
56+
// Convert the Callable used in the new API to the PrivilegedAction used
57+
// in the old API.
58+
MethodType convertSignature = MethodType.methodType(
59+
PrivilegedExceptionAction.class, Callable.class);
60+
MethodHandle converter = lookup.findStatic(
61+
SubjectUtil.class, "callableToPrivilegedExceptionAction", convertSignature);
62+
CALL_AS = MethodHandles.filterArguments(doAs, 1, converter);
63+
} catch (NoSuchMethodException e) {
64+
throw new AssertionError(e);
65+
}
66+
}
67+
} catch (IllegalAccessException e) {
68+
throw new AssertionError(e);
69+
}
70+
}
71+
72+
static {
73+
MethodHandles.Lookup lookup = MethodHandles.lookup();
74+
try {
75+
// Subject.getSubject(AccessControlContext) is deprecated for removal and
76+
// replaced by Subject.current().
77+
// Lookup first the new API, since for Java versions where both exists, the
78+
// new API delegates to the old API (e.g. Java 18, 19 and 20).
79+
// Otherwise (e.g. Java 17), lookup the old API.
80+
CURRENT = lookup.findStatic(
81+
Subject.class, "current", MethodType.methodType(Subject.class));
82+
} catch (NoSuchMethodException e) {
83+
MethodHandle getContext = lookupGetContext();
84+
MethodHandle getSubject = lookupGetSubject();
85+
CURRENT = MethodHandles.filterReturnValue(getContext, getSubject);
86+
} catch (IllegalAccessException e) {
87+
throw new AssertionError(e);
88+
}
89+
}
90+
91+
private static MethodHandle lookupGetSubject() {
92+
MethodHandles.Lookup lookup = MethodHandles.lookup();
93+
try {
94+
Class<?> contextKlass = ClassLoader.getSystemClassLoader()
95+
.loadClass("java.security.AccessControlContext");
96+
return lookup.findStatic(Subject.class,
97+
"getSubject", MethodType.methodType(Subject.class, contextKlass));
98+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
99+
throw new AssertionError(e);
100+
}
101+
}
102+
103+
private static MethodHandle lookupGetContext() {
104+
try {
105+
// Use reflection to work with Java versions that have and don't have
106+
// AccessController.
107+
Class<?> controllerKlass = ClassLoader.getSystemClassLoader()
108+
.loadClass("java.security.AccessController");
109+
Class<?> contextKlass = ClassLoader.getSystemClassLoader()
110+
.loadClass("java.security.AccessControlContext");
111+
112+
MethodHandles.Lookup lookup = MethodHandles.lookup();
113+
return lookup.findStatic(
114+
controllerKlass, "getContext", MethodType.methodType(contextKlass));
115+
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
116+
throw new AssertionError(e);
117+
}
118+
}
119+
120+
/**
121+
* Maps to Subject.callAs() if available, otherwise maps to Subject.doAs().
122+
* It also wraps the Callable so that the Subject is propagated to the new thread
123+
* in all Java versions.
124+
*
125+
* @param subject the subject this action runs as
126+
* @param action the action to run
127+
* @return the result of the action
128+
* @param <T> the type of the result
129+
* @throws CompletionException
130+
*/
131+
@SuppressWarnings("unchecked")
132+
public static <T> T callAs(Subject subject, Callable<T> action) throws CompletionException {
133+
try {
134+
return (T) CALL_AS.invoke(subject, action);
135+
} catch (PrivilegedActionException e) {
136+
throw new CompletionException(e.getCause());
137+
} catch (Throwable t) {
138+
throw sneakyThrow(t);
139+
}
140+
}
141+
142+
/**
143+
* Maps action to a Callable, and delegates to callAs(). On older JVMs, the
144+
* action may be double wrapped (into Callable, and then back into
145+
* PrivilegedAction).
146+
*
147+
* @param subject the subject this action runs as
148+
* @param action action the action to run
149+
* @return the result of the action
150+
*/
151+
public static <T> T doAs(Subject subject, PrivilegedAction<T> action) {
152+
return callAs(subject, privilegedActionToCallable(action));
153+
}
154+
155+
/**
156+
* Maps action to a Callable, and delegates to callAs(). On older JVMs, the
157+
* action may be double wrapped (into Callable, and then back into
158+
* PrivilegedAction).
159+
*
160+
* @param subject the subject this action runs as
161+
* @param action action the action to run
162+
* @return the result of the action
163+
*/
164+
public static <T> T doAs(
165+
Subject subject, PrivilegedExceptionAction<T> action) throws PrivilegedActionException {
166+
try {
167+
return callAs(subject, privilegedExceptionActionToCallable(action));
168+
} catch (CompletionException ce) {
169+
try {
170+
Exception cause = (Exception) (ce.getCause());
171+
throw new PrivilegedActionException(cause);
172+
} catch (ClassCastException castException) {
173+
// This should never happen, as PrivilegedExceptionAction should not wrap
174+
// non-checked exceptions
175+
throw new PrivilegedActionException(new UndeclaredThrowableException(ce.getCause()));
176+
}
177+
}
178+
}
179+
180+
/**
181+
* Maps to Subject.currect() is available, otherwise maps to
182+
* Subject.getSubject()
183+
*
184+
* @return the current subject
185+
*/
186+
public static Subject current() {
187+
try {
188+
return (Subject) CURRENT.invoke();
189+
} catch (Throwable t) {
190+
throw sneakyThrow(t);
191+
}
192+
}
193+
194+
@SuppressWarnings("unused")
195+
private static <T> PrivilegedExceptionAction<T> callableToPrivilegedExceptionAction(
196+
Callable<T> callable) {
197+
return callable::call;
198+
}
199+
200+
private static <T> Callable<T> privilegedExceptionActionToCallable(
201+
PrivilegedExceptionAction<T> action) {
202+
return action::run;
203+
}
204+
205+
private static <T> Callable<T> privilegedActionToCallable(
206+
PrivilegedAction<T> action) {
207+
return action::run;
208+
}
209+
210+
@SuppressWarnings("unchecked")
211+
private static <E extends Throwable> RuntimeException sneakyThrow(Throwable e) throws E {
212+
throw (E) e;
213+
}
214+
}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/UserGroupInformation.java

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@
3333
import java.io.File;
3434
import java.io.IOException;
3535
import java.lang.reflect.UndeclaredThrowableException;
36-
import java.security.AccessControlContext;
37-
import java.security.AccessController;
3836
import java.security.Principal;
3937
import java.security.PrivilegedAction;
4038
import java.security.PrivilegedActionException;
@@ -89,6 +87,7 @@
8987
import org.apache.hadoop.security.token.TokenIdentifier;
9088
import org.apache.hadoop.util.Shell;
9189
import org.apache.hadoop.util.StringUtils;
90+
import org.apache.hadoop.util.SubjectUtil;
9291
import org.apache.hadoop.util.Time;
9392

9493
import org.slf4j.Logger;
@@ -585,8 +584,7 @@ public boolean hasKerberosCredentials() {
585584
@InterfaceStability.Evolving
586585
public static UserGroupInformation getCurrentUser() throws IOException {
587586
ensureInitialized();
588-
AccessControlContext context = AccessController.getContext();
589-
Subject subject = Subject.getSubject(context);
587+
Subject subject = SubjectUtil.current();
590588
if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
591589
return getLoginUser();
592590
} else {
@@ -1936,9 +1934,9 @@ protected Subject getSubject() {
19361934
@InterfaceStability.Evolving
19371935
public <T> T doAs(PrivilegedAction<T> action) {
19381936
tracePrivilegedAction(action);
1939-
return Subject.doAs(subject, action);
1937+
return SubjectUtil.doAs(subject, action);
19401938
}
1941-
1939+
19421940
/**
19431941
* Run the given action as the user, potentially throwing an exception.
19441942
* @param <T> the return type of the run method
@@ -1956,7 +1954,7 @@ public <T> T doAs(PrivilegedExceptionAction<T> action
19561954
) throws IOException, InterruptedException {
19571955
try {
19581956
tracePrivilegedAction(action);
1959-
return Subject.doAs(subject, action);
1957+
return SubjectUtil.doAs(subject, action);
19601958
} catch (PrivilegedActionException pae) {
19611959
Throwable cause = pae.getCause();
19621960
LOG.debug("PrivilegedActionException as: {}", this, cause);

0 commit comments

Comments
 (0)