This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch java25 in repository https://gitbox.apache.org/repos/asf/storm.git
commit 26f4b7902fd32ee9ce3710a219f0532a34384429 Author: Richard Zowalla <[email protected]> AuthorDate: Wed Apr 1 19:31:22 2026 +0200 Migrate to Java 24+ compatible security APIs and add Java 25 to CI (#8456) - Replace Subject.doAs(), Subject.getSubject(AccessControlContext), AccessController.getContext(), and System.getSecurityManager() — all removed in Java 24 with runtime-dispatched compatibility shims that work on Java 17 through 25+. - Introduce SubjectCompat utility with MethodHandle-based dispatch: currentSubject() maps to Subject.current() (18+) or Subject.getSubject(AccessController.getContext()) (17); doAs() maps to Subject.callAs() (18+) or Subject.doAs() (17). - Migrate all 12 removed-API call sites across 8 files. - Consolidate existing ReqContext MethodHandle shim into SubjectCompat. - Remove dead SecurityManager code in NettyRenameThreadFactory. - Add Java 25 to the GitHub Actions CI matrix. --- .github/workflows/maven.yaml | 2 +- .../jvm/org/apache/storm/daemon/worker/Worker.java | 4 +- .../messaging/netty/KerberosSaslNettyClient.java | 60 +++---- .../messaging/netty/KerberosSaslNettyServer.java | 62 +++---- .../messaging/netty/NettyRenameThreadFactory.java | 4 +- .../org/apache/storm/security/auth/ReqContext.java | 63 +------ .../apache/storm/security/auth/SubjectCompat.java | 183 +++++++++++++++++++++ .../auth/kerberos/KerberosSaslTransportPlugin.java | 60 +++---- .../WorkerTokenClientCallbackHandler.java | 4 +- .../org/apache/storm/utils/HadoopLoginUtil.java | 4 +- .../storm/security/auth/SubjectCompatTest.java | 70 ++++++++ .../org/apache/storm/security/auth/AuthTest.java | 9 +- 12 files changed, 347 insertions(+), 178 deletions(-) diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml index f95b502ae..671d06139 100644 --- a/.github/workflows/maven.yaml +++ b/.github/workflows/maven.yaml @@ -28,7 +28,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - java: [ 17, 21 ] + java: [ 17, 21, 25 ] module: [ Client, Server, Core, External, Integration-Test ] experimental: [false] fail-fast: false diff --git a/storm-client/src/jvm/org/apache/storm/daemon/worker/Worker.java b/storm-client/src/jvm/org/apache/storm/daemon/worker/Worker.java index 988e7bd72..c81d98870 100644 --- a/storm-client/src/jvm/org/apache/storm/daemon/worker/Worker.java +++ b/storm-client/src/jvm/org/apache/storm/daemon/worker/Worker.java @@ -20,7 +20,7 @@ import java.io.File; import java.io.IOException; import java.net.UnknownHostException; import java.nio.charset.Charset; -import java.security.PrivilegedExceptionAction; +import org.apache.storm.security.auth.SubjectCompat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -202,7 +202,7 @@ public class Worker implements Shutdownable, DaemonCommon { autoCreds = ClientAuthUtils.getAutoCredentials(topologyConf); subject = ClientAuthUtils.populateSubject(null, autoCreds, initCreds); - Subject.doAs(subject, (PrivilegedExceptionAction<Object>) + SubjectCompat.doAs(subject, () -> loadWorker(stateStorage, stormClusterState, initCreds, initialCredentials) ); diff --git a/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyClient.java b/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyClient.java index 31425d4b4..f38759f72 100644 --- a/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyClient.java +++ b/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyClient.java @@ -14,8 +14,7 @@ package org.apache.storm.messaging.netty; import java.io.IOException; import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import org.apache.storm.security.auth.SubjectCompat; import java.util.Map; import java.util.TreeMap; import javax.security.auth.Subject; @@ -95,28 +94,25 @@ public class KerberosSaslNettyClient { final String fServiceName = serviceName; final CallbackHandler fch = ch; LOG.debug("Kerberos Client with principal: {}, host: {}", fPrincipalName, fHost); - saslClient = Subject.doAs(subject, new PrivilegedExceptionAction<SaslClient>() { - @Override - public SaslClient run() { - try { - Map<String, String> props = new TreeMap<String, String>(); - props.put(Sasl.QOP, "auth"); - props.put(Sasl.SERVER_AUTH, "false"); - return Sasl.createSaslClient( - new String[]{ SaslUtils.KERBEROS }, - fPrincipalName, - fServiceName, - fHost, - props, fch); - } catch (Exception e) { - LOG.error("Subject failed to create sasl client.", e); - return null; - } + saslClient = SubjectCompat.doAs(subject, () -> { + try { + Map<String, String> props = new TreeMap<String, String>(); + props.put(Sasl.QOP, "auth"); + props.put(Sasl.SERVER_AUTH, "false"); + return Sasl.createSaslClient( + new String[]{ SaslUtils.KERBEROS }, + fPrincipalName, + fServiceName, + fHost, + props, fch); + } catch (Exception e) { + LOG.error("Subject failed to create sasl client.", e); + return null; } }); LOG.info("Got Client: {}", saslClient); - } catch (PrivilegedActionException e) { + } catch (Exception e) { LOG.error("KerberosSaslNettyClient: Could not create Sasl Netty Client."); throw new RuntimeException(e); } @@ -134,23 +130,17 @@ public class KerberosSaslNettyClient { */ public byte[] saslResponse(SaslMessageToken saslTokenMessage) { try { - final SaslMessageToken fSaslTokenMessage = saslTokenMessage; - byte[] retval = Subject.doAs(subject, new PrivilegedExceptionAction<byte[]>() { - @Override - public byte[] run() { - try { - byte[] retval = saslClient.evaluateChallenge(fSaslTokenMessage - .getSaslToken()); - return retval; - } catch (SaslException e) { - LOG.error("saslResponse: Failed to respond to SASL server's token:", - e); - throw new RuntimeException(e); - } + return SubjectCompat.doAs(subject, () -> { + try { + return saslClient.evaluateChallenge(saslTokenMessage.getSaslToken()); + } catch (SaslException e) { + LOG.error("saslResponse: Failed to respond to SASL server's token:", e); + throw new RuntimeException(e); } }); - return retval; - } catch (PrivilegedActionException e) { + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { LOG.error("Failed to generate response for token: ", e); throw new RuntimeException(e); } diff --git a/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyServer.java b/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyServer.java index fefcdc66f..0852d74e4 100644 --- a/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyServer.java +++ b/storm-client/src/jvm/org/apache/storm/messaging/netty/KerberosSaslNettyServer.java @@ -14,8 +14,7 @@ package org.apache.storm.messaging.netty; import java.io.IOException; import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import org.apache.storm.security.auth.SubjectCompat; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -83,26 +82,22 @@ class KerberosSaslNettyServer { final String hostName = kerberosName.getHostName(); final String serviceName = kerberosName.getServiceName(); LOG.debug("Server with host: {}", hostName); - saslServer = - Subject.doAs(subject, new PrivilegedExceptionAction<SaslServer>() { - @Override - public SaslServer run() { - try { - Map<String, String> props = new TreeMap<String, String>(); - props.put(Sasl.QOP, "auth"); - props.put(Sasl.SERVER_AUTH, "false"); - return Sasl.createSaslServer(SaslUtils.KERBEROS, - serviceName, - hostName, props, fch); - } catch (Exception e) { - LOG.error("Subject failed to create sasl server.", e); - return null; - } - } - }); + saslServer = SubjectCompat.doAs(subject, () -> { + try { + Map<String, String> props = new TreeMap<String, String>(); + props.put(Sasl.QOP, "auth"); + props.put(Sasl.SERVER_AUTH, "false"); + return Sasl.createSaslServer(SaslUtils.KERBEROS, + serviceName, + hostName, props, fch); + } catch (Exception e) { + LOG.error("Subject failed to create sasl server.", e); + return null; + } + }); LOG.info("Got Server: {}", saslServer); - } catch (PrivilegedActionException e) { + } catch (Exception e) { LOG.error("KerberosSaslNettyServer: Could not create SaslServer: ", e); throw new RuntimeException(e); } @@ -124,23 +119,20 @@ class KerberosSaslNettyServer { */ public byte[] response(final byte[] token) { try { - byte[] retval = Subject.doAs(subject, new PrivilegedExceptionAction<byte[]>() { - @Override - public byte[] run() { - try { - LOG.debug("response: Responding to input token of length: {}", - token.length); - byte[] retval = saslServer.evaluateResponse(token); - return retval; - } catch (SaslException e) { - LOG.error("response: Failed to evaluate client token of length: {} : {}", - token.length, e); - throw new RuntimeException(e); - } + return SubjectCompat.doAs(subject, () -> { + try { + LOG.debug("response: Responding to input token of length: {}", + token.length); + return saslServer.evaluateResponse(token); + } catch (SaslException e) { + LOG.error("response: Failed to evaluate client token of length: {} : {}", + token.length, e); + throw new RuntimeException(e); } }); - return retval; - } catch (PrivilegedActionException e) { + } catch (RuntimeException e) { + throw e; + } catch (Exception e) { LOG.error("Failed to generate response for token: ", e); throw new RuntimeException(e); } diff --git a/storm-client/src/jvm/org/apache/storm/messaging/netty/NettyRenameThreadFactory.java b/storm-client/src/jvm/org/apache/storm/messaging/netty/NettyRenameThreadFactory.java index f66510e70..1bc59c83a 100644 --- a/storm-client/src/jvm/org/apache/storm/messaging/netty/NettyRenameThreadFactory.java +++ b/storm-client/src/jvm/org/apache/storm/messaging/netty/NettyRenameThreadFactory.java @@ -24,9 +24,7 @@ public class NettyRenameThreadFactory implements ThreadFactory { private final String name; public NettyRenameThreadFactory(String name) { - SecurityManager s = System.getSecurityManager(); - group = (s != null) ? s.getThreadGroup() : - Thread.currentThread().getThreadGroup(); + group = Thread.currentThread().getThreadGroup(); this.name = name; } diff --git a/storm-client/src/jvm/org/apache/storm/security/auth/ReqContext.java b/storm-client/src/jvm/org/apache/storm/security/auth/ReqContext.java index 830165084..3e61daf44 100644 --- a/storm-client/src/jvm/org/apache/storm/security/auth/ReqContext.java +++ b/storm-client/src/jvm/org/apache/storm/security/auth/ReqContext.java @@ -18,9 +18,6 @@ package org.apache.storm.security.auth; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.net.InetAddress; import java.security.Principal; import java.util.Set; @@ -38,8 +35,6 @@ import org.apache.storm.shade.com.google.common.annotations.VisibleForTesting; */ public class ReqContext { - private static final MethodHandle CURRENT = lookupCurrent(); - private static final AtomicInteger uniqueId = new AtomicInteger(0); //each thread will have its own request context private static final ThreadLocal<ReqContext> ctxt = @@ -167,64 +162,12 @@ public class ReqContext { } /** - * Maps to Subject.current() is available, otherwise maps to Subject.getSubject() + * Maps to Subject.current() if available, otherwise maps to Subject.getSubject(). * @return the current subject + * @see SubjectCompat#currentSubject() */ public static Subject currentSubject() { - try { - return (Subject) CURRENT.invoke(); - } catch (Throwable t) { - throw new RuntimeException(t); - } - } - - private static MethodHandle lookupCurrent() { - final MethodHandles.Lookup lookup = MethodHandles.lookup(); - try { - // Subject.getSubject(AccessControlContext) is deprecated for removal and replaced by - // Subject.current(). - // Lookup first the new API, since for Java versions where both exists, the - // new API delegates to the old API (for example Java 18, 19 and 20). - // Otherwise (Java 17), lookup the old API. - return lookup.findStatic(Subject.class, "current", - MethodType.methodType(Subject.class)); - } catch (NoSuchMethodException e) { - final MethodHandle getContext = lookupGetContext(); - final MethodHandle getSubject = lookupGetSubject(); - return MethodHandles.filterReturnValue(getContext, getSubject); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - - private static MethodHandle lookupGetSubject() { - final MethodHandles.Lookup lookup = MethodHandles.lookup(); - try { - final Class<?> contextClazz = - ClassLoader.getSystemClassLoader() - .loadClass("java.security.AccessControlContext"); - return lookup.findStatic(Subject.class, "getSubject", - MethodType.methodType(Subject.class, contextClazz)); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { - throw new AssertionError(e); - } - } - - private static MethodHandle lookupGetContext() { - try { - // Use reflection to work with Java versions that have and don't have AccessController. - final Class<?> controllerClazz = - ClassLoader.getSystemClassLoader().loadClass("java.security.AccessController"); - final Class<?> contextClazz = - ClassLoader.getSystemClassLoader() - .loadClass("java.security.AccessControlContext"); - - MethodHandles.Lookup lookup = MethodHandles.lookup(); - return lookup.findStatic(controllerClazz, "getContext", - MethodType.methodType(contextClazz)); - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { - throw new AssertionError(e); - } + return SubjectCompat.currentSubject(); } } diff --git a/storm-client/src/jvm/org/apache/storm/security/auth/SubjectCompat.java b/storm-client/src/jvm/org/apache/storm/security/auth/SubjectCompat.java new file mode 100644 index 000000000..a54541373 --- /dev/null +++ b/storm-client/src/jvm/org/apache/storm/security/auth/SubjectCompat.java @@ -0,0 +1,183 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.storm.security.auth; + +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletionException; +import javax.security.auth.Subject; + +/** + * Compatibility shim for {@link Subject} methods removed in Java 24. + * + * <ul> + * <li>{@code Subject.getSubject(AccessControlContext)} → {@code Subject.current()} (Java 18+)</li> + * <li>{@code Subject.doAs(Subject, PrivilegedExceptionAction)} → {@code Subject.callAs(Subject, Callable)} (Java 18+)</li> + * </ul> + * + * <p>All dispatch is resolved once at class-init via {@link MethodHandle}, so there is no per-call reflection overhead. + */ +public final class SubjectCompat { + + private static final MethodHandle CURRENT = lookupCurrent(); + private static final MethodHandle CALL_AS = lookupCallAs(); + private static final boolean USE_CALL_AS = probeCallAs(); + + private SubjectCompat() { + } + + /** + * Return the current {@link Subject}, equivalent to {@code Subject.current()} on Java 18+ + * or {@code Subject.getSubject(AccessController.getContext())} on Java 17. + */ + public static Subject currentSubject() { + try { + return (Subject) CURRENT.invoke(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + /** + * Execute a {@link Callable} as the given {@link Subject}, equivalent to + * {@code Subject.callAs(subject, callable)} on Java 18+ or + * {@code Subject.doAs(subject, privilegedAction)} on Java 17. + * + * @param subject the subject to run as (may be {@code null}) + * @param callable the action to execute + * @param <T> return type + * @return the callable's result + * @throws Exception if the callable throws + */ + @SuppressWarnings("unchecked") + public static <T> T doAs(Subject subject, Callable<T> callable) throws Exception { + try { + if (USE_CALL_AS) { + return (T) CALL_AS.invoke(subject, callable); + } else { + // Java 17 path: wrap Callable in PrivilegedExceptionAction for Subject.doAs() + return (T) CALL_AS.invoke(subject, + (java.security.PrivilegedExceptionAction<T>) callable::call); + } + } catch (CompletionException e) { + // Subject.callAs wraps in CompletionException + unwrapAndThrow(e.getCause()); + throw e; // unreachable + } catch (java.security.PrivilegedActionException e) { + // Subject.doAs wraps in PrivilegedActionException + unwrapAndThrow(e.getCause()); + throw e; // unreachable + } catch (Exception e) { + throw e; + } catch (Throwable t) { + throw new RuntimeException(t); + } + } + + private static void unwrapAndThrow(Throwable cause) throws Exception { + if (cause instanceof Exception) { + throw (Exception) cause; + } + if (cause instanceof Error) { + throw (Error) cause; + } + throw new RuntimeException(cause); + } + + // --- MethodHandle lookups resolved once at class init --- + + private static boolean probeCallAs() { + try { + MethodHandles.lookup().findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + return true; + } catch (NoSuchMethodException | IllegalAccessException e) { + return false; + } + } + + private static MethodHandle lookupCallAs() { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + // Java 18+: Subject.callAs(Subject, Callable) + return lookup.findStatic(Subject.class, "callAs", + MethodType.methodType(Object.class, Subject.class, Callable.class)); + } catch (NoSuchMethodException e) { + // Java 17: Subject.doAs(Subject, PrivilegedExceptionAction) + try { + return lookup.findStatic(Subject.class, "doAs", + MethodType.methodType(Object.class, Subject.class, + java.security.PrivilegedExceptionAction.class)); + } catch (NoSuchMethodException | IllegalAccessException ex) { + throw new AssertionError(ex); + } + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupCurrent() { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + // Subject.getSubject(AccessControlContext) is deprecated for removal and replaced by + // Subject.current(). + // Lookup first the new API, since for Java versions where both exist, the + // new API delegates to the old API (for example Java 18, 19 and 20). + // Otherwise (Java 17), lookup the old API. + return lookup.findStatic(Subject.class, "current", + MethodType.methodType(Subject.class)); + } catch (NoSuchMethodException e) { + final MethodHandle getContext = lookupGetContext(); + final MethodHandle getSubject = lookupGetSubject(); + return MethodHandles.filterReturnValue(getContext, getSubject); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupGetSubject() { + final MethodHandles.Lookup lookup = MethodHandles.lookup(); + try { + final Class<?> contextClazz = + ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + return lookup.findStatic(Subject.class, "getSubject", + MethodType.methodType(Subject.class, contextClazz)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } + + private static MethodHandle lookupGetContext() { + try { + final Class<?> controllerClazz = + ClassLoader.getSystemClassLoader().loadClass("java.security.AccessController"); + final Class<?> contextClazz = + ClassLoader.getSystemClassLoader() + .loadClass("java.security.AccessControlContext"); + MethodHandles.Lookup lookup = MethodHandles.lookup(); + return lookup.findStatic(controllerClazz, "getContext", + MethodType.methodType(contextClazz)); + } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) { + throw new AssertionError(e); + } + } +} diff --git a/storm-client/src/jvm/org/apache/storm/security/auth/kerberos/KerberosSaslTransportPlugin.java b/storm-client/src/jvm/org/apache/storm/security/auth/kerberos/KerberosSaslTransportPlugin.java index 980ac4478..ab9c7bd4f 100644 --- a/storm-client/src/jvm/org/apache/storm/security/auth/kerberos/KerberosSaslTransportPlugin.java +++ b/storm-client/src/jvm/org/apache/storm/security/auth/kerberos/KerberosSaslTransportPlugin.java @@ -14,8 +14,7 @@ package org.apache.storm.security.auth.kerberos; import java.io.IOException; import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import org.apache.storm.security.auth.SubjectCompat; import java.util.Map; import java.util.Set; import java.util.SortedMap; @@ -200,23 +199,19 @@ public class KerberosSaslTransportPlugin extends SaslTransportPlugin { //open Sasl transport with the login credential try { - Subject.doAs(subject, - new PrivilegedExceptionAction<Void>() { - @Override - public Void run() { - try { - LOG.debug("do as:" + principal); - sasalTransport.open(); - } catch (Exception e) { - LOG.error("Client failed to open SaslClientTransport to interact with a server during " - + "session initiation: " - + e, - e); - } - return null; - } - }); - } catch (PrivilegedActionException e) { + SubjectCompat.doAs(subject, () -> { + try { + LOG.debug("do as:" + principal); + sasalTransport.open(); + } catch (Exception e) { + LOG.error("Client failed to open SaslClientTransport to interact with a server during " + + "session initiation: " + + e, + e); + } + return null; + }); + } catch (Exception e) { throw new RuntimeException(e); } @@ -265,20 +260,19 @@ public class KerberosSaslTransportPlugin extends SaslTransportPlugin { @Override public TTransport getTransport(final TTransport trans) { try { - return Subject.doAs(subject, - (PrivilegedExceptionAction<TTransport>) () -> { - try { - return wrapped.getTransport(trans); - } catch (Exception e) { - LOG.debug("Storm server failed to open transport to interact with a client during " - + "session initiation: " - + e, - e); - return new NoOpTTrasport(null); - } - }); - } catch (PrivilegedActionException e) { - LOG.error("Storm server experienced a PrivilegedActionException exception while creating a transport " + return SubjectCompat.doAs(subject, () -> { + try { + return wrapped.getTransport(trans); + } catch (Exception e) { + LOG.debug("Storm server failed to open transport to interact with a client during " + + "session initiation: " + + e, + e); + return new NoOpTTrasport(null); + } + }); + } catch (Exception e) { + LOG.error("Storm server experienced an exception while creating a transport " + "using a JAAS principal context:" + e, e); diff --git a/storm-client/src/jvm/org/apache/storm/security/auth/workertoken/WorkerTokenClientCallbackHandler.java b/storm-client/src/jvm/org/apache/storm/security/auth/workertoken/WorkerTokenClientCallbackHandler.java index 451c6d806..d85ff0382 100644 --- a/storm-client/src/jvm/org/apache/storm/security/auth/workertoken/WorkerTokenClientCallbackHandler.java +++ b/storm-client/src/jvm/org/apache/storm/security/auth/workertoken/WorkerTokenClientCallbackHandler.java @@ -12,12 +12,12 @@ package org.apache.storm.security.auth.workertoken; -import java.security.AccessController; import java.util.Base64; import javax.security.auth.Subject; import org.apache.storm.generated.WorkerToken; import org.apache.storm.generated.WorkerTokenServiceType; import org.apache.storm.security.auth.ClientAuthUtils; +import org.apache.storm.security.auth.SubjectCompat; import org.apache.storm.security.auth.ThriftConnectionType; import org.apache.storm.security.auth.sasl.SimpleSaslClientCallbackHandler; @@ -49,7 +49,7 @@ public class WorkerTokenClientCallbackHandler extends SimpleSaslClientCallbackHa WorkerTokenServiceType serviceType = type.getWtType(); WorkerToken ret = null; if (serviceType != null) { - Subject subject = Subject.getSubject(AccessController.getContext()); + Subject subject = SubjectCompat.currentSubject(); if (subject != null) { ret = ClientAuthUtils.findWorkerToken(subject, serviceType); } diff --git a/storm-client/src/jvm/org/apache/storm/utils/HadoopLoginUtil.java b/storm-client/src/jvm/org/apache/storm/utils/HadoopLoginUtil.java index de8b417f6..76d3a1a67 100644 --- a/storm-client/src/jvm/org/apache/storm/utils/HadoopLoginUtil.java +++ b/storm-client/src/jvm/org/apache/storm/utils/HadoopLoginUtil.java @@ -20,11 +20,11 @@ package org.apache.storm.utils; import java.lang.reflect.Method; import java.net.UnknownHostException; -import java.security.AccessController; import java.security.PrivilegedAction; import java.util.Map; import javax.security.auth.Subject; import org.apache.storm.Config; +import org.apache.storm.security.auth.SubjectCompat; import org.apache.storm.shade.com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -136,7 +136,7 @@ public class HadoopLoginUtil { Method doAsMethod = ugiClass.getMethod("doAs", PrivilegedAction.class); Object ugi = currentUserMethod.invoke(null); return (Subject) doAsMethod.invoke(ugi, - (PrivilegedAction<Subject>) () -> Subject.getSubject(AccessController.getContext())); + (PrivilegedAction<Subject>) SubjectCompat::currentSubject); } catch (Exception e) { throw new RuntimeException("Error getting hadoop user!", e); } diff --git a/storm-client/test/jvm/org/apache/storm/security/auth/SubjectCompatTest.java b/storm-client/test/jvm/org/apache/storm/security/auth/SubjectCompatTest.java new file mode 100644 index 000000000..f938eab4d --- /dev/null +++ b/storm-client/test/jvm/org/apache/storm/security/auth/SubjectCompatTest.java @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version + * 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package org.apache.storm.security.auth; + +import java.io.IOException; +import javax.security.auth.Subject; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class SubjectCompatTest { + + @Test + void currentSubjectReturnsNullWhenNoneSet() { + assertNull(SubjectCompat.currentSubject()); + } + + @Test + void doAsExecutesCallableAndReturnsResult() throws Exception { + Subject subject = new Subject(); + String result = SubjectCompat.doAs(subject, () -> "hello"); + assertEquals("hello", result); + } + + @Test + void doAsWithNullSubject() throws Exception { + String result = SubjectCompat.doAs(null, () -> "works"); + assertEquals("works", result); + } + + @Test + void doAsPropagatesCheckedException() { + Subject subject = new Subject(); + assertThrows(IOException.class, () -> + SubjectCompat.doAs(subject, () -> { + throw new IOException("test error"); + }) + ); + } + + @Test + void doAsPropagatesRuntimeException() { + Subject subject = new Subject(); + assertThrows(IllegalStateException.class, () -> + SubjectCompat.doAs(subject, () -> { + throw new IllegalStateException("test error"); + }) + ); + } + + @Test + void doAsSetsCurrentSubject() throws Exception { + Subject subject = new Subject(); + Subject observed = SubjectCompat.doAs(subject, SubjectCompat::currentSubject); + assertSame(subject, observed); + } +} diff --git a/storm-server/src/test/java/org/apache/storm/security/auth/AuthTest.java b/storm-server/src/test/java/org/apache/storm/security/auth/AuthTest.java index 0706fe7c4..3a06d52f2 100644 --- a/storm-server/src/test/java/org/apache/storm/security/auth/AuthTest.java +++ b/storm-server/src/test/java/org/apache/storm/security/auth/AuthTest.java @@ -16,8 +16,7 @@ import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.security.Principal; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; +import org.apache.storm.security.auth.SubjectCompat; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -184,8 +183,8 @@ public class AuthTest { } public static void tryConnectAs(Map<String, Object> conf, ThriftServer server, Subject subject, String topoId) - throws PrivilegedActionException { - Subject.doAs(subject, (PrivilegedExceptionAction<Void>) () -> { + throws Exception { + SubjectCompat.doAs(subject, () -> { try (NimbusClient client = NimbusClient.Builder.withConf(conf).withTimeout(NIMBUS_TIMEOUT) .buildWithNimbusHostPort("localhost", server.getPort())) { client.getClient().activate(topoId); //Yes this should be a topo name, but it makes this simpler... @@ -195,7 +194,7 @@ public class AuthTest { } public static Subject testConnectWithTokenFor(WorkerTokenManager wtMan, Map<String, Object> conf, ThriftServer server, - String user, String topoId) throws PrivilegedActionException { + String user, String topoId) throws Exception { WorkerToken wt = wtMan.createOrUpdateTokenFor(WorkerTokenServiceType.NIMBUS, user, topoId); Subject subject = createSubjectWith(wt); tryConnectAs(conf, server, subject, topoId);
