From 54e1710bb52492b9d2a4d110cd1b3119b5988e08 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 23 May 2024 17:13:34 +0100 Subject: [PATCH 01/11] Update Samples section of reference See gh-208 --- spring-graphql-docs/modules/ROOT/pages/samples.adoc | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/spring-graphql-docs/modules/ROOT/pages/samples.adoc b/spring-graphql-docs/modules/ROOT/pages/samples.adoc index 43b2327e..2cc35469 100644 --- a/spring-graphql-docs/modules/ROOT/pages/samples.adoc +++ b/spring-graphql-docs/modules/ROOT/pages/samples.adoc @@ -1,15 +1,4 @@ [[samples]] = Samples -Check out the https://spring.io/guides/gs/graphql-server/["Building a GraphQL service"] Getting Started Guide. - -In addition, the 1.0.x branch of this repository contains {github-10x-branch}/samples[sample applications] for various scenarios. -Those samples do not exist in the `main` branch and are planned to be -https://github.com/spring-projects/spring-graphql/issues/208[moved out] into a separate repository. -To run those samples, check out the 1.0.x branch run their main application classes from your IDE, -or from the command line: - -[source,bash,indent=0,subs="verbatim,quotes"] ----- -$ ./gradlew :samples:{sample-directory-name}:bootRun ----- \ No newline at end of file +Please, see the [spring-graphql-examples](https://github.com/spring-projects/spring-graphql-examples) repository. From 46b03c3291669e596bd57d3a30cd2c2f0fe93708 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 23 May 2024 17:30:27 +0100 Subject: [PATCH 02/11] Fix link to samples See gh-208 --- spring-graphql-docs/antora.yml | 3 ++- spring-graphql-docs/modules/ROOT/pages/samples.adoc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spring-graphql-docs/antora.yml b/spring-graphql-docs/antora.yml index b5049c4a..e0b55fd7 100644 --- a/spring-graphql-docs/antora.yml +++ b/spring-graphql-docs/antora.yml @@ -29,4 +29,5 @@ asciidoc: javadoc: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/api # version attributes from main build.gradle spring-framework-ref-docs: https://docs.spring.io/spring-framework/reference - spring-boot-ref-docs: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/html \ No newline at end of file + spring-boot-ref-docs: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference/html + examples-repo: https://github.com/spring-projects/spring-graphql-examples \ No newline at end of file diff --git a/spring-graphql-docs/modules/ROOT/pages/samples.adoc b/spring-graphql-docs/modules/ROOT/pages/samples.adoc index 2cc35469..57be3266 100644 --- a/spring-graphql-docs/modules/ROOT/pages/samples.adoc +++ b/spring-graphql-docs/modules/ROOT/pages/samples.adoc @@ -1,4 +1,4 @@ [[samples]] = Samples -Please, see the [spring-graphql-examples](https://github.com/spring-projects/spring-graphql-examples) repository. +Please, see the {examples-repo}[spring-graphql-examples] repository. From 3175b3a19cedad985134600cc510c77f5945d8f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 May 2024 10:02:45 +0000 Subject: [PATCH 03/11] Update Antora Spring UI to v0.4.15 --- spring-graphql-docs/antora-playbook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-graphql-docs/antora-playbook.yml b/spring-graphql-docs/antora-playbook.yml index 6d3617e8..4aa60296 100644 --- a/spring-graphql-docs/antora-playbook.yml +++ b/spring-graphql-docs/antora-playbook.yml @@ -34,7 +34,7 @@ urls: redirect_facility: httpd ui: bundle: - url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.3.3/ui-bundle.zip + url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip snapshot: true runtime: log: From e369b12aa91d1ab10b65392417dc86552ff9b15b Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 13 Jun 2024 16:03:00 +0100 Subject: [PATCH 04/11] Polishing in AuthenticationPrincipalArgumentResolverTests --- ...icationPrincipalArgumentResolverTests.java | 118 ++++++++---------- 1 file changed, 54 insertions(+), 64 deletions(-) diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolverTests.java index 0aac0ad8..0a3703cb 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,6 @@ import reactor.util.context.Context; import org.springframework.core.DefaultParameterNameDiscoverer; import org.springframework.core.MethodParameter; -import org.springframework.core.ResolvableType; import org.springframework.core.annotation.SynthesizingMethodParameter; import org.springframework.graphql.data.method.annotation.Argument; import org.springframework.graphql.data.method.annotation.QueryMapping; @@ -54,26 +53,10 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat; * Tests for {@link AuthenticationPrincipalArgumentResolver}. * * @author Rob Winch + * @author Rossen Stoyanchev */ class AuthenticationPrincipalArgumentResolverTests { - private static final Class STRING_CLASS = String.class; - - private static final Class USER_DETAILS_CLASS = UserDetails.class; - - private static final Class MONO_USER_DETAILS_CLASS = - ResolvableType.forClassWithGenerics(Mono.class, UserDetails.class).getRawClass(); - - private static final Class MONO_STRING_CLASS = - ResolvableType.forClassWithGenerics(Mono.class, String.class).getRawClass(); - - private static final Class PUBLISHER_USER_DETAILS_CLASS = - ResolvableType.forClassWithGenerics(Publisher.class, UserDetails.class).getRawClass(); - - private static final Class TESTPUBLISHER_USER_DETAILS_CLASS = - ResolvableType.forClassWithGenerics(TestPublisher.class, UserDetails.class).getRawClass(); - - private final AuthenticationPrincipalArgumentResolver resolver = new AuthenticationPrincipalArgumentResolver((beanName, context) -> new PrincipalConverter()); @@ -83,48 +66,49 @@ class AuthenticationPrincipalArgumentResolverTests { SecurityContextHolder.clearContext(); } + @Test void supportsParameterWhenNoAnnotation() { - MethodParameter parameter = firstParameter(UserController.class, "noParameter", USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "noParameter", UserDetails.class); assertThat(this.resolver.supportsParameter(parameter)).isFalse(); } @Test void supportsParameterWhenWrongAnnotation() { - MethodParameter parameter = firstParameter(UserController.class, "argument", USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "argument", UserDetails.class); assertThat(this.resolver.supportsParameter(parameter)).isFalse(); } @Test void supportsParameterWhenCurrentUser() { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", UserDetails.class); assertThat(this.resolver.supportsParameter(parameter)).isTrue(); } @Test void supportsParameterWhenAuthenticationPrincipal() { - MethodParameter parameter = firstParameter(UserController.class, "userDetails", USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "userDetails", UserDetails.class); assertThat(this.resolver.supportsParameter(parameter)).isTrue(); } @Test void resolveArgumentWhenAuthenticationPrincipal() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "userDetails", USER_DETAILS_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "userDetails", UserDetails.class); + Mono details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenCurrentUser() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", UserDetails.class); + Mono details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenNoSecurityContext() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", UserDetails.class); + Mono details = resolveArgument(parameter); assertThat(details.block()).isNull(); } @@ -133,82 +117,82 @@ class AuthenticationPrincipalArgumentResolverTests { SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(usernamePasswordAuthentication()); SecurityContextHolder.setContext(context); - MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", UserDetails.class); + Mono details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenAuthenticationPrincipalUsername() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "username", STRING_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "username", String.class); + Mono details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block()).isEqualTo("user"); } @Test void resolveArgumentWhenBeanName() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "beanName", USER_DETAILS_CLASS); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "beanName", UserDetails.class); + Mono details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenErrorOnInvalidType() throws Exception { MethodParameter parameter = firstParameter(UserController.class, "errorOnInvalidType", String.class); - Mono details = (Mono) this.resolver.resolveArgument(parameter, null); + Mono details = resolveArgument(parameter); assertThatExceptionOfType(ClassCastException.class) .isThrownBy(() -> details.contextWrite(authenticationContext()).block()); } @Test void resolveArgumentWhenInvalidType() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "invalidType", STRING_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "invalidType", String.class); + Mono> details = resolveArgument(parameter); assertThat(details.flatMap(u -> u).contextWrite(authenticationContext()).block()).isNull(); } @Test void supportsParameterWhenMonoNoAnnotation() { - MethodParameter parameter = firstParameter(UserController.class, "noParameter", MONO_USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "noParameter", Mono.class); assertThat(this.resolver.supportsParameter(parameter)).isFalse(); } @Test void supportsParameterWhenMonoWrongAnnotation() { - MethodParameter parameter = firstParameter(UserController.class, "argument", MONO_USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "argument", Mono.class); assertThat(this.resolver.supportsParameter(parameter)).isFalse(); } @Test void supportsParameterWhenMonoCurrentUser() { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", MONO_USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", Mono.class); assertThat(this.resolver.supportsParameter(parameter)).isTrue(); } @Test void supportsParameterWhenMonoAuthenticationPrincipal() { - MethodParameter parameter = firstParameter(UserController.class, "userDetails", MONO_USER_DETAILS_CLASS); + MethodParameter parameter = firstParameter(UserController.class, "userDetails", Mono.class); assertThat(this.resolver.supportsParameter(parameter)).isTrue(); } @Test void resolveArgumentWhenMonoAuthenticationPrincipal() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "userDetails", MONO_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "userDetails", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenMonoCurrentUser() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", MONO_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenMonoNoSecurityContext() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "currentUser", MONO_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().block()).isNull(); } @@ -217,58 +201,58 @@ class AuthenticationPrincipalArgumentResolverTests { SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(usernamePasswordAuthentication()); SecurityContextHolder.setContext(context); - MethodParameter parameter = firstParameter(UserController.class, "currentUser", MONO_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "currentUser", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenMonoAuthenticationPrincipalUsername() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "username", MONO_STRING_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "username", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().contextWrite(authenticationContext()).block()).isEqualTo("user"); } @Test void resolveArgumentWhenMonoBeanName() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "beanName", MONO_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "beanName", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.flatMap(u -> u).contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenMonoErrorOnInvalidType() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "errorOnInvalidType", MONO_STRING_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "errorOnInvalidType", Mono.class); + Mono> details = resolveArgument(parameter); assertThatExceptionOfType(ClassCastException.class) .isThrownBy(() -> details.flatMap(u -> u).contextWrite(authenticationContext()).block()); } @Test void resolveArgumentWhenMonoInvalidType() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "invalidType", MONO_STRING_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "invalidType", Mono.class); + Mono> details = resolveArgument(parameter); assertThat(details.flatMap(u -> u).contextWrite(authenticationContext()).block()).isNull(); } @Test void resolveArgumentWhenPublisher() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "publisher", PUBLISHER_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "publisher", Publisher.class); + Mono> details = resolveArgument(parameter); assertThat(details.block().contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user"); } @Test void resolveArgumentWhenTestPublisherThenEmptyMono() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "publisher", TESTPUBLISHER_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "publisher", TestPublisher.class); + Mono> details = resolveArgument(parameter); assertThat(details.contextWrite(authenticationContext()).block()).isNull(); } @Test void resolveArgumentWhenTestPublisherAndErrorOnInvalidType() throws Exception { - MethodParameter parameter = firstParameter(UserController.class, "errorOnInvalidType", TESTPUBLISHER_USER_DETAILS_CLASS); - Mono> details = (Mono>) this.resolver.resolveArgument(parameter, null); + MethodParameter parameter = firstParameter(UserController.class, "errorOnInvalidType", TestPublisher.class); + Mono> details = resolveArgument(parameter); assertThatExceptionOfType(ClassCastException.class) .isThrownBy(() -> details.flatMap(u -> u).contextWrite(authenticationContext()).block()); } @@ -284,6 +268,11 @@ class AuthenticationPrincipalArgumentResolverTests { return parameter; } + @SuppressWarnings({"unchecked", "DataFlowIssue"}) + private T resolveArgument(MethodParameter parameter) throws Exception { + return (T) this.resolver.resolveArgument(parameter, null); + } + private static Function authenticationContext() { return (context) -> ReactiveSecurityContextHolder.withAuthentication(usernamePasswordAuthentication()); } @@ -297,6 +286,7 @@ class AuthenticationPrincipalArgumentResolverTests { return new User("user", "password", AuthorityUtils.createAuthorityList("ROLE_USER")); } + static class PrincipalConverter { public UserDetails convert(UserDetails userDetails) { return userDetails; From d4a6a73b1d027e267aa2275ab352373902a51360 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 18 Jun 2024 07:18:31 +0100 Subject: [PATCH 05/11] Correct order of authentication resolvers Closes gh-982 --- .../AnnotatedControllerConfigurer.java | 6 +- ...gPrincipalMethodArgumentResolverTests.java | 61 ++++++++++++++++--- 2 files changed, 57 insertions(+), 10 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index 720d949c..58ead114 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -277,6 +277,10 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I 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()); @@ -284,9 +288,7 @@ public class AnnotatedControllerConfigurer implements ApplicationContextAware, I 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()); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java index 49e4e949..bc44dad2 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/SchemaMappingPrincipalMethodArgumentResolverTests.java @@ -44,7 +44,9 @@ import org.springframework.graphql.execution.DataFetcherExceptionResolver; 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; @@ -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(); @@ -121,10 +123,10 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { @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 responseMono = executeAsync( "type Query { greetingMono: String }", "{ greetingMono }", @@ -217,20 +219,47 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { } + + @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 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 executeAsync( String schema, String document, Function contextWriter) { + return executeAsync(schema, document, contextWriter, null); } private Mono executeAsync( - String schema, String document, Function contextWriter, @Nullable DataFetcherExceptionResolver exceptionResolver) { + String schema, String document, Function 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); @@ -288,6 +317,22 @@ public class SchemaMappingPrincipalMethodArgumentResolverTests { 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 ""; + } + } } From ce1dcfcb9d7bcc1e0241c86bd8b1952ff57410db Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 17 Jun 2024 10:03:16 +0000 Subject: [PATCH 06/11] Update Antora Spring UI to v0.4.16 --- spring-graphql-docs/antora-playbook.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-graphql-docs/antora-playbook.yml b/spring-graphql-docs/antora-playbook.yml index 4aa60296..6b8d0175 100644 --- a/spring-graphql-docs/antora-playbook.yml +++ b/spring-graphql-docs/antora-playbook.yml @@ -34,7 +34,7 @@ urls: redirect_facility: httpd ui: bundle: - url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.15/ui-bundle.zip + url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.16/ui-bundle.zip snapshot: true runtime: log: From b979f7901fdc831febcfff4adbf28c1ed66c089d Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 18 Jun 2024 16:20:46 +0200 Subject: [PATCH 07/11] Upgrade to Spring Framework 6.0.22 Closes gh-1003 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index daa28a91..fcdad9e6 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ description = "Spring for GraphQL" ext { moduleProjects = [project(":spring-graphql"), project(":spring-graphql-test")] - springFrameworkVersion = "6.0.18" + springFrameworkVersion = "6.0.22" graphQlJavaVersion = "21.5" springBootVersion = "3.1.5" } From bb009f49f030222fccc10f12da2580c1f105991f Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 18 Jun 2024 16:21:37 +0200 Subject: [PATCH 08/11] Upgrade to Reactor 2022.0.20 Closes gh-1004 --- platform/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build.gradle b/platform/build.gradle index 7171c6d8..ee734b43 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -9,7 +9,7 @@ javaPlatform { dependencies { api(platform("org.springframework:spring-framework-bom:${springFrameworkVersion}")) api(platform("com.fasterxml.jackson:jackson-bom:2.15.4")) - api(platform("io.projectreactor:reactor-bom:2022.0.16")) + api(platform("io.projectreactor:reactor-bom:2022.0.20")) api(platform("io.micrometer:micrometer-bom:1.11.9")) api(platform("io.micrometer:micrometer-tracing-bom:1.1.10")) api(platform("org.springframework.data:spring-data-bom:2023.0.9")) From ba3874f1917b8c762bd3cc87912c38fcc4c3a3f7 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 18 Jun 2024 16:22:15 +0200 Subject: [PATCH 09/11] Upgrade to Micrometer 1.11.12 Closes gh-1005 --- platform/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/build.gradle b/platform/build.gradle index ee734b43..ec10769d 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -10,8 +10,8 @@ dependencies { api(platform("org.springframework:spring-framework-bom:${springFrameworkVersion}")) api(platform("com.fasterxml.jackson:jackson-bom:2.15.4")) api(platform("io.projectreactor:reactor-bom:2022.0.20")) - api(platform("io.micrometer:micrometer-bom:1.11.9")) - api(platform("io.micrometer:micrometer-tracing-bom:1.1.10")) + api(platform("io.micrometer:micrometer-bom:1.11.12")) + api(platform("io.micrometer:micrometer-tracing-bom:1.1.13")) api(platform("org.springframework.data:spring-data-bom:2023.0.9")) api(platform("org.springframework.security:spring-security-bom:6.1.7")) api(platform("com.querydsl:querydsl-bom:5.0.0")) From 29f71d0562c9c82e41edecc6caf1e37715140883 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 18 Jun 2024 16:22:48 +0200 Subject: [PATCH 10/11] Upgrade to Spring Data 2023.0.12 Closes gh-1006 --- platform/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build.gradle b/platform/build.gradle index ec10769d..ec3d18e0 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -12,7 +12,7 @@ dependencies { api(platform("io.projectreactor:reactor-bom:2022.0.20")) api(platform("io.micrometer:micrometer-bom:1.11.12")) api(platform("io.micrometer:micrometer-tracing-bom:1.1.13")) - api(platform("org.springframework.data:spring-data-bom:2023.0.9")) + api(platform("org.springframework.data:spring-data-bom:2023.0.12")) api(platform("org.springframework.security:spring-security-bom:6.1.7")) api(platform("com.querydsl:querydsl-bom:5.0.0")) api(platform("io.rsocket:rsocket-bom:1.1.4")) From 16151cce46a1e723d476fe894dc4b2f34037f527 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 18 Jun 2024 16:23:10 +0200 Subject: [PATCH 11/11] Upgrade to Spring Security 6.1.9 Closes gh-1007 --- platform/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/build.gradle b/platform/build.gradle index ec3d18e0..969fbe00 100644 --- a/platform/build.gradle +++ b/platform/build.gradle @@ -13,7 +13,7 @@ dependencies { api(platform("io.micrometer:micrometer-bom:1.11.12")) api(platform("io.micrometer:micrometer-tracing-bom:1.1.13")) api(platform("org.springframework.data:spring-data-bom:2023.0.12")) - api(platform("org.springframework.security:spring-security-bom:6.1.7")) + api(platform("org.springframework.security:spring-security-bom:6.1.9")) api(platform("com.querydsl:querydsl-bom:5.0.0")) api(platform("io.rsocket:rsocket-bom:1.1.4")) api(platform("org.jetbrains.kotlin:kotlin-bom:${kotlinVersion}"))