diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java index 0cf3cb7e045..0f779f2df32 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/AbstractRequestMatcherRegistry.java @@ -509,7 +509,8 @@ static class DispatcherServletRequestMatcher implements RequestMatcher { public boolean matches(HttpServletRequest request) { String name = request.getHttpServletMapping().getServletName(); ServletRegistration registration = this.servletContext.getServletRegistration(name); - Assert.notNull(registration, computeErrorMessage(this.servletContext.getServletRegistrations().values())); + Assert.notNull(registration, + () -> computeErrorMessage(this.servletContext.getServletRegistrations().values())); try { Class clazz = Class.forName(registration.getClassName()); return DispatcherServlet.class.isAssignableFrom(clazz); 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 367eea5a7e3..838e3cca07a 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,14 @@ public void adviseWhenPrePostEnabledThenEachInterceptorRunsExactlyOnce() { verify(expressionHandler, times(4)).createEvaluationContext(any(Supplier.class), any()); } + @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 +1384,23 @@ List resultsContainDave(List list) { 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 {}; + } @Retention(RetentionPolicy.RUNTIME) @@ -1386,6 +1411,10 @@ List resultsContainDave(List list) { } + static class EntityClass { + + } + @Retention(RetentionPolicy.RUNTIME) @PreAuthorize("hasAuthority('SCOPE_{claim}') || hasAnyRole({roles})") @interface HasClaim { diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationPropertyConversionService.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationPropertyConversionService.java new file mode 100644 index 00000000000..730d5871bec --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationAnnotationPropertyConversionService.java @@ -0,0 +1,88 @@ +/* + * Copyright 2002-2024 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.authorization.method; + +import java.util.Collections; +import java.util.Set; + +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConverterRegistry; +import org.springframework.core.convert.converter.GenericConverter; +import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.convert.support.GenericConversionService; + +/** + * A {@link ConversionService} configured with converters that provide class to string + * conversion + * + * @author DingHao + * @since 6.3 + */ +public final class AuthorizationAnnotationPropertyConversionService extends GenericConversionService { + + private static volatile AuthorizationAnnotationPropertyConversionService sharedInstance; + + private AuthorizationAnnotationPropertyConversionService() { + addConverters(this); + } + + /** + * Returns a shared instance of + * {@code AuthorizationAnnotationPropertyConversionService}. + * @return a shared instance of + * {@code AuthorizationAnnotationPropertyConversionService} + */ + public static AuthorizationAnnotationPropertyConversionService getSharedInstance() { + AuthorizationAnnotationPropertyConversionService cs = sharedInstance; + if (cs == null) { + synchronized (AuthorizationAnnotationPropertyConversionService.class) { + cs = sharedInstance; + if (cs == null) { + cs = new AuthorizationAnnotationPropertyConversionService(); + sharedInstance = cs; + } + } + } + return cs; + } + + /** + * Adds the converters that provide type conversion for + * AuthorizationAnnotationProperty values to the provided {@link ConverterRegistry}. + * @param converterRegistry the registry of converters to add to + */ + public static void addConverters(ConverterRegistry converterRegistry) { + DefaultConversionService.addDefaultConverters(converterRegistry); + converterRegistry.addConverter(new ClassToStringConverter()); + } + + 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; + } + + } + +} 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 de0e9c9111e..43e70bbdbbc 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 @@ -29,7 +29,6 @@ import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.annotation.RepeatableContainers; -import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.util.PropertyPlaceholderHelper; /** @@ -70,7 +69,8 @@ static Function withDefaults(Class