Skip to content

Commit b0da37d

Browse files
kse-musicjzheaux
authored andcommitted
Have Method Security Start at Target Class
Closes gh-13783
1 parent d2bc340 commit b0da37d

13 files changed

+215
-32
lines changed

core/src/main/java/org/springframework/security/authorization/method/AbstractExpressionAttributeRegistry.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
* For internal use only, as this contract is likely to change
3030
*
3131
* @author Evgeniy Cheban
32+
* @author DingHao
3233
*/
3334
abstract class AbstractExpressionAttributeRegistry<T extends ExpressionAttribute> {
3435

@@ -67,4 +68,8 @@ final T getAttribute(Method method, Class<?> targetClass) {
6768
@NonNull
6869
abstract T resolveAttribute(Method method, Class<?> targetClass);
6970

71+
Class<?> targetClass(Method method, Class<?> targetClass) {
72+
return (targetClass != null) ? targetClass : method.getDeclaringClass();
73+
}
74+
7075
}

core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -44,6 +44,7 @@
4444
*
4545
* @author Evgeniy Cheban
4646
* @author Josh Cummings
47+
* @author DingHao
4748
* @since 5.6
4849
*/
4950
public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> {
@@ -121,7 +122,8 @@ AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> ta
121122
private Annotation findJsr250Annotation(Method method, Class<?> targetClass) {
122123
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
123124
Annotation annotation = findAnnotation(specificMethod);
124-
return (annotation != null) ? annotation : findAnnotation(specificMethod.getDeclaringClass());
125+
return (annotation != null) ? annotation
126+
: findAnnotation((targetClass != null) ? targetClass : specificMethod.getDeclaringClass());
125127
}
126128

127129
private Annotation findAnnotation(Method method) {

core/src/main/java/org/springframework/security/authorization/method/PostAuthorizeExpressionAttributeRegistry.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
* For internal use only, as this contract is likely to change.
3232
*
3333
* @author Evgeniy Cheban
34+
* @author DingHao
3435
* @since 5.8
3536
*/
3637
final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
@@ -54,7 +55,7 @@ MethodSecurityExpressionHandler getExpressionHandler() {
5455
@Override
5556
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
5657
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
57-
PostAuthorize postAuthorize = findPostAuthorizeAnnotation(specificMethod);
58+
PostAuthorize postAuthorize = findPostAuthorizeAnnotation(specificMethod, targetClass);
5859
if (postAuthorize == null) {
5960
return ExpressionAttribute.NULL_ATTRIBUTE;
6061
}
@@ -63,10 +64,10 @@ ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
6364
return new ExpressionAttribute(postAuthorizeExpression);
6465
}
6566

66-
private PostAuthorize findPostAuthorizeAnnotation(Method method) {
67+
private PostAuthorize findPostAuthorizeAnnotation(Method method, Class<?> targetClass) {
6768
PostAuthorize postAuthorize = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PostAuthorize.class);
68-
return (postAuthorize != null) ? postAuthorize
69-
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PostAuthorize.class);
69+
return (postAuthorize != null) ? postAuthorize : AuthorizationAnnotationUtils
70+
.findUniqueAnnotation(targetClass(method, targetClass), PostAuthorize.class);
7071
}
7172

7273
}

core/src/main/java/org/springframework/security/authorization/method/PostFilterExpressionAttributeRegistry.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
* For internal use only, as this contract is likely to change.
3131
*
3232
* @author Evgeniy Cheban
33+
* @author DingHao
3334
* @since 5.8
3435
*/
3536
final class PostFilterExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
@@ -53,7 +54,7 @@ MethodSecurityExpressionHandler getExpressionHandler() {
5354
@Override
5455
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
5556
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
56-
PostFilter postFilter = findPostFilterAnnotation(specificMethod);
57+
PostFilter postFilter = findPostFilterAnnotation(specificMethod, targetClass);
5758
if (postFilter == null) {
5859
return ExpressionAttribute.NULL_ATTRIBUTE;
5960
}
@@ -62,10 +63,10 @@ ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
6263
return new ExpressionAttribute(postFilterExpression);
6364
}
6465

65-
private PostFilter findPostFilterAnnotation(Method method) {
66+
private PostFilter findPostFilterAnnotation(Method method, Class<?> targetClass) {
6667
PostFilter postFilter = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PostFilter.class);
6768
return (postFilter != null) ? postFilter
68-
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PostFilter.class);
69+
: AuthorizationAnnotationUtils.findUniqueAnnotation(targetClass(method, targetClass), PostFilter.class);
6970
}
7071

