Skip to content

Commit 6e1bcfe

Browse files
NerminKarapandzicjzheaux
authored andcommitted
Add argument resolver for SecurityContext
Closes gh-13425
1 parent a808c13 commit 6e1bcfe

File tree

6 files changed

+269
-23
lines changed

6 files changed

+269
-23
lines changed

messaging/src/main/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolver.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,21 @@ public void setAdapterRegistry(ReactiveAdapterRegistry adapterRegistry) {
118118

119119
@Override
120120
public boolean supportsParameter(MethodParameter parameter) {
121-
return findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
121+
return isMonoSecurityContext(parameter)
122+
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
123+
}
124+
125+
private boolean isMonoSecurityContext(MethodParameter parameter) {
126+
boolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());
127+
if (isParameterPublisher) {
128+
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
129+
Class<?> genericType = resolvableType.resolveGeneric(0);
130+
if (genericType == null) {
131+
return false;
132+
}
133+
return SecurityContext.class.isAssignableFrom(genericType);
134+
}
135+
return false;
122136
}
123137

124138
@Override
@@ -136,6 +150,14 @@ public Mono<Object> resolveArgument(MethodParameter parameter, Message<?> messag
136150

137151
private Object resolveSecurityContext(MethodParameter parameter, Object securityContext) {
138152
CurrentSecurityContext contextAnno = findMethodAnnotation(CurrentSecurityContext.class, parameter);
153+
if (contextAnno != null) {
154+
return resolveSecurityContextFromAnnotation(contextAnno, parameter, securityContext);
155+
}
156+
return securityContext;
157+
}
158+
159+
private Object resolveSecurityContextFromAnnotation(CurrentSecurityContext contextAnno, MethodParameter parameter,
160+
Object securityContext) {
139161
String expressionToParse = contextAnno.expression();
140162
if (StringUtils.hasLength(expressionToParse)) {
141163
StandardEvaluationContext context = new StandardEvaluationContext();

messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/CurrentSecurityContextArgumentResolverTests.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,24 @@ public void supportsParameterWhenAuthenticationPrincipalThenTrue() {
4646
assertThat(this.resolver.supportsParameter(arg0("currentSecurityContextOnMonoSecurityContext"))).isTrue();
4747
}
4848

49+
@Test
50+
public void supportsParameterWhenMonoSecurityContextNoAnnotationThenTrue() {
51+
assertThat(this.resolver.supportsParameter(arg0("currentSecurityContextOnMonoSecurityContextNoAnnotation")))
52+
.isTrue();
53+
}
54+
55+
@Test
56+
public void supportsParameterWhenMonoCustomSecurityContextNoAnnotationThenTrue() {
57+
assertThat(
58+
this.resolver.supportsParameter(arg0("currentCustomSecurityContextOnMonoSecurityContextNoAnnotation")))
59+
.isTrue();
60+
}
61+
62+
@Test
63+
public void supportsParameterWhenNoSecurityContextNoAnnotationThenFalse() {
64+
assertThat(this.resolver.supportsParameter(arg0("currentSecurityContextOnMonoStringNoAnnotation"))).isFalse();
65+
}
66+
4967
@Test
5068
public void resolveArgumentWhenAuthenticationPrincipalAndEmptyContextThenNull() {
5169
Object result = this.resolver.resolveArgument(arg0("currentSecurityContextOnMonoSecurityContext"), null)
@@ -67,6 +85,18 @@ public void resolveArgumentWhenAuthenticationPrincipalThenFound() {
6785
private void currentSecurityContextOnMonoSecurityContext(@CurrentSecurityContext Mono<SecurityContext> context) {
6886
}
6987

88+
@SuppressWarnings("unused")
89+
private void currentSecurityContextOnMonoSecurityContextNoAnnotation(Mono<SecurityContext> context) {
90+
}
91+
92+
@SuppressWarnings("unused")
93+
private void currentCustomSecurityContextOnMonoSecurityContextNoAnnotation(Mono<CustomSecurityContext> context) {
94+
}
95+
96+
@SuppressWarnings("unused")
97+
private void currentSecurityContextOnMonoStringNoAnnotation(Mono<String> context) {
98+
}
99+
70100
@Test
71101
public void supportsParameterWhenCurrentUserThenTrue() {
72102
assertThat(this.resolver.supportsParameter(arg0("currentUserOnMonoUserDetails"))).isTrue();
@@ -110,6 +140,41 @@ public void supportsParameterWhenNotAnnotatedThenFalse() {
110140
private void monoUserDetails(Mono<UserDetails> user) {
111141
}
112142

143+
@Test
144+
public void supportsParameterWhenSecurityContextNotAnnotatedThenTrue() {
145+
assertThat(this.resolver.supportsParameter(arg0("monoSecurityContext"))).isTrue();
146+
}
147+
148+
@Test
149+
public void resolveArgumentWhenMonoSecurityContextNoAnnotationThenFound() {
150+
Authentication authentication = TestAuthentication.authenticatedUser();
151+
Mono<SecurityContext> result = (Mono<SecurityContext>) this.resolver
152+
.resolveArgument(arg0("monoSecurityContext"), null)
153+
.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))
154+
.block();
155+
assertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());
156+
}
157+
158+
@SuppressWarnings("unused")
159+
private void monoSecurityContext(Mono<SecurityContext> securityContext) {
160+
}
161+
162+
@Test
163+
public void resolveArgumentWhenMonoCustomSecurityContextNoAnnotationThenFound() {
164+
Authentication authentication = TestAuthentication.authenticatedUser();
165+
CustomSecurityContext securityContext = new CustomSecurityContext();
166+
securityContext.setAuthentication(authentication);
167+
Mono<CustomSecurityContext> result = (Mono<CustomSecurityContext>) this.resolver
168+
.resolveArgument(arg0("monoCustomSecurityContext"), null)
169+
.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)))
170+
.block();
171+
assertThat(result.block().getAuthentication().getPrincipal()).isEqualTo(authentication.getPrincipal());
172+
}
173+
174+
@SuppressWarnings("unused")
175+
private void monoCustomSecurityContext(Mono<CustomSecurityContext> securityContext) {
176+
}
177+
113178
private MethodParameter arg0(String methodName) {
114179
ResolvableMethod method = ResolvableMethod.on(getClass()).named(methodName).method();
115180
return new SynthesizingMethodParameter(method.method(), 0);
@@ -121,4 +186,20 @@ private MethodParameter arg0(String methodName) {
121186

122187
}
123188

189+
static class CustomSecurityContext implements SecurityContext {
190+
191+
private Authentication authentication;
192+
193+
@Override
194+
public Authentication getAuthentication() {
195+
return this.authentication;
196+
}
197+
198+
@Override
199+
public void setAuthentication(Authentication authentication) {
200+
this.authentication = authentication;
201+
}
202+
203+
}
204+
124205
}

web/src/main/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolver.java

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ public final class CurrentSecurityContextArgumentResolver implements HandlerMeth
8585

8686
@Override
8787
public boolean supportsParameter(MethodParameter parameter) {
88-
return findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
88+
return SecurityContext.class.isAssignableFrom(parameter.getParameterType())
89+
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
8990
}
9091

9192
@Override
@@ -95,26 +96,12 @@ public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer m
9596
if (securityContext == null) {
9697
return null;
9798
}
98-
Object securityContextResult = securityContext;
9999
CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
100-
String expressionToParse = annotation.expression();
101-
if (StringUtils.hasLength(expressionToParse)) {
102-
StandardEvaluationContext context = new StandardEvaluationContext();
103-
context.setRootObject(securityContext);
104-
context.setVariable("this", securityContext);
105-
context.setBeanResolver(this.beanResolver);
106-
Expression expression = this.parser.parseExpression(expressionToParse);
107-
securityContextResult = expression.getValue(context);
108-
}
109-
if (securityContextResult != null
110-
&& !parameter.getParameterType().isAssignableFrom(securityContextResult.getClass())) {
111-
if (annotation.errorOnInvalidType()) {
112-
throw new ClassCastException(
113-
securityContextResult + " is not assignable to " + parameter.getParameterType());
114-
}
115-
return null;
100+
if (annotation != null) {
101+
return resolveSecurityContextFromAnnotation(parameter, annotation, securityContext);
116102
}
117-
return securityContextResult;
103+
104+
return securityContext;
118105
}
119106

120107
/**
@@ -137,6 +124,29 @@ public void setBeanResolver(BeanResolver beanResolver) {
137124
this.beanResolver = beanResolver;
138125
}
139126

127+
private Object resolveSecurityContextFromAnnotation(MethodParameter parameter, CurrentSecurityContext annotation,
128+
SecurityContext securityContext) {
129+
Object securityContextResult = securityContext;
130+
String expressionToParse = annotation.expression();
131+
if (StringUtils.hasLength(expressionToParse)) {
132+
StandardEvaluationContext context = new StandardEvaluationContext();
133+
context.setRootObject(securityContext);
134+
context.setVariable("this", securityContext);
135+
context.setBeanResolver(this.beanResolver);
136+
Expression expression = this.parser.parseExpression(expressionToParse);
137+
securityContextResult = expression.getValue(context);
138+
}
139+
if (securityContextResult != null
140+
&& !parameter.getParameterType().isAssignableFrom(securityContextResult.getClass())) {
141+
if (annotation.errorOnInvalidType()) {
142+
throw new ClassCastException(
143+
securityContextResult + " is not assignable to " + parameter.getParameterType());
144+
}
145+
return null;
146+
}
147+
return securityContextResult;
148+
}
149+
140150
/**
141151
* Obtain the specified {@link Annotation} on the specified {@link MethodParameter}.
142152
* @param annotationClass the class of the {@link Annotation} to find on the

web/src/main/java/org/springframework/security/web/reactive/result/method/annotation/CurrentSecurityContextArgumentResolver.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,21 @@ public void setBeanResolver(BeanResolver beanResolver) {
6767

6868
@Override
6969
public boolean supportsParameter(MethodParameter parameter) {
70-
return findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
70+
return isMonoSecurityContext(parameter)
71+
|| findMethodAnnotation(CurrentSecurityContext.class, parameter) != null;
72+
}
73+
74+
private boolean isMonoSecurityContext(MethodParameter parameter) {
75+
boolean isParameterPublisher = Publisher.class.isAssignableFrom(parameter.getParameterType());
76+
if (isParameterPublisher) {
77+
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
78+
Class<?> genericType = resolvableType.resolveGeneric(0);
79+
if (genericType == null) {
80+
return false;
81+
}
82+
return SecurityContext.class.isAssignableFrom(genericType);
83+
}
84+
return false;
7185
}
7286

7387
@Override
@@ -95,6 +109,14 @@ public Mono<Object> resolveArgument(MethodParameter parameter, BindingContext bi
95109
*/
96110
private Object resolveSecurityContext(MethodParameter parameter, SecurityContext securityContext) {
97111
CurrentSecurityContext annotation = findMethodAnnotation(CurrentSecurityContext.class, parameter);
112+
if (annotation != null) {
113+
return resolveSecurityContextFromAnnotation(annotation, parameter, securityContext);
114+
}
115+
return securityContext;
116+
}
117+
118+
private Object resolveSecurityContextFromAnnotation(CurrentSecurityContext annotation, MethodParameter parameter,
119+
Object securityContext) {
98120
Object securityContextResult = securityContext;
99121
String expressionToParse = annotation.expression();
100122
if (StringUtils.hasLength(expressionToParse)) {

web/src/test/java/org/springframework/security/web/method/annotation/CurrentSecurityContextArgumentResolverTests.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,26 @@ public void cleanup() {
6969
SecurityContextHolder.clearContext();
7070
}
7171

72+
@Test
73+
public void supportsParameterNoAnnotationWrongType() {
74+
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotationTypeMismatch())).isFalse();
75+
}
76+
7277
@Test
7378
public void supportsParameterNoAnnotation() {
74-
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotation())).isFalse();
79+
assertThat(this.resolver.supportsParameter(showSecurityContextNoAnnotation())).isTrue();
80+
}
81+
82+
@Test
83+
public void supportsParameterCustomSecurityContextNoAnnotation() {
84+
assertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))
85+
.isTrue();
86+
}
87+
88+
@Test
89+
public void supportsParameterNoAnnotationCustomType() {
90+
assertThat(this.resolver.supportsParameter(showSecurityContextWithCustomSecurityContextNoAnnotation()))
91+
.isTrue();
7592
}
7693

