From 5c20505b0e992680f5b4e13590ccbeb8f0666ddb Mon Sep 17 00:00:00 2001 From: DingHao Date: Wed, 4 Sep 2024 23:39:32 +0800 Subject: [PATCH] Support Class Attributes in Annotation Template Processing Closes gh-15721 --- ...ePostMethodSecurityConfigurationTests.java | 30 +++++++++++++++++++ .../method/AuthorizationAnnotationUtils.java | 26 +++++++++++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java index 367eea5a7e..223fc3f8fc 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java @@ -998,6 +998,15 @@ public class PrePostMethodSecurityConfigurationTests { verify(expressionHandler, times(4)).createEvaluationContext(any(Supplier.class), any()); } + // gh-15721 + @Test + @WithMockUser(roles = "uid") + public void methodWhenMetaAnnotationPropertiesHasClassProperties() { + this.spring.register(MetaAnnotationPlaceholderConfig.class).autowire(); + MetaAnnotationService service = this.spring.getContext().getBean(MetaAnnotationService.class); + assertThat(service.getIdPath("uid")).isEqualTo("uid"); + } + private static Consumer disallowBeanOverriding() { return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false); } @@ -1376,6 +1385,27 @@ public class PrePostMethodSecurityConfigurationTests { return list; } + @RestrictedAccess(entityClass = EntityClass.class) + String getIdPath(String id) { + return id; + } + + } + + @Retention(RetentionPolicy.RUNTIME) + @PreAuthorize("hasRole({idPath})") + @interface RestrictedAccess { + + String idPath() default "#id"; + + Class entityClass(); + + String[] recipes() default {}; + + } + + static class EntityClass { + } @Retention(RetentionPolicy.RUNTIME) diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationUtils.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationUtils.java index de0e9c9111..9387b7be50 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationUtils.java +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationUtils.java @@ -19,9 +19,11 @@ package org.springframework.security.authorization.method; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Method; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Function; import org.springframework.core.annotation.AnnotationConfigurationException; @@ -29,6 +31,8 @@ import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.RepeatableContainers; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.PropertyPlaceholderHelper; @@ -55,6 +59,12 @@ import org.springframework.util.PropertyPlaceholderHelper; */ final class AuthorizationAnnotationUtils { + private static final DefaultConversionService conversionService = new DefaultConversionService(); + + static { + conversionService.addConverter(new ClassToStringConverter()); + } + static Function withDefaults(Class type, PrePostTemplateDefaults defaults) { Function, A> map = (mergedAnnotation) -> { @@ -70,7 +80,7 @@ final class AuthorizationAnnotationUtils { String key = property.getKey(); Object value = property.getValue(); String asString = (value instanceof String) ? (String) value - : DefaultConversionService.getSharedInstance().convert(value, String.class); + : conversionService.convert(value, String.class); stringProperties.put(key, asString); } AnnotatedElement annotatedElement = (AnnotatedElement) mergedAnnotation.getSource(); @@ -156,4 +166,18 @@ final class AuthorizationAnnotationUtils { } + static class ClassToStringConverter implements GenericConverter { + + @Override + public Set getConvertibleTypes() { + return Collections.singleton(new ConvertiblePair(Class.class, String.class)); + } + + @Override + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { + return (source != null) ? source.toString() : null; + } + + } + }