7172
}

core/src/main/java/org/springframework/security/authorization/method/PreAuthorizeExpressionAttributeRegistry.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
* For internal use only, as this contract is likely to change.
3232
*
3333
* @author Evgeniy Cheban
34+
* @author DingHao
3435
* @since 5.8
3536
*/
3637
final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
@@ -58,7 +59,7 @@ MethodSecurityExpressionHandler getExpressionHandler() {
5859
@Override
5960
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
6061
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
61-
PreAuthorize preAuthorize = findPreAuthorizeAnnotation(specificMethod);
62+
PreAuthorize preAuthorize = findPreAuthorizeAnnotation(specificMethod, targetClass);
6263
if (preAuthorize == null) {
6364
return ExpressionAttribute.NULL_ATTRIBUTE;
6465
}
@@ -67,10 +68,10 @@ ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
6768
return new ExpressionAttribute(preAuthorizeExpression);
6869
}
6970

70-
private PreAuthorize findPreAuthorizeAnnotation(Method method) {
71+
private PreAuthorize findPreAuthorizeAnnotation(Method method, Class<?> targetClass) {
7172
PreAuthorize preAuthorize = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PreAuthorize.class);
72-
return (preAuthorize != null) ? preAuthorize
73-
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PreAuthorize.class);
73+
return (preAuthorize != null) ? preAuthorize : AuthorizationAnnotationUtils
74+
.findUniqueAnnotation(targetClass(method, targetClass), PreAuthorize.class);
7475
}
7576

7677
}

core/src/main/java/org/springframework/security/authorization/method/PreFilterExpressionAttributeRegistry.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
* For internal use only, as this contract is likely to change.
3131
*
3232
* @author Evgeniy Cheban
33+
* @author DingHao
3334
* @since 5.8
3435
*/
3536
final class PreFilterExpressionAttributeRegistry
@@ -54,7 +55,7 @@ MethodSecurityExpressionHandler getExpressionHandler() {
5455
@Override
5556
PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {
5657
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
57-
PreFilter preFilter = findPreFilterAnnotation(specificMethod);
58+
PreFilter preFilter = findPreFilterAnnotation(specificMethod, targetClass);
5859
if (preFilter == null) {
5960
return PreFilterExpressionAttribute.NULL_ATTRIBUTE;
6061
}
@@ -63,10 +64,10 @@ PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClas
6364
return new PreFilterExpressionAttribute(preFilterExpression, preFilter.filterTarget());
6465
}
6566

66-
private PreFilter findPreFilterAnnotation(Method method) {
67+
private PreFilter findPreFilterAnnotation(Method method, Class<?> targetClass) {
6768
PreFilter preFilter = AuthorizationAnnotationUtils.findUniqueAnnotation(method, PreFilter.class);
6869
return (preFilter != null) ? preFilter
69-
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), PreFilter.class);
70+
: AuthorizationAnnotationUtils.findUniqueAnnotation(targetClass(method, targetClass), PreFilter.class);
7071
}
7172

