diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index 316c448976..48e0d2519c 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -21,6 +21,7 @@ dependencies { api 'org.springframework:spring-context' api 'org.springframework:spring-core' + optional project(':spring-security-data') optional project(':spring-security-ldap') optional project(':spring-security-messaging') optional project(path: ':spring-security-saml2-service-provider') diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyDataConfiguration.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyDataConfiguration.java new file mode 100644 index 0000000000..e446ee2736 --- /dev/null +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/AuthorizationProxyDataConfiguration.java @@ -0,0 +1,37 @@ +/* + * 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.config.annotation.method.configuration; + +import org.springframework.aop.framework.AopInfrastructureBean; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.security.aot.hint.SecurityHintsRegistrar; +import org.springframework.security.authorization.AuthorizationProxyFactory; +import org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar; + +@Configuration(proxyBeanMethods = false) +final class AuthorizationProxyDataConfiguration implements AopInfrastructureBean { + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + static SecurityHintsRegistrar authorizeReturnObjectDataHintsRegistrar(AuthorizationProxyFactory proxyFactory) { + return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory); + } + +} diff --git a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java index f8f5c8f172..2ceb262a14 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java +++ b/config/src/main/java/org/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.java @@ -26,6 +26,7 @@ import org.springframework.context.annotation.AutoProxyRegistrar; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import org.springframework.lang.NonNull; +import org.springframework.util.ClassUtils; /** * Dynamically determines which imports to include using the {@link EnableMethodSecurity} @@ -37,6 +38,9 @@ import org.springframework.lang.NonNull; */ final class MethodSecuritySelector implements ImportSelector { + private static final boolean isDataPresent = ClassUtils + .isPresent("org.springframework.security.data.aot.hint.AuthorizeReturnObjectDataHintsRegistrar", null); + private final ImportSelector autoProxy = new AutoProxyRegistrarSelector(); @Override @@ -57,6 +61,9 @@ final class MethodSecuritySelector implements ImportSelector { imports.add(Jsr250MethodSecurityConfiguration.class.getName()); } imports.add(AuthorizationProxyConfiguration.class.getName()); + if (isDataPresent) { + imports.add(AuthorizationProxyDataConfiguration.class.getName()); + } return imports.toArray(new String[0]); } diff --git a/data/src/main/java/org/springframework/security/data/aot/hint/AuthorizeReturnObjectDataHintsRegistrar.java b/data/src/main/java/org/springframework/security/data/aot/hint/AuthorizeReturnObjectDataHintsRegistrar.java new file mode 100644 index 0000000000..73eeb9eb10 --- /dev/null +++ b/data/src/main/java/org/springframework/security/data/aot/hint/AuthorizeReturnObjectDataHintsRegistrar.java @@ -0,0 +1,107 @@ +/* + * 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.data.aot.hint; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.core.ResolvableType; +import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; +import org.springframework.security.aot.hint.AuthorizeReturnObjectCoreHintsRegistrar; +import org.springframework.security.aot.hint.AuthorizeReturnObjectHintsRegistrar; +import org.springframework.security.aot.hint.SecurityHintsRegistrar; +import org.springframework.security.authorization.AuthorizationProxyFactory; +import org.springframework.security.authorization.method.AuthorizeReturnObject; +import org.springframework.security.core.annotation.SecurityAnnotationScanner; +import org.springframework.security.core.annotation.SecurityAnnotationScanners; + +/** + * A {@link SecurityHintsRegistrar} that scans all beans for implementations of + * {@link RepositoryFactoryBeanSupport}, registering the corresponding entity class as a + * {@link org.springframework.aot.hint.TypeHint} should any if that repository's method + * use {@link AuthorizeReturnObject}. + * + *
+ * It also traverses those found types for other return values. + * + *
+ * An instance of this class is published as an infrastructural bean by the + * {@code spring-security-config} module. However, in the event you need to publish it + * yourself, remember to publish it as an infrastructural bean like so: + * + *
+ * @Bean + * @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + * static SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) { + * return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory); + * } + *+ * + * @author Josh Cummings + * @since 6.4 + * @see AuthorizeReturnObjectCoreHintsRegistrar + * @see AuthorizeReturnObjectHintsRegistrar + */ +public final class AuthorizeReturnObjectDataHintsRegistrar implements SecurityHintsRegistrar { + + private final AuthorizationProxyFactory proxyFactory; + + private final SecurityAnnotationScanner