Skip to content

Commit d4a6a73

Browse files
committed
Correct order of authentication resolvers
Closes gh-982
1 parent e369b12 commit d4a6a73

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,16 +277,18 @@ private HandlerMethodArgumentResolverComposite initArgumentResolvers() {
277277
resolvers.addResolver(new ArgumentsMethodArgumentResolver(argumentBinder));
278278
resolvers.addResolver(new ContextValueMethodArgumentResolver());
279279
resolvers.addResolver(new LocalContextValueMethodArgumentResolver());
280+
if (springSecurityPresent) {
281+
ApplicationContext context = obtainApplicationContext();
282+
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
283+
}
280284

281285
// Type based
282286
resolvers.addResolver(new DataFetchingEnvironmentMethodArgumentResolver());
283287
resolvers.addResolver(new DataLoaderMethodArgumentResolver());
284288
addSubrangeMethodArgumentResolver(resolvers);
285289
addSortMethodArgumentResolver(resolvers);
286290
if (springSecurityPresent) {
287-
ApplicationContext context = obtainApplicationContext();
288291
resolvers.addResolver(new PrincipalMethodArgumentResolver());
289-
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
290292
}
291293
if (KotlinDetector.isKotlinPresent()) {
292294
resolvers.addResolver(new ContinuationHandlerMethodArgumentResolver());

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

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
import org.springframework.graphql.execution.ErrorType;
4545
import org.springframework.lang.Nullable;
4646
import org.springframework.security.authentication.TestingAuthenticationToken;
47+
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
4748
import org.springframework.security.core.Authentication;
49+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
4850
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
4951
import org.springframework.security.core.context.SecurityContextHolder;
5052
import org.springframework.security.core.context.SecurityContextImpl;
@@ -79,7 +81,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests {
7981

8082
@Test
8183
void supportsParameter() {
82-
Method method = ClassUtils.getMethod(SchemaMappingPrincipalMethodArgumentResolverTests.class, "handle", (Class<?>[]) null);
84+
Method method = ClassUtils.getMethod(getClass(), "handle", (Class<?>[]) null);
8385
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 0))).isTrue();
8486
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 1))).isTrue();
8587
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 2))).isFalse();
@@ -121,10 +123,10 @@ void nullablePrincipalDoesntRequireSecurityContext() {
121123
@Test
122124
void nonNullPrincipalRequiresSecurityContext() {
123125
DataFetcherExceptionResolver exceptionResolver =
124-
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
125-
.message("Resolved error: " + ex.getMessage())
126-
.errorType(ErrorType.UNAUTHORIZED)
127-
.build());
126+
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
127+
.message("Resolved error: " + ex.getMessage())
128+
.errorType(ErrorType.UNAUTHORIZED)
129+
.build());
128130

129131
Mono<ExecutionGraphQlResponse> responseMono = executeAsync(
130132
"type Query { greetingMono: String }", "{ greetingMono }",
@@ -217,20 +219,47 @@ private void testSubscription(Function<Context, Context> contextModifier) {
217219

218220
}
219221

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

225253
private Mono<ExecutionGraphQlResponse> executeAsync(
226-
String schema, String document, Function<Context, Context> contextWriter, @Nullable DataFetcherExceptionResolver exceptionResolver) {
254+
String schema, String document, Function<Context, Context> contextWriter,
255+
@Nullable DataFetcherExceptionResolver exceptionResolver) {
227256

228257
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
229258
context.registerBean(GreetingController.class, () -> greetingController);
230259
context.refresh();
231260

232-
GraphQlSetup graphQlSetup = GraphQlSetup.schemaContent(schema)
233-
.runtimeWiringForAnnotatedControllers(context);
261+
GraphQlSetup graphQlSetup =
262+
GraphQlSetup.schemaContent(schema).runtimeWiringForAnnotatedControllers(context);
234263

235264
if (exceptionResolver != null) {
236265
graphQlSetup.exceptionResolver(exceptionResolver);
@@ -288,6 +317,22 @@ Flux<String> greetingSubscription(Principal principal) {
288317
return Flux.just("Hello", "Hi");
289318
}
290319

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

293338
}

0 commit comments

Comments
 (0)