Skip to content

Commit

Permalink
Correct order of authentication resolvers
Browse files Browse the repository at this point in the history
Closes gh-982
  • Loading branch information
rstoyanchev committed Jun 18, 2024
1 parent e369b12 commit d4a6a73
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -277,16 +277,18 @@ private HandlerMethodArgumentResolverComposite initArgumentResolvers() {
resolvers.addResolver(new ArgumentsMethodArgumentResolver(argumentBinder));
resolvers.addResolver(new ContextValueMethodArgumentResolver());
resolvers.addResolver(new LocalContextValueMethodArgumentResolver());
if (springSecurityPresent) {
ApplicationContext context = obtainApplicationContext();
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
}

// Type based
resolvers.addResolver(new DataFetchingEnvironmentMethodArgumentResolver());
resolvers.addResolver(new DataLoaderMethodArgumentResolver());
addSubrangeMethodArgumentResolver(resolvers);
addSortMethodArgumentResolver(resolvers);
if (springSecurityPresent) {
ApplicationContext context = obtainApplicationContext();
resolvers.addResolver(new PrincipalMethodArgumentResolver());
resolvers.addResolver(new AuthenticationPrincipalArgumentResolver(new BeanFactoryResolver(context)));
}
if (KotlinDetector.isKotlinPresent()) {
resolvers.addResolver(new ContinuationHandlerMethodArgumentResolver());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@
import org.springframework.graphql.execution.ErrorType;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
Expand Down Expand Up @@ -79,7 +81,7 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests {

@Test
void supportsParameter() {
Method method = ClassUtils.getMethod(SchemaMappingPrincipalMethodArgumentResolverTests.class, "handle", (Class<?>[]) null);
Method method = ClassUtils.getMethod(getClass(), "handle", (Class<?>[]) null);
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 0))).isTrue();
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 1))).isTrue();
assertThat(this.resolver.supportsParameter(new MethodParameter(method, 2))).isFalse();
Expand Down Expand Up @@ -121,10 +123,10 @@ void nullablePrincipalDoesntRequireSecurityContext() {
@Test
void nonNullPrincipalRequiresSecurityContext() {
DataFetcherExceptionResolver exceptionResolver =
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
.message("Resolved error: " + ex.getMessage())
.errorType(ErrorType.UNAUTHORIZED)
.build());
DataFetcherExceptionResolver.forSingleError((ex, env) -> GraphqlErrorBuilder.newError(env)
.message("Resolved error: " + ex.getMessage())
.errorType(ErrorType.UNAUTHORIZED)
.build());

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

}


@Nested
class AuthenticationPrincipalTests {

@Test // gh-982
void query() {
Authentication authentication = new UsernamePasswordAuthenticationToken(new GraphQlPrincipal(), null);
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
try {
String field = "greetingAuthenticationPrincipal";
Mono<ExecutionGraphQlResponse> responseMono = executeAsync(
"type Query { " + field + " : String }", "{ " + field + " }", threadLocalContextWriter);

String greeting = ResponseHelper.forResponse(responseMono).toEntity(field, String.class);
assertThat(greeting).isEqualTo("Hello");
assertThat(greetingController.principal()).isSameAs(authentication.getPrincipal());
}
finally {
SecurityContextHolder.clearContext();
}
}

}


private Mono<ExecutionGraphQlResponse> executeAsync(
String schema, String document, Function<Context, Context> contextWriter) {

return executeAsync(schema, document, contextWriter, null);
}

private Mono<ExecutionGraphQlResponse> executeAsync(
String schema, String document, Function<Context, Context> contextWriter, @Nullable DataFetcherExceptionResolver exceptionResolver) {
String schema, String document, Function<Context, Context> contextWriter,
@Nullable DataFetcherExceptionResolver exceptionResolver) {

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

GraphQlSetup graphQlSetup = GraphQlSetup.schemaContent(schema)
.runtimeWiringForAnnotatedControllers(context);
GraphQlSetup graphQlSetup =
GraphQlSetup.schemaContent(schema).runtimeWiringForAnnotatedControllers(context);

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

@QueryMapping
String greetingAuthenticationPrincipal(@AuthenticationPrincipal GraphQlPrincipal principal) {
this.principal = principal;
return "Hello";
}

}


private static final class GraphQlPrincipal implements Principal {

@Override
public String getName() {
return "";
}

}

}

0 comments on commit d4a6a73

Please sign in to comment.