Bläddra i källkod

Added Testing

Issue gh-16177
Josh Cummings 8 månader sedan
förälder
incheckning
4cbaabb239

+ 24 - 2
config/src/test/java/org/springframework/security/config/annotation/web/reactive/EnableWebFluxSecurityTests.java

@@ -16,6 +16,10 @@
 
 package org.springframework.security.config.annotation.web.reactive;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.nio.charset.StandardCharsets;
 
 import org.junit.jupiter.api.Test;
@@ -28,6 +32,7 @@ import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.context.annotation.Import;
 import org.springframework.core.Ordered;
+import org.springframework.core.annotation.AliasFor;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.io.buffer.DataBuffer;
 import org.springframework.core.io.buffer.DefaultDataBufferFactory;
@@ -404,11 +409,28 @@ public class EnableWebFluxSecurityTests {
 
 		}
 
+		@Target({ ElementType.PARAMETER })
+		@Retention(RetentionPolicy.RUNTIME)
+		@AuthenticationPrincipal
+		@interface Property {
+
+			@AliasFor(attribute = "expression", annotation = AuthenticationPrincipal.class)
+			String value() default "id";
+
+		}
+
+		interface UsernameResolver {
+
+			String username(@Property("@principalBean.username(#this)") String username);
+
+		}
+
 		@RestController
-		static class AuthenticationPrincipalResolver {
+		static class AuthenticationPrincipalResolver implements UsernameResolver {
 
+			@Override
 			@GetMapping("/spel")
-			String username(@AuthenticationPrincipal(expression = "@principalBean.username(#this)") String username) {
+			public String username(String username) {
 				return username;
 			}
 

+ 35 - 2
messaging/src/test/java/org/springframework/security/messaging/context/AuthenticationPrincipalArgumentResolverTests.java

@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.core.MethodParameter;
 import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.AnnotatedMethod;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
 import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@ -186,10 +187,21 @@ public class AuthenticationPrincipalArgumentResolverTests {
 		assertThat(this.resolver.resolveArgument(showUserCustomMetaAnnotationTpl(), null)).isEqualTo(principal.id);
 	}
 
+	@Test
+	public void resolveArgumentWhenAliasForOnInterfaceThenInherits() {
+		CustomUserPrincipal principal = new CustomUserPrincipal();
+		setAuthenticationPrincipal(principal);
+		assertThat(this.resolver.resolveArgument(showUserNoConcreteAnnotation(), null)).isEqualTo(principal.property);
+	}
+
 	private MethodParameter showUserNoAnnotation() {
 		return getMethodParameter("showUserNoAnnotation", String.class);
 	}
 
+	private MethodParameter showUserNoConcreteAnnotation() {
+		return getMethodParameter("showUserNoConcreteAnnotation", String.class);
+	}
+
 	private MethodParameter showUserAnnotationString() {
 		return getMethodParameter("showUserAnnotation", String.class);
 	}
@@ -240,7 +252,7 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	private MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {
 		Method method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);
-		return new MethodParameter(method, 0);
+		return new AnnotatedMethod(method).getMethodParameters()[0];
 	}
 
 	private void setAuthenticationPrincipal(Object principal) {
@@ -280,11 +292,32 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	}
 
-	public static class TestController {
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@AuthenticationPrincipal
+	@interface Property {
+
+		@AliasFor(attribute = "expression", annotation = AuthenticationPrincipal.class)
+		String value() default "id";
+
+	}
+
+	private interface TestInterface {
+
+		void showUserNoConcreteAnnotation(@Property("property") String property);
+
+	}
+
+	public static class TestController implements TestInterface {
 
 		public void showUserNoAnnotation(String user) {
 		}
 
+		@Override
+		public void showUserNoConcreteAnnotation(String user) {
+
+		}
+
 		public void showUserAnnotation(@AuthenticationPrincipal String user) {
 		}
 

+ 43 - 0
messaging/src/test/java/org/springframework/security/messaging/handler/invocation/reactive/AuthenticationPrincipalArgumentResolverTests.java

@@ -16,14 +16,17 @@
 
 package org.springframework.security.messaging.handler.invocation.reactive;
 
+import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 
 import org.junit.jupiter.api.Test;
 import reactor.core.publisher.Mono;
 
 import org.springframework.core.MethodParameter;
 import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.AnnotatedMethod;
 import org.springframework.core.annotation.SynthesizingMethodParameter;
 import org.springframework.security.authentication.TestAuthentication;
 import org.springframework.security.authentication.TestingAuthenticationToken;
@@ -128,6 +131,19 @@ public class AuthenticationPrincipalArgumentResolverTests {
 		assertThat(this.resolver.supportsParameter(arg0("monoUserDetails"))).isFalse();
 	}
 
+	@Test
+	public void resolveArgumentWhenAliasForOnInterfaceThenInherits() {
+		CustomUserPrincipal principal = new CustomUserPrincipal();
+		Authentication authentication = new TestingAuthenticationToken(principal, "password", "ROLE_USER");
+		ResolvableMethod method = ResolvableMethod.on(TestController.class)
+			.named("showUserNoConcreteAnnotation")
+			.method();
+		MethodParameter parameter = new AnnotatedMethod(method.method()).getMethodParameters()[0];
+		Mono<Object> result = this.resolver.resolveArgument(parameter, null)
+			.contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication));
+		assertThat(result.block()).isEqualTo(principal.property);
+	}
+
 	@SuppressWarnings("unused")
 	private void monoUserDetails(Mono<UserDetails> user) {
 	}
@@ -172,6 +188,8 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 		public final int id = 1;
 
+		public final String property = "property";
+
 		public Object getPrincipal() {
 			return this;
 		}
@@ -195,4 +213,29 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@AuthenticationPrincipal
+	@interface Property {
+
+		@AliasFor(attribute = "expression", annotation = AuthenticationPrincipal.class)
+		String value() default "id";
+
+	}
+
+	private interface TestInterface {
+
+		void showUserNoConcreteAnnotation(@Property("property") String property);
+
+	}
+
+	private static class TestController implements TestInterface {
+
+		@Override
+		public void showUserNoConcreteAnnotation(String user) {
+
+		}
+
+	}
+
 }

+ 23 - 2
web/src/main/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolver.java

@@ -19,6 +19,8 @@ package org.springframework.security.web.method.annotation;
 import java.lang.annotation.Annotation;
 
 import org.springframework.core.MethodParameter;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.MergedAnnotations;
 import org.springframework.expression.BeanResolver;
 import org.springframework.expression.Expression;
 import org.springframework.expression.ExpressionParser;
@@ -93,6 +95,8 @@ import org.springframework.web.method.support.ModelAndViewContainer;
  */
 public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
 
+	private final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;
+
 	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
 		.getContextHolderStrategy();
 
@@ -101,6 +105,8 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
 	private SecurityAnnotationScanner<AuthenticationPrincipal> scanner = SecurityAnnotationScanners
 		.requireUnique(AuthenticationPrincipal.class);
 
+	private boolean useAnnotationTemplate = false;
+
 	private BeanResolver beanResolver;
 
 	@Override
@@ -165,6 +171,7 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
 	 */
 	public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
 		this.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);
