Skip to content

Commit 97ee6d6

Browse files
MGabrrwinch
authored andcommitted
Fix SecurityContext creation for TEST_EXECUTION
Currently, there is support for setting up a SecurityContext after @before by using TestExecutionEvent.TEST_EXECUTION. The current implementation, however, already creates the SecurityContext in @before and just does not set it yet. This leads to issues like #6591. For the case of @WithUserDetails, the creation of the SecurityContext already looks up a user from the repository. If the user was inserted in @before, the user is not found despite using TestExecutionEvent.TEST_EXECUTION. This commit changes the creation of the SecurityContext to happen after @before if using TestExecutionEvent.TEST_EXECUTION. Closes gh-6591
1 parent c71352c commit 97ee6d6

File tree

2 files changed

+46
-20
lines changed

2 files changed

+46
-20
lines changed

test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExecutionListener.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.lang.annotation.Annotation;
1919
import java.lang.reflect.AnnotatedElement;
20+
import java.util.function.Supplier;
2021

2122
import org.springframework.beans.BeanUtils;
2223
import org.springframework.core.GenericTypeResolver;
@@ -69,11 +70,12 @@ public void beforeTestMethod(TestContext testContext) {
6970
return;
7071
}
7172

72-
SecurityContext securityContext = testSecurityContext.securityContext;
73+
Supplier<SecurityContext> supplier = testSecurityContext
74+
.getSecurityContextSupplier();
7375
if (testSecurityContext.getTestExecutionEvent() == TestExecutionEvent.TEST_METHOD) {
74-
TestSecurityContextHolder.setContext(securityContext);
76+
TestSecurityContextHolder.setContext(supplier.get());
7577
} else {
76-
testContext.setAttribute(SECURITY_CONTEXT_ATTR_NAME, securityContext);
78+
testContext.setAttribute(SECURITY_CONTEXT_ATTR_NAME, supplier);
7779
}
7880
}
7981

