diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java
index 73ed6179..3f1a8bcb 100644
--- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java
+++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AuthenticationPrincipalArgumentResolver.java
@@ -15,10 +15,13 @@
*/
package org.springframework.graphql.data.method.annotation.support;
+import java.lang.annotation.Annotation;
+
import graphql.schema.DataFetchingEnvironment;
import org.reactivestreams.Publisher;
+import reactor.core.publisher.Mono;
+
import org.springframework.core.MethodParameter;
-import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.Expression;
@@ -35,13 +38,11 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
-import reactor.core.publisher.Mono;
-
-import java.lang.annotation.Annotation;
/**
- * Resolver to obtain {@link Authentication#getPrincipal()} from Spring Security context via
- * {@link SecurityContext#getAuthentication()} for parameters annotated with {@link AuthenticationPrincipal}.
+ * Resolver to obtain {@link Authentication#getPrincipal()} from Spring Security
+ * context via {@link SecurityContext#getAuthentication()} for parameters
+ * annotated with {@link AuthenticationPrincipal}.
*
*
The resolver checks both ThreadLocal context via {@link SecurityContextHolder}
* for Spring MVC applications, and {@link ReactiveSecurityContextHolder} for
@@ -52,10 +53,11 @@ import java.lang.annotation.Annotation;
*/
public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
- private ExpressionParser parser = new SpelExpressionParser();
+ private final ExpressionParser parser = new SpelExpressionParser();
private final BeanResolver beanResolver;
+
/**
* Creates a new instance.
* @param beanResolver the {@link BeanResolver} used for resolving beans in SpEL expressions. Cannot be null.
@@ -65,37 +67,60 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
this.beanResolver = beanResolver;
}
+
@Override
public boolean supportsParameter(MethodParameter parameter) {
- return findMethodAnnotation(AuthenticationPrincipal.class, parameter) != null;
+ return findMethodAnnotation(parameter) != null;
+ }
+
+ /**
+ * Obtains the {@link AuthenticationPrincipal} annotation which can be
+ * directly on the {@link MethodParameter} or on a custom annotation that
+ * is meta-annotated with it.
+ */
+ @Nullable
+ private static AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {
+ AuthenticationPrincipal annotation = parameter.getParameterAnnotation(AuthenticationPrincipal.class);
+ if (annotation != null) {
+ return annotation;
+ }
+ Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
+ for (Annotation toSearch : annotationsToSearch) {
+ annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), AuthenticationPrincipal.class);
+ if (annotation != null) {
+ return annotation;
+ }
+ }
+ return null;
}
@Override
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
- return getCurrentAuthentication().map(Authentication::getPrincipal)
- .flatMap((principal) -> Mono.justOrEmpty(resolvePrincipal(parameter, principal)))
- .transform((argument) -> {
- boolean isParameterPublisher = isParameterMonoAssignable(parameter);
- return isParameterPublisher ? Mono.just(argument) : argument;
- });
+ return getCurrentAuthentication()
+ .flatMap(auth -> Mono.justOrEmpty(resolvePrincipal(parameter, auth.getPrincipal())))
+ .transform((argument) -> isParameterMonoAssignable(parameter) ? Mono.just(argument) : argument);
+ }
+
+ private static boolean isParameterMonoAssignable(MethodParameter parameter) {
+ Class> type = parameter.getParameterType();
+ return (Publisher.class.equals(type) || Mono.class.equals(type));
}
private Mono getCurrentAuthentication() {
- SecurityContext securityContext = SecurityContextHolder.getContext();
- return Mono.justOrEmpty(securityContext.getAuthentication())
+ return Mono.justOrEmpty(SecurityContextHolder.getContext().getAuthentication())
.switchIfEmpty(ReactiveSecurityContextHolder.getContext().map(SecurityContext::getAuthentication));
}
@Nullable
private Object resolvePrincipal(MethodParameter parameter, Object principal) {
- AuthenticationPrincipal annotation = findMethodAnnotation(AuthenticationPrincipal.class, parameter);
- String expressionToParse = annotation.expression();
- if (StringUtils.hasLength(expressionToParse)) {
+ AuthenticationPrincipal annotation = findMethodAnnotation(parameter);
+ String expressionValue = annotation.expression();
+ if (StringUtils.hasLength(expressionValue)) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setRootObject(principal);
context.setVariable("this", principal);
context.setBeanResolver(this.beanResolver);
- Expression expression = this.parser.parseExpression(expressionToParse);
+ Expression expression = this.parser.parseExpression(expressionValue);
principal = expression.getValue(context);
}
if (isInvalidType(parameter, principal)) {
@@ -113,9 +138,8 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
}
Class> typeToCheck = parameter.getParameterType();
if (isParameterMonoAssignable(parameter)) {
- ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
- Class> genericType = resolvableType.resolveGeneric(0);
- if (genericType == null) {
+ Class> genericType = parameter.nested().getNestedParameterType();
+ if (genericType.equals(Object.class)) {
return false;
}
typeToCheck = genericType;
@@ -123,31 +147,4 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
return !ClassUtils.isAssignable(typeToCheck, principal.getClass());
}
- private boolean isParameterMonoAssignable(MethodParameter parameter) {
- return Publisher.class.equals(parameter.getParameterType()) ||
- Mono.class.equals(parameter.getParameterType());
- }
-
- /**
- * Obtains the specified {@link Annotation} on the specified {@link MethodParameter}.
- * @param annotationClass the class of the {@link Annotation} to find on the
- * {@link MethodParameter}
- * @param parameter the {@link MethodParameter} to search for an {@link Annotation}
- * @return the {@link Annotation} that was found or null.
- */
- @Nullable
- private T findMethodAnnotation(Class annotationClass, MethodParameter parameter) {
- T annotation = parameter.getParameterAnnotation(annotationClass);
- if (annotation != null) {
- return annotation;
- }
- Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
- for (Annotation toSearch : annotationsToSearch) {
- annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
- if (annotation != null) {
- return annotation;
- }
- }
- return null;
- }
}
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 51dde89d..fb0536d1 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
@@ -15,10 +15,18 @@
*/
package org.springframework.graphql.data.method.annotation.support;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.util.function.Function;
+
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
+import reactor.core.publisher.Mono;
+import reactor.test.publisher.TestPublisher;
+import reactor.util.context.Context;
+
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
@@ -36,14 +44,6 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.util.ClassUtils;
-import reactor.core.publisher.Mono;
-import reactor.test.publisher.TestPublisher;
-import reactor.util.context.Context;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.reflect.Method;
-import java.util.function.Function;
import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType;
import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
@@ -55,73 +55,76 @@ import static org.assertj.core.api.AssertionsForInterfaceTypes.assertThat;
*/
class AuthenticationPrincipalArgumentResolverTests {
- private final static Class> MONO_USER_DETAILS_CLASS = ResolvableType.forClassWithGenerics(Mono.class, UserDetails.class).getRawClass();
+ private final static Class STRING_CLASS = String.class;
private final static Class USER_DETAILS_CLASS = UserDetails.class;
- private final static Class STRING_CLASS = String.class;
+ private final static Class> MONO_USER_DETAILS_CLASS =
+ ResolvableType.forClassWithGenerics(Mono.class, UserDetails.class).getRawClass();
- private final static Class> MONO_STRING_CLASS = ResolvableType.forClassWithGenerics(Mono.class, String.class).getRawClass();
+ private final static Class> MONO_STRING_CLASS =
+ ResolvableType.forClassWithGenerics(Mono.class, String.class).getRawClass();
- private final static Class> PUBLISHER_USER_DETAILS_CLASS = ResolvableType.forClassWithGenerics(Publisher.class, UserDetails.class).getRawClass();
+ private final static Class> PUBLISHER_USER_DETAILS_CLASS =
+ ResolvableType.forClassWithGenerics(Publisher.class, UserDetails.class).getRawClass();
- private final static Class> TESTPUBLISHER_USER_DETAILS_CLASS = ResolvableType.forClassWithGenerics(TestPublisher.class, UserDetails.class).getRawClass();
+ private final static Class> TESTPUBLISHER_USER_DETAILS_CLASS =
+ ResolvableType.forClassWithGenerics(TestPublisher.class, UserDetails.class).getRawClass();
- private AuthenticationPrincipalArgumentResolver resolver;
- @BeforeEach
- void setup() {
- this.resolver = new AuthenticationPrincipalArgumentResolver((beanName, context) -> new PrincipalConverter());
- }
+ private final AuthenticationPrincipalArgumentResolver resolver =
+ new AuthenticationPrincipalArgumentResolver((beanName, context) -> new PrincipalConverter());
+
@AfterEach
void cleanup() {
SecurityContextHolder.clearContext();
}
+
@Test
- void supportsParameterWhenNoAnnotation() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "noParameter", USER_DETAILS_CLASS);
- assertThat(this.resolver.supportsParameter(methodParameter)).isFalse();
+ void supportsParameterWhenNoAnnotation() {
+ MethodParameter parameter = firstParameter(UserController.class, "noParameter", USER_DETAILS_CLASS);
+ assertThat(this.resolver.supportsParameter(parameter)).isFalse();
}
@Test
- void supportsParameterWhenWrongAnnotation() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "argument", USER_DETAILS_CLASS);
- assertThat(this.resolver.supportsParameter(methodParameter)).isFalse();
+ void supportsParameterWhenWrongAnnotation() {
+ MethodParameter parameter = firstParameter(UserController.class, "argument", USER_DETAILS_CLASS);
+ assertThat(this.resolver.supportsParameter(parameter)).isFalse();
}
@Test
- void supportsParameterWhenCurrentUser() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
- assertThat(this.resolver.supportsParameter(methodParameter)).isTrue();
+ void supportsParameterWhenCurrentUser() {
+ MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
+ assertThat(this.resolver.supportsParameter(parameter)).isTrue();
}
@Test
- void supportsParameterWhenAuthenticationPrincipal() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "userDetails", USER_DETAILS_CLASS);
- assertThat(this.resolver.supportsParameter(methodParameter)).isTrue();
+ void supportsParameterWhenAuthenticationPrincipal() {
+ MethodParameter parameter = firstParameter(UserController.class, "userDetails", USER_DETAILS_CLASS);
+ assertThat(this.resolver.supportsParameter(parameter)).isTrue();
}
@Test
void resolveArgumentWhenAuthenticationPrincipal() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "userDetails", USER_DETAILS_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
+ MethodParameter parameter = firstParameter(UserController.class, "userDetails", USER_DETAILS_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
}
@Test
void resolveArgumentWhenCurrentUser() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
+ MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
}
@Test
void resolveArgumentWhenNoSecurityContext() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.block()).isNull();
+ MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.block()).isNull();
}
@Test
@@ -129,83 +132,83 @@ class AuthenticationPrincipalArgumentResolverTests {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(usernamePasswordAuthentication());
SecurityContextHolder.setContext(context);
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
+ MethodParameter parameter = firstParameter(UserController.class, "currentUser", USER_DETAILS_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
}
@Test
void resolveArgumentWhenAuthenticationPrincipalUsername() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "username", STRING_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.contextWrite(authenticationContext()).block()).isEqualTo("user");
+ MethodParameter parameter = firstParameter(UserController.class, "username", STRING_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.contextWrite(authenticationContext()).block()).isEqualTo("user");
}
@Test
void resolveArgumentWhenBeanName() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "beanName", USER_DETAILS_CLASS);
- Mono userDetails = (Mono) this.resolver.resolveArgument(methodParameter, null);
- assertThat(userDetails.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
+ MethodParameter parameter = firstParameter(UserController.class, "beanName", USER_DETAILS_CLASS);
+ Mono details = (Mono) this.resolver.resolveArgument(parameter, null);
+ assertThat(details.contextWrite(authenticationContext()).block().getUsername()).isEqualTo("user");
}
@Test
void resolveArgumentWhenErrorOnInvalidType() throws Exception {
- MethodParameter methodParameter = firstMethodParameter(UserController.class, "errorOnInvalidType", String.class);
- Mono