Skip to content

Commit 6350664

Browse files
committed
Correct order of authentication resolvers
Closes gh-982
1 parent ac720eb commit 6350664

File tree

2 files changed

+57
-10
lines changed

2 files changed

+57
-10
lines changed

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,18 @@ protected HandlerMethodArgumentResolverComposite initArgumentResolvers() {
190190
resolvers.addResolver(new ArgumentsMethodArgumentResolver(argumentBinder));
191191
resolvers.addResolver(new ContextValueMethodArgumentResolver());
192192
resolvers.addResolver(new LocalContextValueMethodArgumentResolver());
193+
if (springSecurityPresent) {
194+
ApplicationContext context = obtainApplicationContext();
195+
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
196+
}
193197

194198
// Type based
195199
resolvers.addResolver(new DataFetchingEnvironmentMethodArgumentResolver());
196200
resolvers.addResolver(new DataLoaderMethodArgumentResolver());
197201
addSubrangeMethodArgumentResolver(resolvers);
198202
addSortMethodArgumentResolver(resolvers);
199203
if (springSecurityPresent) {
200-
ApplicationContext context = obtainApplicationContext();
201204
resolvers.addResolver(new PrincipalMethodArgumentResolver());
202-
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
203205
}
204206
if (KotlinDetector.isKotlinPresent()) {
205207
resolvers.addResolver(new ContinuationHandlerMethodArgumentResolver());

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java

+53-8
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@
4545
import org.springframework.graphql.execution.ErrorType;
4646
import org.springframework.lang.Nullable;
4747
import org.springframework.security.authentication.TestingAuthenticationToken;
48+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
4849
import org.springframework.security.core.Authentication;
50+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
4951
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
5052
import org.springframework.security.core.context.SecurityContextHolder;
5153
import org.springframework.security.core.context.SecurityContextImpl;
@@ -82,7 +84,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests {
8284

8385
@Test
8486
void supportsParameter() {
85-
Method method = ClassUtils.getMethod(SchemaMappingPrincipalMethodArgumentResolverTests.class, "handle", (Class<?>[]) null);
87+
Method method = ClassUtils.getMethod(getClass(), "handle", (Class<?>[]) null);
8688
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 0))).isTrue();
8789
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 1))).isTrue();
8890
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 2))).isFalse();
@@ -124,10 +126,10 @@ void nullablePrincipalDoesntRequireSecurityContext() {
124126
@Test
125127
void nonNullPrincipalRequiresSecurityContext() {
126128
DataFetcherExceptionResolver exceptionResolver =
127-
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
128-
.message("Resolved error: " + ex.getMessage())
129-
.errorType(ErrorType.UNAUTHORIZED)
130-
.build());
129+
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
130+
.message("Resolved error: " + ex.getMessage())
131+
.errorType(ErrorType.UNAUTHORIZED)
132+
.build());
131133

132134
Mono<ExecutionGraphQlResponse> responseMono = executeAsync(
133135
"type Query { greetingMono: String }", "{ greetingMono }",
@@ -220,20 +222,47 @@ private void testSubscription(Function<Context, Context> contextModifier) {
220222

221223
}
222224

225+
226+
@Nested
227+
class AuthenticationPrincipalTests {
228+
229+
@Test // gh-982
230+
void query() {
231+
Authentication authentication = new UsernamePasswordAuthenticationToken(new GraphQlPrincipal(), null);
232+
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
233+
try {
234+
String field = "greetingAuthenticationPrincipal";
235+
Mono<ExecutionGraphQlResponse> responseMono = executeAsync(
236+
"type Query { " + field + " : String }", "{ " + field + " }", threadLocalContextWriter);
237+
238+
String greeting = ResponseHelper.forResponse(responseMono).toEntity(field, String.class);
239+
assertThat(greeting).isEqualTo("Hello");
240+
assertThat(greetingController.principal()).isSameAs(authentication.getPrincipal());
241+
}
242+
finally {
243+
SecurityContextHolder.clearContext();
244+
}
245+
}
246+
247+
}
248+
249+
223250
private Mono<ExecutionGraphQlResponse> executeAsync(
224251
String schema, String document, Function<Context, Context> contextWriter) {
252+
225253
return executeAsync(schema, document, contextWriter, null);
226254
}
227255

228256
private Mono<ExecutionGraphQlResponse> executeAsync(
229-
String schema, String document, Function<Context, Context> contextWriter, @Nullable DataFetcherExceptionResolver exceptionResolver) {
257+
String schema, String document, Function<Context, Context> contextWriter,
258+
@Nullable DataFetcherExceptionResolver exceptionResolver) {
230259

231260
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
232261
context.registerBean(GreetingController.class, () -> greetingController);
233262
context.refresh();
234263

235-
GraphQlSetup graphQlSetup = GraphQlSetup.schemaContent(schema)
236-
.runtimeWiringForAnnotatedControllers(context);
264+
GraphQlSetup graphQlSetup =
265+
GraphQlSetup.schemaContent(schema).runtimeWiringForAnnotatedControllers(context);
237266

238267
if (exceptionResolver != null) {
239268
graphQlSetup.exceptionResolver(exceptionResolver);
@@ -291,6 +320,22 @@ Flux<String> greetingSubscription(Principal principal) {
291320
return Flux.just("Hello", "Hi");
292321
}
293322

323+
@QueryMapping
324+
String greetingAuthenticationPrincipal(@AuthenticationPrincipal GraphQlPrincipal principal) {
325+
this.principal = principal;
326+
return "Hello";
327+
}
328+
329+
}
330+
331+
332+
private static final class GraphQlPrincipal implements Principal {
333+
334+
@Override
335+
public String getName() {
336+
return "";
337+
}
338+
294339
}
295340

296341
}

0 commit comments

Comments
 (0)