From 82f8ec41dd9cc8c83c0d001d022aa09f3893cc65 Mon Sep 17 00:00:00 2001 From: Darran Lofthouse Date: Thu, 11 Mar 2021 13:02:20 +0000 Subject: [PATCH 1/4] Add a test case to test security propagation. --- .../smallrye/context/test/SecurityTest.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/src/test/java/io/smallrye/context/test/SecurityTest.java diff --git a/tests/src/test/java/io/smallrye/context/test/SecurityTest.java b/tests/src/test/java/io/smallrye/context/test/SecurityTest.java new file mode 100644 index 00000000..6f823a0f --- /dev/null +++ b/tests/src/test/java/io/smallrye/context/test/SecurityTest.java @@ -0,0 +1,122 @@ +package io.smallrye.context.test; + +import static org.junit.Assert.assertSame; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.security.auth.Subject; + +import org.eclipse.microprofile.context.ManagedExecutor; +import org.eclipse.microprofile.context.ThreadContext; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import io.smallrye.context.SmallRyeManagedExecutor; + +/** + * The most basic level of security propagation could be the propagation of both a JAAS Subject, + * and AccessControlContext - before vendor specific representations are added these are the + * original approaches available. + * + * This test adds tests to verify a Subject can be successfully propagated. + */ +public class SecurityTest { + + private static Subject identity; + private ExecutorService executorService; + + @BeforeClass + public static void setupSubject() { + Subject identity = new Subject(); + identity.setReadOnly(); + + // We don't need content as we can verify the instance was propagated. + SecurityTest.identity = identity; + } + + @AfterClass + public static void clearSubject() { + identity = null; + } + + @Before + public void createExecutor() throws Exception { + executorService = Executors.newSingleThreadExecutor(); + // Ensure we initialise the initial Thread so we don't accidentally + // capture the AccessControlContext. + Future result = executorService.submit(() -> { + }); + result.get(); + } + + @After + public void shutDownExecutor() { + executorService.shutdown(); + executorService = null; + } + + @Test + public void testManagedExecutor() { + Subject.doAs(identity, (PrivilegedAction) () -> { + _testManagedExecutor(); + return null; + }); + } + + private void _testManagedExecutor() { + assertCorrectSubject(); + + ManagedExecutor executor = SmallRyeManagedExecutor.builder() + .withExecutorService(executorService) + .propagated(ThreadContext.SECURITY) + .build(); + + CompletableFuture future = executor.runAsync(this::assertCorrectSubject); + future.join(); + + executor.shutdown(); + } + + private void assertCorrectSubject() { + AccessControlContext accessControllContext = AccessController.getContext(); + Subject currentSubject = Subject.getSubject(accessControllContext); + + assertSame("Same Subject", identity, currentSubject); + } + + @Test + public void testThreadContext() throws Exception { + Subject.doAs(identity, (PrivilegedExceptionAction) () -> { + _testThreadContext(); + return null; + }); + } + + private void _testThreadContext() throws InterruptedException, ExecutionException { + assertCorrectSubject(); + + ThreadContext threadContext = ThreadContext.builder() + .propagated(ThreadContext.SECURITY) + .build(); + + Runnable runnable = threadContext.contextualRunnable(this::assertCorrectSubject); + + Future future = executorService.submit(runnable); + future.get(); + } + + // TODO Opposite Tests, i.e. Threads with an AccessControlContext and Subject should be + // cleared for execution. + +} From 80f0134b9270624ca6701979a8586a9e0e27b8e3 Mon Sep 17 00:00:00 2001 From: Darran Lofthouse Date: Fri, 6 Mar 2020 17:27:45 +0000 Subject: [PATCH 2/4] Extend the ThreadContextSnapshot API to make it possible to wrap the task being executed. --- .../spi/WrappingThreadContextSnapshot.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 api/src/main/java/io/smallrye/context/spi/WrappingThreadContextSnapshot.java diff --git a/api/src/main/java/io/smallrye/context/spi/WrappingThreadContextSnapshot.java b/api/src/main/java/io/smallrye/context/spi/WrappingThreadContextSnapshot.java new file mode 100644 index 00000000..2ed265f7 --- /dev/null +++ b/api/src/main/java/io/smallrye/context/spi/WrappingThreadContextSnapshot.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * Licensed 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 io.smallrye.context.spi; + +import java.util.concurrent.Callable; + +import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; + +/** + * An extension of {code ThreadContextSnapshot} which enables the snapshot to + * perform propagation by wrapping the task. + * + * @author Darran Lofthouse + */ +@FunctionalInterface +public interface WrappingThreadContextSnapshot extends ThreadContextSnapshot { + + /** + * Does this snapshot need to wrap the underlying task instead of directly + * manipulating ThreadLocals. + * + * @return {@code true} if this snapshot needs to wrap the underlying task. + */ + default boolean needsToWrap() { + return false; + } + + /** + * Wrap the provided task to ensure the context being propagated is correctly + * established and cleared as the underlying task is called. + * + * @param task the task to wrap. + * @return the wrapped task. + */ + default Callable wrap(Callable callable) { + return callable; + } + +} From fb68b772e7d2ea0b4a965c2b7584ae0495068f72 Mon Sep 17 00:00:00 2001 From: Darran Lofthouse Date: Thu, 11 Mar 2021 17:36:16 +0000 Subject: [PATCH 3/4] Enable wrapping of the functional interface being called. --- .../smallrye/context/CleanAutoCloseable.java | 6 ++-- .../context/SmallRyeThreadContext.java | 32 +++++++++++++------ .../context/impl/CapturedContextState.java | 4 ++- .../context/impl/SlowActiveContextState.java | 25 +++++++++++++-- .../impl/SlowCapturedContextState.java | 9 +++--- .../java/io/smallrye/context/impl/Util.java | 10 ++++++ .../wrappers/SlowContextualBiConsumer.java | 5 ++- .../wrappers/SlowContextualBiFunction.java | 4 +-- .../impl/wrappers/SlowContextualCallable.java | 4 +-- .../impl/wrappers/SlowContextualConsumer.java | 5 ++- .../impl/wrappers/SlowContextualExecutor.java | 5 ++- .../impl/wrappers/SlowContextualFunction.java | 4 +-- .../impl/wrappers/SlowContextualRunnable.java | 5 ++- .../impl/wrappers/SlowContextualSupplier.java | 4 +-- 14 files changed, 90 insertions(+), 32 deletions(-) create mode 100644 core/src/main/java/io/smallrye/context/impl/Util.java diff --git a/core/src/main/java/io/smallrye/context/CleanAutoCloseable.java b/core/src/main/java/io/smallrye/context/CleanAutoCloseable.java index ca7c0d48..6df2e712 100644 --- a/core/src/main/java/io/smallrye/context/CleanAutoCloseable.java +++ b/core/src/main/java/io/smallrye/context/CleanAutoCloseable.java @@ -3,8 +3,10 @@ /** * AutoCloseable interface which doesn't throw. */ -@FunctionalInterface -public interface CleanAutoCloseable extends AutoCloseable { +public interface CleanAutoCloseable extends AutoCloseable { + + T callNoChecked(); + /** * Close this resource, no exception thrown. */ diff --git a/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java b/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java index e59897ba..64c7aea6 100644 --- a/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java +++ b/core/src/main/java/io/smallrye/context/SmallRyeThreadContext.java @@ -68,6 +68,12 @@ public class SmallRyeThreadContext implements ThreadContext { .threadLocal(SmallRyeThreadContextStorageDeclaration.class); private final static CleanAutoCloseable NULL_THREAD_STATE = new CleanAutoCloseable() { + + @Override + public Object callNoChecked() { + throw new IllegalStateException(); + } + @Override public void close() { currentThreadContext.remove(); @@ -84,7 +90,7 @@ public void execute(Runnable command) { /** * Updates the current @{link SmallRyeThreadContext} in use by the current thread, and returns an * object suitable for use in try-with-resource to restore the previous value. - * + * * @param threadContext the @{link SmallRyeThreadContext} to use * @return an object suitable for use in try-with-resource to restore the previous value. */ @@ -96,6 +102,12 @@ public static CleanAutoCloseable withThreadContext(SmallRyeThreadContext threadC return NULL_THREAD_STATE; } else { return new CleanAutoCloseable() { + + @Override + public Object callNoChecked() { + throw new IllegalStateException(); + } + @Override public void close() { currentThreadContext.set(oldValue); @@ -108,7 +120,7 @@ public void close() { /** * Invokes the given @{link Runnable} with the current @{link SmallRyeThreadContext} updated to the given value * for the current thread. - * + * * @param threadContext the @{link SmallRyeThreadContext} to use * @param f the @{link Runnable} to invoke */ @@ -129,7 +141,7 @@ public static void withThreadContext(SmallRyeThreadContext threadContext, Runnab /** * Returns the current thread's @{link SmallRyeThreadContext} if set, or a @{link SmallRyeThreadContext} * which propagates all contexts. - * + * * @return the current thread's @{link SmallRyeThreadContext} if set, or a @{link SmallRyeThreadContext} * which propagates all contexts. */ @@ -140,7 +152,7 @@ public static SmallRyeThreadContext getCurrentThreadContextOrPropagatedContexts( /** * Returns the current thread's @{link SmallRyeThreadContext} if set, or a @{link SmallRyeThreadContext} * which clears all contexts. - * + * * @return the current thread's @{link SmallRyeThreadContext} if set, or a @{link SmallRyeThreadContext} * which clears all contexts. */ @@ -151,7 +163,7 @@ public static SmallRyeThreadContext getCurrentThreadContextOrClearedContexts() { /** * Returns the current thread's @{link SmallRyeThreadContext} if set, or the given @{link SmallRyeThreadContext} * default value. - * + * * @param defaultValue the default value to use * @return the current thread's @{link SmallRyeThreadContext} if set, or the given @{link SmallRyeThreadContext} * default value. @@ -163,7 +175,7 @@ public static SmallRyeThreadContext getCurrentThreadContext(SmallRyeThreadContex /** * Returns the current thread's @{link SmallRyeThreadContext} if set, or null. - * + * * @return the current thread's @{link SmallRyeThreadContext} if set, or null. */ public static SmallRyeThreadContext getCurrentThreadContext() { @@ -200,7 +212,7 @@ public ExecutorService getDefaultExecutor() { /** * Returns true if this thread context has no context to propagate nor clear, and so * will not contextualise anything. - * + * * @return true if this thread context has no context to propagate nor clear */ public boolean isEmpty() { @@ -209,7 +221,7 @@ public boolean isEmpty() { /** * Returns true if the given lambda instance is already contextualized - * + * * @param lambda the lambda to test * @return true if the given lambda instance is already contextualized */ @@ -236,7 +248,7 @@ public static Builder builder() { * or the default executor service as set by * {@link SmallRyeContextManager.Builder#withDefaultExecutorService(ExecutorService)}, * or otherwise have no default executor. - * + * * If this thread context has no default executor, the new stage and all dependent stages created from it, and so forth, * have no default asynchronous execution facility and must raise {@link java.lang.UnsupportedOperationException} * for all *Async methods that do not specify an executor. For example, @@ -284,7 +296,7 @@ public CompletableFuture withContextCapture(CompletableFuture future, * or the default executor service as set by * {@link SmallRyeContextManager.Builder#withDefaultExecutorService(ExecutorService)}, * or otherwise have no default executor. - * + * * If this thread context has no default executor, the new stage and all dependent stages created from it, and so forth, * and/or cleared as described in the documentation of the {@link ManagedExecutor} class, except that * this ThreadContext instance takes the place of the default asynchronous execution facility in diff --git a/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java b/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java index 0df1faad..c5889a5e 100644 --- a/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java +++ b/core/src/main/java/io/smallrye/context/impl/CapturedContextState.java @@ -1,8 +1,10 @@ package io.smallrye.context.impl; +import java.util.concurrent.Callable; + import io.smallrye.context.CleanAutoCloseable; @FunctionalInterface public interface CapturedContextState { - CleanAutoCloseable begin(); + CleanAutoCloseable begin(Callable callable); } diff --git a/core/src/main/java/io/smallrye/context/impl/SlowActiveContextState.java b/core/src/main/java/io/smallrye/context/impl/SlowActiveContextState.java index 3b2c9ab4..0fde879c 100644 --- a/core/src/main/java/io/smallrye/context/impl/SlowActiveContextState.java +++ b/core/src/main/java/io/smallrye/context/impl/SlowActiveContextState.java @@ -1,36 +1,55 @@ package io.smallrye.context.impl; import java.util.List; +import java.util.concurrent.Callable; import org.eclipse.microprofile.context.spi.ThreadContextController; import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; import io.smallrye.context.CleanAutoCloseable; import io.smallrye.context.SmallRyeThreadContext; +import io.smallrye.context.spi.WrappingThreadContextSnapshot; /** * Restores a context and allows you to clean it up (unrestore it). */ -public class SlowActiveContextState implements CleanAutoCloseable { +public class SlowActiveContextState implements CleanAutoCloseable { private final ThreadContextController[] activeContext; private final CleanAutoCloseable activeThreadContext; + private final Callable callable; /** * Restores a previously captured context. - * + * * @param threadContext the thread context * @param threadContextSnapshots the captured snapshots */ - public SlowActiveContextState(SmallRyeThreadContext threadContext, List threadContextSnapshots) { + public SlowActiveContextState(SmallRyeThreadContext threadContext, List threadContextSnapshots, + Callable callable) { activeContext = new ThreadContextController[threadContextSnapshots.size()]; int i = 0; for (ThreadContextSnapshot threadContextSnapshot : threadContextSnapshots) { activeContext[i++] = threadContextSnapshot.begin(); + if (threadContextSnapshot instanceof WrappingThreadContextSnapshot + && ((WrappingThreadContextSnapshot) threadContextSnapshot).needsToWrap()) { + callable = ((WrappingThreadContextSnapshot) threadContextSnapshot).wrap(callable); + } } + this.callable = callable; activeThreadContext = SmallRyeThreadContext.withThreadContext(threadContext); } + @Override + public T callNoChecked() { + try { + return callable.call(); + } catch (Exception e) { + Util.rethrow(e); + return null; + } + } + /** * Unrestores / clean-up a previously restored context. */ diff --git a/core/src/main/java/io/smallrye/context/impl/SlowCapturedContextState.java b/core/src/main/java/io/smallrye/context/impl/SlowCapturedContextState.java index 676bdf80..970395d5 100644 --- a/core/src/main/java/io/smallrye/context/impl/SlowCapturedContextState.java +++ b/core/src/main/java/io/smallrye/context/impl/SlowCapturedContextState.java @@ -1,6 +1,7 @@ package io.smallrye.context.impl; import java.util.List; +import java.util.concurrent.Callable; import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; @@ -17,7 +18,7 @@ public class SlowCapturedContextState implements CapturedContextState { /** * Captures the current context according to the given ThreadContext - * + * * @param threadContext the thread context */ public SlowCapturedContextState(SmallRyeThreadContext threadContext) { @@ -27,10 +28,10 @@ public SlowCapturedContextState(SmallRyeThreadContext threadContext) { /** * Restores the captured context and returns an instance that can unrestore (cleanup) it. - * + * * @return the captured context state */ - public SlowActiveContextState begin() { - return new SlowActiveContextState(threadContext, threadContextSnapshots); + public SlowActiveContextState begin(Callable callable) { + return new SlowActiveContextState(threadContext, threadContextSnapshots, callable); } } diff --git a/core/src/main/java/io/smallrye/context/impl/Util.java b/core/src/main/java/io/smallrye/context/impl/Util.java new file mode 100644 index 00000000..938e9ee7 --- /dev/null +++ b/core/src/main/java/io/smallrye/context/impl/Util.java @@ -0,0 +1,10 @@ +package io.smallrye.context.impl; + +/** + * @author Kabir Khan + */ +public class Util { + public static void rethrow(Throwable t) throws T { + throw (T) t; + } +} \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiConsumer.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiConsumer.java index 5e6fbbef..1e0ba9dd 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiConsumer.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiConsumer.java @@ -17,8 +17,11 @@ public SlowContextualBiConsumer(CapturedContextState state, BiConsumer con @Override public void accept(T t, U u) { - try (CleanAutoCloseable activeState = state.begin()) { + try (CleanAutoCloseable activeState = state.begin(() -> { consumer.accept(t, u); + return null; + })) { + activeState.callNoChecked(); } } } \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiFunction.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiFunction.java index ad6c04c1..7d76df48 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiFunction.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualBiFunction.java @@ -17,8 +17,8 @@ public SlowContextualBiFunction(CapturedContextState state, BiFunction @Override public R apply(T t, U u) { - try (CleanAutoCloseable activeState = state.begin()) { - return function.apply(t, u); + try (CleanAutoCloseable activeState = state.begin(() -> function.apply(t, u))) { + return activeState.callNoChecked(); } } } \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualCallable.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualCallable.java index 398144ed..23989b6e 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualCallable.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualCallable.java @@ -17,8 +17,8 @@ public SlowContextualCallable(CapturedContextState state, Callable callable) @Override public R call() throws Exception { - try (CleanAutoCloseable activeState = state.begin()) { - return callable.call(); + try (CleanAutoCloseable activeState = state.begin(callable)) { + return activeState.callNoChecked(); } } } \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualConsumer.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualConsumer.java index 63200f43..e5731681 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualConsumer.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualConsumer.java @@ -17,8 +17,11 @@ public SlowContextualConsumer(CapturedContextState state, Consumer consumer) @Override public void accept(T t) { - try (CleanAutoCloseable activeState = state.begin()) { + try (CleanAutoCloseable activeState = state.begin(() -> { consumer.accept(t); + return null; + })) { + activeState.callNoChecked(); } } } \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualExecutor.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualExecutor.java index 897e596a..3285504f 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualExecutor.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualExecutor.java @@ -15,8 +15,11 @@ public SlowContextualExecutor(CapturedContextState state) { @Override public void execute(Runnable command) { - try (CleanAutoCloseable foo = state.begin()) { + try (CleanAutoCloseable foo = state.begin(() -> { command.run(); + return null; + })) { + foo.callNoChecked(); } } diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualFunction.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualFunction.java index 7329e519..8f7b78eb 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualFunction.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualFunction.java @@ -17,8 +17,8 @@ public SlowContextualFunction(CapturedContextState state, Function functio @Override public R apply(T t) { - try (CleanAutoCloseable activeState = state.begin()) { - return function.apply(t); + try (CleanAutoCloseable activeState = state.begin(() -> function.apply(t))) { + return activeState.callNoChecked(); } } } \ No newline at end of file diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualRunnable.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualRunnable.java index 9c7ee6b5..34225755 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualRunnable.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualRunnable.java @@ -15,8 +15,11 @@ public SlowContextualRunnable(CapturedContextState state, Runnable runnable) { @Override public void run() { - try (CleanAutoCloseable activeState = state.begin()) { + try (CleanAutoCloseable activeState = state.begin(() -> { runnable.run(); + return null; + })) { + activeState.callNoChecked(); } } } diff --git a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualSupplier.java b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualSupplier.java index 84f27ee2..5d5359ab 100644 --- a/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualSupplier.java +++ b/core/src/main/java/io/smallrye/context/impl/wrappers/SlowContextualSupplier.java @@ -17,8 +17,8 @@ public SlowContextualSupplier(CapturedContextState state, Supplier supplier) @Override public R get() { - try (CleanAutoCloseable activeState = state.begin()) { - return supplier.get(); + try (CleanAutoCloseable activeState = state.begin(supplier::get)) { + return activeState.callNoChecked(); } } } From 1373c8cfd1af94af87f319dbdfefde8a21df25f0 Mon Sep 17 00:00:00 2001 From: Darran Lofthouse Date: Thu, 11 Mar 2021 14:35:42 +0000 Subject: [PATCH 4/4] Add a new SecurityContextProvider to satisfy ThreadContext.SECURITY --- pom.xml | 1 + security/pom.xml | 60 +++++++++++++ .../propagation/SecurityContextProvider.java | 84 +++++++++++++++++++ ...oprofile.context.spi.ThreadContextProvider | 1 + tests/pom.xml | 6 ++ .../context/test/CompletableFutureTest.java | 2 +- 6 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 security/pom.xml create mode 100644 security/src/main/java/io/smallrye/context/security/context/propagation/SecurityContextProvider.java create mode 100644 security/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider diff --git a/pom.xml b/pom.xml index ec5e0a0e..44b22628 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,7 @@ application propagators-rxjava1 propagators-rxjava2 + security tests tck api diff --git a/security/pom.xml b/security/pom.xml new file mode 100644 index 00000000..e4d3dc64 --- /dev/null +++ b/security/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + io.smallrye + smallrye-context-propagation-parent + 1.1.1-SNAPSHOT + + smallrye-context-propagation-security + smallrye-context-propagation-security + + + + ${project.groupId} + smallrye-context-propagation-api + ${project.version} + + + junit + junit + test + + + org.eclipse.microprofile.context-propagation + microprofile-context-propagation-api + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + + + + + coverage + + @{jacocoArgLine} + + + + + org.jacoco + jacoco-maven-plugin + + + + + + diff --git a/security/src/main/java/io/smallrye/context/security/context/propagation/SecurityContextProvider.java b/security/src/main/java/io/smallrye/context/security/context/propagation/SecurityContextProvider.java new file mode 100644 index 00000000..ec96e47c --- /dev/null +++ b/security/src/main/java/io/smallrye/context/security/context/propagation/SecurityContextProvider.java @@ -0,0 +1,84 @@ +package io.smallrye.context.security.context.propagation; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.eclipse.microprofile.context.ThreadContext; +import org.eclipse.microprofile.context.spi.ThreadContextController; +import org.eclipse.microprofile.context.spi.ThreadContextProvider; +import org.eclipse.microprofile.context.spi.ThreadContextSnapshot; + +import io.smallrye.context.spi.WrappingThreadContextSnapshot; + +public class SecurityContextProvider implements ThreadContextProvider { + + private static final ThreadContextController NOOP_THREAD_CONTEXT_CONTROLLER = new ThreadContextController() { + @Override + public void endContext() throws IllegalStateException { + } + }; + + @Override + public ThreadContextSnapshot currentContext(Map props) { + return new SecurityThreadContextSnapshot(true); + } + + @Override + public ThreadContextSnapshot clearedContext(Map props) { + return new SecurityThreadContextSnapshot(false); + } + + @Override + public String getThreadContextType() { + return ThreadContext.SECURITY; + } + + static final class SecurityThreadContextSnapshot implements WrappingThreadContextSnapshot { + + private final AccessControlContext accessControlContext; + + SecurityThreadContextSnapshot(final boolean capture) { + if (capture) { + accessControlContext = AccessController.getContext(); + } else { + accessControlContext = null; + } + } + + @Override + public ThreadContextController begin() { + return NOOP_THREAD_CONTEXT_CONTROLLER; + } + + @Override + public boolean needsToWrap() { + return accessControlContext != null; + } + + @Override + public Callable wrap(Callable callable) { + if (accessControlContext == null) { + return callable; + } + + return new Callable() { + + @Override + public T call() throws Exception { + try { + return AccessController.doPrivilegedWithCombiner((PrivilegedExceptionAction) callable::call, + accessControlContext); + } catch (PrivilegedActionException e) { + throw e.getException(); + } + } + }; + } + + } + +} diff --git a/security/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider b/security/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider new file mode 100644 index 00000000..c250a3d5 --- /dev/null +++ b/security/src/main/resources/META-INF/services/org.eclipse.microprofile.context.spi.ThreadContextProvider @@ -0,0 +1 @@ +io.smallrye.context.security.context.propagation.SecurityContextProvider \ No newline at end of file diff --git a/tests/pom.xml b/tests/pom.xml index ac0bdec6..e5788499 100644 --- a/tests/pom.xml +++ b/tests/pom.xml @@ -78,6 +78,12 @@ tests test-jar + + ${project.groupId} + smallrye-context-propagation-security + ${project.version} + test + org.jboss.weld.se weld-se-shaded diff --git a/tests/src/test/java/io/smallrye/context/test/CompletableFutureTest.java b/tests/src/test/java/io/smallrye/context/test/CompletableFutureTest.java index aaf6531d..faea13a1 100644 --- a/tests/src/test/java/io/smallrye/context/test/CompletableFutureTest.java +++ b/tests/src/test/java/io/smallrye/context/test/CompletableFutureTest.java @@ -297,7 +297,7 @@ public void testExistingCFWrapping() throws InterruptedException, ExecutionExcep SmallRyeThreadContext threadContext = managedExecutor.getThreadContext(); Assert.assertNotNull(threadContext); ThreadContextProviderPlan plan = threadContext.getPlan(); - Assert.assertEquals(4, plan.clearedProviders.size()); + Assert.assertEquals(5, plan.clearedProviders.size()); Assert.assertTrue(plan.unchangedProviders.isEmpty()); Assert.assertEquals(1, plan.propagatedProviders.size());