Skip to content

Commit

Permalink
Added Testing
Browse files Browse the repository at this point in the history
Issue gh-16177
  • Loading branch information
jzheaux committed Dec 10, 2024
1 parent f565b23 commit 4cbaabb
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -28,6 +32,7 @@
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;
Expand Down Expand Up @@ -404,11 +409,28 @@ public String username(UserDetails user) {

}

@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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

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;
Expand Down Expand Up @@ -186,10 +187,21 @@ public void resolveArgumentCustomMetaAnnotationTpl() throws Exception {
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);
}
Expand Down Expand Up @@ -240,7 +252,7 @@ private MethodParameter showUserAnnotationObject() {

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) {
Expand Down Expand Up @@ -280,11 +292,32 @@ private void setAuthenticationPrincipal(Object principal) {

}

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) {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -128,6 +131,19 @@ public void supportsParameterWhenNotAnnotatedThenFalse() {
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) {
}
Expand Down Expand Up @@ -172,6 +188,8 @@ static class CustomUserPrincipal {

public final int id = 1;

public final String property = "property";

public Object getPrincipal() {
return this;
}
Expand All @@ -195,4 +213,29 @@ public Object getPrincipal() {

}

@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) {

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
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;
Expand Down Expand Up @@ -93,6 +95,8 @@
*/
public final class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {

private final Class<AuthenticationPrincipal> annotationType = AuthenticationPrincipal.class;

private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();

Expand All @@ -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
Expand Down Expand Up @@ -165,6 +171,7 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur
*/
public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDefaults) {
this.scanner = SecurityAnnotationScanners.requireUnique(AuthenticationPrincipal.class, templateDefaults);
this.useAnnotationTemplate = templateDefaults != null;
}

/**
Expand All @@ -174,8 +181,22 @@ public void setTemplateDefaults(AnnotationTemplateExpressionDefaults templateDef
* @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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

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;
Expand Down Expand Up @@ -214,10 +215,22 @@ public void resolveArgumentCustomMetaAnnotationTpl() throws Exception {
.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);
}
Expand Down Expand Up @@ -272,7 +285,7 @@ private MethodParameter showUserCustomMetaAnnotationTpl() {

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) {
Expand All @@ -295,6 +308,16 @@ private void setAuthenticationPrincipal(Object principal) {

}

@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 {
Expand All @@ -312,11 +335,22 @@ private void setAuthenticationPrincipal(Object principal) {

}

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) {
}

Expand Down
Loading

0 comments on commit 4cbaabb

Please sign in to comment.