@@ -83,9 +85,10 @@ public void beforeTestMethod(TestContext testContext) {
8385
*/
8486
@Override
8587
public void beforeTestExecution(TestContext testContext) {
86-
SecurityContext securityContext = (SecurityContext) testContext.removeAttribute(SECURITY_CONTEXT_ATTR_NAME);
87-
if (securityContext != null) {
88-
TestSecurityContextHolder.setContext(securityContext);
88+
Supplier<SecurityContext> supplier = (Supplier<SecurityContext>) testContext
89+
.removeAttribute(SECURITY_CONTEXT_ATTR_NAME);
90+
if (supplier != null) {
91+
TestSecurityContextHolder.setContext(supplier.get());
8992
}
9093
}
9194

@@ -118,14 +121,16 @@ private TestSecurityContext createTestSecurityContext(AnnotatedElement annotated
118121
.resolveTypeArgument(factory.getClass(),
119122
WithSecurityContextFactory.class);
120123
Annotation annotation = findAnnotation(annotated, type);
124+
Supplier<SecurityContext> supplier = () -> {
125+
try {
126+
return factory.createSecurityContext(annotation);
127+
} catch (RuntimeException e) {
128+
throw new IllegalStateException(
129+
"Unable to create SecurityContext using " + annotation, e);
130+
}
131+
};
121132
TestExecutionEvent initialize = withSecurityContext.setupBefore();
122-
try {
123-
return new TestSecurityContext(factory.createSecurityContext(annotation), initialize);
124-
}
125-
catch (RuntimeException e) {
126-
throw new IllegalStateException(
127-
"Unable to create SecurityContext using " + annotation, e);
128-
}
133+
return new TestSecurityContext(supplier, initialize);
129134
}
130135

131136
private Annotation findAnnotation(AnnotatedElement annotated,
@@ -179,16 +184,17 @@ public int getOrder() {
179184
}
180185

181186
static class TestSecurityContext {
182-
private final SecurityContext securityContext;
187+
private final Supplier<SecurityContext> securityContextSupplier;
183188
private final TestExecutionEvent testExecutionEvent;
184189

185-
TestSecurityContext(SecurityContext securityContext, TestExecutionEvent testExecutionEvent) {
186-
this.securityContext = securityContext;
190+
TestSecurityContext(Supplier<SecurityContext> securityContextSupplier,
191+
TestExecutionEvent testExecutionEvent) {
192+
this.securityContextSupplier = securityContextSupplier;
187193
this.testExecutionEvent = testExecutionEvent;
188194
}
189195

190-
public SecurityContext getSecurityContext() {
191-
return this.securityContext;
196+
public Supplier<SecurityContext> getSecurityContextSupplier() {
197+
return this.securityContextSupplier;
192198
}
193199

194200
public TestExecutionEvent getTestExecutionEvent() {

test/src/test/java/org/springframework/security/test/context/support/WithSecurityContextTestExecutionListenerTests.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.junit.Rule;
2222
import org.junit.Test;
2323
import org.junit.runner.RunWith;
24+
import org.mockito.ArgumentCaptor;
25+
import org.mockito.ArgumentMatchers;
2426
import org.mockito.Mock;
2527
import org.mockito.junit.MockitoJUnitRunner;
2628
import org.springframework.beans.factory.annotation.Autowired;
@@ -36,6 +38,7 @@
3638
import org.springframework.test.context.junit4.rules.SpringMethodRule;
3739

3840
import java.lang.reflect.Method;
41+
import java.util.function.Supplier;
3942

4043
import static org.assertj.core.api.Assertions.*;
4144
import static org.mockito.ArgumentMatchers.any;
@@ -102,7 +105,23 @@ public void beforeTestMethodWhenWithMockUserTestExecutionThenTestContextSet() th
102105
this.listener.beforeTestMethod(this.testContext);
103106

104107
assertThat(TestSecurityContextHolder.getContext().getAuthentication()).isNull();
105-
verify(this.testContext).setAttribute(eq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME), any(SecurityContext.class));
108+
verify(this.testContext).setAttribute(eq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME)
109+
, ArgumentMatchers.<Supplier<SecurityContext>>any());
110+
}
111+
112+
@Test
113+
@SuppressWarnings("unchecked")
114+
public void beforeTestMethodWhenWithMockUserTestExecutionThenTestContextSupplierOk() throws Exception {
115+
Method testMethod = TheTest.class.getMethod("withMockUserTestExecution");
116+
when(this.testContext.getApplicationContext()).thenReturn(this.applicationContext);
117+
when(this.testContext.getTestMethod()).thenReturn(testMethod);
118+
119+
this.listener.beforeTestMethod(this.testContext);
120+
121+
ArgumentCaptor<Supplier<SecurityContext>> supplierCaptor = ArgumentCaptor.forClass(Supplier.class);
122+
verify(this.testContext).setAttribute(eq(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME),
123+
supplierCaptor.capture());
124+
assertThat(supplierCaptor.getValue().get().getAuthentication()).isNotNull();
106125
}
107126

108127
@Test
@@ -116,7 +135,8 @@ public void beforeTestExecutionWhenTestContextNullThenSecurityContextNotSet() {
116135
public void beforeTestExecutionWhenTestContextNotNullThenSecurityContextSet() {
117136
SecurityContextImpl securityContext = new SecurityContextImpl();
118137
securityContext.setAuthentication(new TestingAuthenticationToken("user", "passsword", "ROLE_USER"));
119-
when(this.testContext.removeAttribute(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME)).thenReturn(securityContext);
138+
Supplier<SecurityContext> supplier = () -> securityContext;
139+
when(this.testContext.removeAttribute(WithSecurityContextTestExecutionListener.SECURITY_CONTEXT_ATTR_NAME)).thenReturn(supplier);
120140

121141
this.listener.beforeTestExecution(this.testContext);
122142

0 commit comments

Comments
 (0)