SEC-3031: DelegatingSecurityContext(Runnable|Callable) only modify SecurityContext on new Thread
Modifying the SecurityContext on the same Thread can cause issues. For example, with a RejectedExecutionHandler the SecurityContext may be cleared out on the original Thread. This change modifies both the DelegatingSecurityContextRunnable and DelegatingSecurityContextCallable to, by default, only modify the SecurityContext if they are invoked on a new Thread. The behavior can be changed by setting the property enableOnOrigionalThread to true.
This commit is contained in:
@@ -17,6 +17,9 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -45,6 +48,8 @@ public class DelegatingSecurityContextCallableTests {
|
||||
|
||||
private Callable<Object> callable;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("serial")
|
||||
public void setUp() throws Exception {
|
||||
@@ -55,6 +60,7 @@ public class DelegatingSecurityContextCallableTests {
|
||||
return super.answer(invocation);
|
||||
}
|
||||
});
|
||||
executor = Executors.newFixedThreadPool(1);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -90,7 +96,7 @@ public class DelegatingSecurityContextCallableTests {
|
||||
public void call() throws Exception {
|
||||
callable = new DelegatingSecurityContextCallable<Object>(delegate,
|
||||
securityContext);
|
||||
assertWrapped(callable.call());
|
||||
assertWrapped(callable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,6 +105,23 @@ public class DelegatingSecurityContextCallableTests {
|
||||
callable = new DelegatingSecurityContextCallable<Object>(delegate);
|
||||
SecurityContextHolder.clearContext(); // ensure callable is what sets up the
|
||||
// SecurityContextHolder
|
||||
assertWrapped(callable);
|
||||
}
|
||||
|
||||
// SEC-3031
|
||||
@Test
|
||||
public void callOnSameThread() throws Exception {
|
||||
callable = new DelegatingSecurityContextCallable<Object>(delegate,
|
||||
securityContext);
|
||||
securityContext = SecurityContextHolder.createEmptyContext();
|
||||
assertWrapped(callable.call());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnSameThreadExplicitlyEnabled() throws Exception {
|
||||
DelegatingSecurityContextCallable<Object> callable = new DelegatingSecurityContextCallable<Object>(delegate,
|
||||
securityContext);
|
||||
callable.setEnableOnOriginalThread(true);
|
||||
assertWrapped(callable.call());
|
||||
}
|
||||
|
||||
@@ -120,13 +143,13 @@ public class DelegatingSecurityContextCallableTests {
|
||||
callable = DelegatingSecurityContextCallable.create(delegate, null);
|
||||
SecurityContextHolder.clearContext(); // ensure callable is what sets up the
|
||||
// SecurityContextHolder
|
||||
assertWrapped(callable.call());
|
||||
assertWrapped(callable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create() throws Exception {
|
||||
callable = DelegatingSecurityContextCallable.create(delegate, securityContext);
|
||||
assertWrapped(callable.call());
|
||||
assertWrapped(callable);
|
||||
}
|
||||
|
||||
// --- toString
|
||||
@@ -139,8 +162,12 @@ public class DelegatingSecurityContextCallableTests {
|
||||
assertThat(callable.toString()).isEqualTo(delegate.toString());
|
||||
}
|
||||
|
||||
private void assertWrapped(Object actualResult) throws Exception {
|
||||
assertThat(actualResult).isEqualTo(callableResult);
|
||||
private void assertWrapped(Callable<Object> callable) throws Exception {
|
||||
Future<Object> submit = executor.submit(callable);
|
||||
assertWrapped(submit.get());
|
||||
}
|
||||
|
||||
private void assertWrapped(Object callableResult) throws Exception {
|
||||
verify(delegate).call();
|
||||
assertThat(SecurityContextHolder.getContext()).isEqualTo(
|
||||
SecurityContextHolder.createEmptyContext());
|
||||
|
||||
@@ -16,6 +16,10 @@ import static org.fest.assertions.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -24,6 +28,8 @@ import org.mockito.Mock;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.springframework.core.task.SyncTaskExecutor;
|
||||
import org.springframework.core.task.support.ExecutorServiceAdapter;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@@ -43,6 +49,8 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
|
||||
private Runnable runnable;
|
||||
|
||||
private ExecutorService executor;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
doAnswer(new Answer<Object>() {
|
||||
@@ -51,6 +59,8 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
return null;
|
||||
}
|
||||
}).when(delegate).run();
|
||||
|
||||
executor = Executors.newFixedThreadPool(1);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -85,8 +95,7 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
@Test
|
||||
public void call() throws Exception {
|
||||
runnable = new DelegatingSecurityContextRunnable(delegate, securityContext);
|
||||
runnable.run();
|
||||
assertWrapped();
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,8 +104,26 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
runnable = new DelegatingSecurityContextRunnable(delegate);
|
||||
SecurityContextHolder.clearContext(); // ensure runnable is what sets up the
|
||||
// SecurityContextHolder
|
||||
runnable.run();
|
||||
assertWrapped();
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
// SEC-3031
|
||||
@Test
|
||||
public void callOnSameThread() throws Exception {
|
||||
executor = synchronousExecutor();
|
||||
runnable = new DelegatingSecurityContextRunnable(delegate,
|
||||
securityContext);
|
||||
securityContext = SecurityContextHolder.createEmptyContext();
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callOnSameThreadExplicitlyEnabled() throws Exception {
|
||||
executor = synchronousExecutor();
|
||||
DelegatingSecurityContextRunnable runnable = new DelegatingSecurityContextRunnable(delegate,
|
||||
securityContext);
|
||||
runnable.setEnableOnOriginalThread(true);
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
// --- create ---
|
||||
@@ -112,20 +139,18 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createNullSecurityContext() {
|
||||
public void createNullSecurityContext() throws Exception {
|
||||
SecurityContextHolder.setContext(securityContext);
|
||||
runnable = DelegatingSecurityContextRunnable.create(delegate, null);
|
||||
SecurityContextHolder.clearContext(); // ensure runnable is what sets up the
|
||||
// SecurityContextHolder
|
||||
runnable.run();
|
||||
assertWrapped();
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void create() {
|
||||
public void create() throws Exception {
|
||||
runnable = DelegatingSecurityContextRunnable.create(delegate, securityContext);
|
||||
runnable.run();
|
||||
assertWrapped();
|
||||
assertWrapped(runnable);
|
||||
}
|
||||
|
||||
// --- toString
|
||||
@@ -137,9 +162,15 @@ public class DelegatingSecurityContextRunnableTests {
|
||||
assertThat(runnable.toString()).isEqualTo(delegate.toString());
|
||||
}
|
||||
|
||||
private void assertWrapped() {
|
||||
private void assertWrapped(Runnable runnable) throws Exception {
|
||||
Future<?> submit = executor.submit(runnable);
|
||||
submit.get();
|
||||
verify(delegate).run();
|
||||
assertThat(SecurityContextHolder.getContext()).isEqualTo(
|
||||
SecurityContextHolder.createEmptyContext());
|
||||
}
|
||||
|
||||
private static ExecutorService synchronousExecutor() {
|
||||
return new ExecutorServiceAdapter(new SyncTaskExecutor());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user