7273
static final class PreFilterExpressionAttribute extends ExpressionAttribute {

core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -41,6 +41,7 @@
4141
* contains a specified authority from the Spring Security's {@link Secured} annotation.
4242
*
4343
* @author Evgeniy Cheban
44+
* @author DingHao
4445
* @since 5.6
4546
*/
4647
public final class SecuredAuthorizationManager implements AuthorizationManager<MethodInvocation> {
@@ -86,14 +87,14 @@ private Set<String> getAuthorities(MethodInvocation methodInvocation) {
8687

8788
private Set<String> resolveAuthorities(Method method, Class<?> targetClass) {
8889
Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
89-
Secured secured = findSecuredAnnotation(specificMethod);
90+
Secured secured = findSecuredAnnotation(specificMethod, targetClass);
9091
return (secured != null) ? Set.of(secured.value()) : Collections.emptySet();
9192
}
9293

93-
private Secured findSecuredAnnotation(Method method) {
94+
private Secured findSecuredAnnotation(Method method, Class<?> targetClass) {
9495
Secured secured = AuthorizationAnnotationUtils.findUniqueAnnotation(method, Secured.class);
95-
return (secured != null) ? secured
96-
: AuthorizationAnnotationUtils.findUniqueAnnotation(method.getDeclaringClass(), Secured.class);
96+
return (secured != null) ? secured : AuthorizationAnnotationUtils
97+
.findUniqueAnnotation((targetClass != null) ? targetClass : method.getDeclaringClass(), Secured.class);
9798
}
9899

99100
}

core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -225,6 +225,56 @@ public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationE
225225
.isThrownBy(() -> manager.check(authentication, methodInvocation));
226226
}
227227

228+
@Test
229+
public void checkRequiresUserWhenMethodsFromInheritThenApplies() throws Exception {
230+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new RolesAllowedClass(),
231+
RolesAllowedClass.class, "securedUser");
232+
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
233+
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
234+
assertThat(decision.isGranted()).isTrue();
235+
}
236+
237+
@Test
238+
public void checkPermitAllWhenMethodsFromInheritThenApplies() throws Exception {
239+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new PermitAllClass(), PermitAllClass.class,
240+
"securedUser");
241+
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
242+
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
243+
assertThat(decision.isGranted()).isTrue();
244+
}
245+
246+
@Test
247+
public void checkDenyAllWhenMethodsFromInheritThenApplies() throws Exception {
248+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new DenyAllClass(), DenyAllClass.class,
249+
"securedUser");
250+
Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager();
251+
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, methodInvocation);
252+
assertThat(decision.isGranted()).isFalse();
253+
}
254+
255+
@RolesAllowed("USER")
256+
public static class RolesAllowedClass extends ParentClass {
257+
258+
}
259+
260+
@PermitAll
261+
public static class PermitAllClass extends ParentClass {
262+
263+
}
264+
265+
@DenyAll
266+
public static class DenyAllClass extends ParentClass {
267+
268+
}
269+
270+
public static class ParentClass {
271+
272+
public void securedUser() {
273+
274+
}
275+
276+
}
277+
228278
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
229279

230280
public void doSomething() {

core/src/test/java/org/springframework/security/authorization/method/PostAuthorizeAuthorizationManagerTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2022 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -167,6 +167,29 @@ public void checkInheritedAnnotationsWhenConflictingThenAnnotationConfigurationE
167167
.isThrownBy(() -> manager.check(authentication, result));
168168
}
169169

170+
@Test
171+
public void checkRequiresUserWhenMethodsFromInheritThenApplies() throws Exception {
172+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new PostAuthorizeClass(),
173+
PostAuthorizeClass.class, "securedUser");
174+
MethodInvocationResult result = new MethodInvocationResult(methodInvocation, null);
175+
PostAuthorizeAuthorizationManager manager = new PostAuthorizeAuthorizationManager();
176+
AuthorizationDecision decision = manager.check(TestAuthentication::authenticatedUser, result);
177+
assertThat(decision.isGranted()).isTrue();
178+
}
179+
180+
@PostAuthorize("hasRole('USER')")
181+
public static class PostAuthorizeClass extends ParentClass {
182+
183+
}
184+
185+
public static class ParentClass {
186+
187+
public void securedUser() {
188+
189+
}
190+
191+
}
192+
170193
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
171194

172195
public void doSomething() {

core/src/test/java/org/springframework/security/authorization/method/PostFilterAuthorizationMethodInterceptorTests.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2023 the original author or authors.
2+
* Copyright 2002-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -170,6 +170,34 @@ public Object proceed() {
170170
SecurityContextHolder.setContextHolderStrategy(saved);
171171
}
172172

173+
@Test
174+
public void checkPostFilterWhenMethodsFromInheritThenApplies() throws Throwable {
175+
String[] array = { "john", "bob" };
176+
MockMethodInvocation methodInvocation = new MockMethodInvocation(new PostFilterClass(), PostFilterClass.class,
177+
"inheritMethod", new Class[] { String[].class }, new Object[] { array }) {
178+
@Override
179+
public Object proceed() {
180+
return array;
181+
}
182+
};
183+
PostFilterAuthorizationMethodInterceptor advice = new PostFilterAuthorizationMethodInterceptor();
184+
Object result = advice.invoke(methodInvocation);
185+
assertThat(result).asInstanceOf(InstanceOfAssertFactories.array(String[].class)).containsOnly("john");
186+
}
187+
188+
@PostFilter("filterObject == 'john'")
189+
public static class PostFilterClass extends ParentClass {
190+
191+
}
192+
193+
public static class ParentClass {
194+
195+
public String[] inheritMethod(String[] array) {
196+
return array;
197+
}
198+
199+
}
200+
173201
@PostFilter("filterObject == 'john'")
174202
public static class TestClass implements InterfaceAnnotationsOne, InterfaceAnnotationsTwo {
175203

0 commit comments

Comments
 (0)