+		this.useAnnotationTemplate = templateDefaults != null;
 	}
 
 	/**
@@ -174,8 +181,22 @@ public final class AuthenticationPrincipalArgumentResolver implements HandlerMet
 	 * @return the {@link Annotation} that was found or null.
 	 */
 	@SuppressWarnings("unchecked")
-	private <T extends Annotation> T findMethodAnnotation(MethodParameter parameter) {
-		return (T) this.scanner.scan(parameter.getParameter());
+	private AuthenticationPrincipal findMethodAnnotation(MethodParameter parameter) {
+		if (this.useAnnotationTemplate) {
+			return this.scanner.scan(parameter.getParameter());
+		}
+		AuthenticationPrincipal annotation = parameter.getParameterAnnotation(this.annotationType);
+		if (annotation != null) {
+			return annotation;
+		}
+		Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
+		for (Annotation toSearch : annotationsToSearch) {
+			annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), this.annotationType);
+			if (annotation != null) {
+				return MergedAnnotations.from(toSearch).get(this.annotationType).synthesize();
+			}
+		}
+		return null;
 	}
 
 }

+ 36 - 2
web/src/test/java/org/springframework/security/web/method/annotation/AuthenticationPrincipalArgumentResolverTests.java

@@ -28,6 +28,7 @@ import org.junit.jupiter.api.Test;
 
 import org.springframework.core.MethodParameter;
 import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.AnnotatedMethod;
 import org.springframework.expression.BeanResolver;
 import org.springframework.security.authentication.TestingAuthenticationToken;
 import org.springframework.security.core.annotation.AnnotationTemplateExpressionDefaults;
@@ -214,10 +215,22 @@ public class AuthenticationPrincipalArgumentResolverTests {
 			.isEqualTo(this.expectedPrincipal);
 	}
 