7794
@Test
@@ -88,6 +105,24 @@ public void resolveArgumentWithCustomSecurityContext() {
88105
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
89106
}
90107

108+
@Test
109+
public void resolveArgumentWithCustomSecurityContextNoAnnotation() {
110+
String principal = "custom_security_context";
111+
setAuthenticationPrincipalWithCustomSecurityContext(principal);
112+
CustomSecurityContext customSecurityContext = (CustomSecurityContext) this.resolver
113+
.resolveArgument(showSecurityContextWithCustomSecurityContextNoAnnotation(), null, null, null);
114+
assertThat(customSecurityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
115+
}
116+
117+
@Test
118+
public void resolveArgumentWithNoAnnotation() {
119+
String principal = "custom_security_context";
120+
setAuthenticationPrincipal(principal);
121+
SecurityContext securityContext = (SecurityContext) this.resolver
122+
.resolveArgument(showSecurityContextNoAnnotation(), null, null, null);
123+
assertThat(securityContext.getAuthentication().getPrincipal()).isEqualTo(principal);
124+
}
125+
91126
@Test
92127
public void resolveArgumentWithCustomSecurityContextTypeMatch() {
93128
String principal = "custom_security_context_type_match";
@@ -212,10 +247,14 @@ public void metaAnnotationWhenCurrentSecurityWithErrorOnInvalidTypeThenMisMatch(
212247
.resolveArgument(showCurrentSecurityWithErrorOnInvalidTypeMisMatch(), null, null, null));
213248
}
214249

215-
private MethodParameter showSecurityContextNoAnnotation() {
250+
private MethodParameter showSecurityContextNoAnnotationTypeMismatch() {
216251
return getMethodParameter("showSecurityContextNoAnnotation", String.class);
217252
}
218253

254+
private MethodParameter showSecurityContextNoAnnotation() {
255+
return getMethodParameter("showSecurityContextNoAnnotation", SecurityContext.class);
256+
}
257+
219258
private MethodParameter showSecurityContextAnnotation() {
220259
return getMethodParameter("showSecurityContextAnnotation", SecurityContext.class);
221260
}
@@ -276,6 +315,11 @@ public MethodParameter showCurrentSecurityWithErrorOnInvalidTypeMisMatch() {
276315
return getMethodParameter("showCurrentSecurityWithErrorOnInvalidTypeMisMatch", String.class);
277316
}
278317

318+
public MethodParameter showSecurityContextWithCustomSecurityContextNoAnnotation() {
319+
return getMethodParameter("showSecurityContextWithCustomSecurityContextNoAnnotation",
320+
CustomSecurityContext.class);
321+
}
322+
279323
private MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {
280324
Method method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);
281325
return new MethodParameter(method, 0);
@@ -358,6 +402,12 @@ public void showCurrentSecurityWithErrorOnInvalidTypeMisMatch(
358402
@CurrentSecurityWithErrorOnInvalidType String typeMisMatch) {
359403
}
360404

405+
public void showSecurityContextNoAnnotation(SecurityContext context) {
406+
}
407+
408+
public void showSecurityContextWithCustomSecurityContextNoAnnotation(CustomSecurityContext context) {
409+
}
410+
361411
}
362412

363413
static class CustomSecurityContext implements SecurityContext {

0 commit comments

Comments
 (0)