+	@Test
+	public void resolveArgumentWhenAliasForOnInterfaceThenInherits() throws Exception {
+		CustomUserPrincipal principal = new CustomUserPrincipal();
+		setAuthenticationPrincipal(principal);
+		assertThat(this.resolver.resolveArgument(showUserNoConcreteAnnotation(), null, null, null))
+			.isEqualTo(principal.property);
+	}
+
 	private MethodParameter showUserNoAnnotation() {
 		return getMethodParameter("showUserNoAnnotation", String.class);
 	}
 
+	private MethodParameter showUserNoConcreteAnnotation() {
+		return getMethodParameter("showUserNoConcreteAnnotation", String.class);
+	}
+
 	private MethodParameter showUserAnnotationString() {
 		return getMethodParameter("showUserAnnotation", String.class);
 	}
@@ -272,7 +285,7 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	private MethodParameter getMethodParameter(String methodName, Class<?>... paramTypes) {
 		Method method = ReflectionUtils.findMethod(TestController.class, methodName, paramTypes);
-		return new MethodParameter(method, 0);
+		return new AnnotatedMethod(method).getMethodParameters()[0];
 	}
 
 	private void setAuthenticationPrincipal(Object principal) {
@@ -295,6 +308,16 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@AuthenticationPrincipal
+	@interface Property {
+
+		@AliasFor(attribute = "expression", annotation = AuthenticationPrincipal.class)
+		String value() default "id";
+
+	}
+
 	@Retention(RetentionPolicy.RUNTIME)
 	@AuthenticationPrincipal
 	public @interface CurrentUser2 {
@@ -312,11 +335,22 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	}
 
-	public static class TestController {
+	public interface TestInterface {
+
+		void showUserNoConcreteAnnotation(@Property("property") String property);
+
+	}
+
+	public static class TestController implements TestInterface {
 
 		public void showUserNoAnnotation(String user) {
 		}
 
+		@Override
+		public void showUserNoConcreteAnnotation(String user) {
+
+		}
+
 		public void showUserAnnotation(@AuthenticationPrincipal String user) {
 		}
 

+ 41 - 0
web/src/test/java/org/springframework/security/web/reactive/result/method/annotation/AuthenticationPrincipalArgumentResolverTests.java

@@ -32,6 +32,7 @@ import reactor.core.publisher.Mono;
 import org.springframework.core.MethodParameter;
 import org.springframework.core.ReactiveAdapterRegistry;
 import org.springframework.core.annotation.AliasFor;
+import org.springframework.core.annotation.AnnotatedMethod;
 import org.springframework.core.annotation.SynthesizingMethodParameter;
 import org.springframework.expression.BeanResolver;
 import org.springframework.security.core.Authentication;
@@ -230,6 +231,19 @@ public class AuthenticationPrincipalArgumentResolverTests {
 		assertThat(result.block()).isEqualTo(principal.id);
 	}
 
+	@Test
+	public void resolveArgumentWhenAliasForOnInterfaceThenInherits() {
+		CustomUserPrincipal principal = new CustomUserPrincipal();
+		given(this.authentication.getPrincipal()).willReturn(principal);
+		ResolvableMethod method = ResolvableMethod.on(TestController.class)
+			.named("showUserNoConcreteAnnotation")
+			.build();
+		MethodParameter parameter = new AnnotatedMethod(method.method()).getMethodParameters()[0];
+		Mono<Object> result = this.resolver.resolveArgument(parameter, this.bindingContext, this.exchange)
+			.contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.authentication));
+		assertThat(result.block()).isEqualTo(principal.property);
+	}
+
 	private MethodParameter arg0(String methodName) {
 		ResolvableMethod method = ResolvableMethod.on(getClass()).named(methodName).build();
 		return new SynthesizingMethodParameter(method.method(), 0);
@@ -317,6 +331,8 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 		public final int id = 1;
 
+		public final String property = "property";
+
 		public Object getPrincipal() {
 			return this;
 		}
@@ -340,4 +356,29 @@ public class AuthenticationPrincipalArgumentResolverTests {
 
 	}
 
+	@Target({ ElementType.PARAMETER })
+	@Retention(RetentionPolicy.RUNTIME)
+	@AuthenticationPrincipal
+	@interface Property {
+
+		@AliasFor(attribute = "expression", annotation = AuthenticationPrincipal.class)
+		String value() default "id";
+
+	}
+
+	private interface TestInterface {
+
+		void showUserNoConcreteAnnotation(@Property("property") String property);
+
+	}
+
+	private static class TestController implements TestInterface {
+
+		@Override
+		public void showUserNoConcreteAnnotation(String user) {
+
+		}
+
+	}
+
 }