mirror of
https://github.com/spring-projects/spring-security.git
synced 2025-07-12 05:13:33 +00:00
Add AuthorizeReturnObject Spring Data Hints
Issue gh-15709
This commit is contained in:
parent
fd5d03d384
commit
e29058c7e4
@ -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')
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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]);
|
||||
}
|
||||
|
||||
|
@ -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}.
|
||||
*
|
||||
* <p>
|
||||
* It also traverses those found types for other return values.
|
||||
*
|
||||
* <p>
|
||||
* 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:
|
||||
*
|
||||
* <pre>
|
||||
* @Bean
|
||||
* @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
* static SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {
|
||||
* return new AuthorizeReturnObjectDataHintsRegistrar(proxyFactory);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.4
|
||||
* @see AuthorizeReturnObjectCoreHintsRegistrar
|
||||
* @see AuthorizeReturnObjectHintsRegistrar
|
||||
*/
|
||||
public final class AuthorizeReturnObjectDataHintsRegistrar implements SecurityHintsRegistrar {
|
||||
|
||||
private final AuthorizationProxyFactory proxyFactory;
|
||||
|
||||
private final SecurityAnnotationScanner<AuthorizeReturnObject> scanner = SecurityAnnotationScanners
|
||||
.requireUnique(AuthorizeReturnObject.class);
|
||||
|
||||
private final Set<Class<?>> visitedClasses = new HashSet<>();
|
||||
|
||||
public AuthorizeReturnObjectDataHintsRegistrar(AuthorizationProxyFactory proxyFactory) {
|
||||
this.proxyFactory = proxyFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory) {
|
||||
List<Class<?>> toProxy = new ArrayList<>();
|
||||
for (String name : beanFactory.getBeanDefinitionNames()) {
|
||||
ResolvableType type = beanFactory.getBeanDefinition(name).getResolvableType();
|
||||
if (!RepositoryFactoryBeanSupport.class.isAssignableFrom(type.toClass())) {
|
||||
continue;
|
||||
}
|
||||
Class<?>[] generics = type.resolveGenerics();
|
||||
Class<?> entity = generics[1];
|
||||
AuthorizeReturnObject authorize = beanFactory.findAnnotationOnBean(name, AuthorizeReturnObject.class);
|
||||
if (authorize != null) {
|
||||
toProxy.add(entity);
|
||||
continue;
|
||||
}
|
||||
Class<?> repository = generics[0];
|
||||
for (Method method : repository.getDeclaredMethods()) {
|
||||
AuthorizeReturnObject returnObject = this.scanner.scan(method, repository);
|
||||
if (returnObject == null) {
|
||||
continue;
|
||||
}
|
||||
// optimistically assume that the entity needs wrapping if any of the
|
||||
// repository methods use @AuthorizeReturnObject
|
||||
toProxy.add(entity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
new AuthorizeReturnObjectHintsRegistrar(this.proxyFactory, toProxy).registerHints(hints, beanFactory);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* 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.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.security.aot.hint.SecurityHintsRegistrar;
|
||||
import org.springframework.security.authorization.AuthorizationProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
/**
|
||||
* Tests for {@link AuthorizeReturnObjectDataHintsRegistrar}
|
||||
*/
|
||||
public class AuthorizeReturnObjectDataHintsRegistrarTests {
|
||||
|
||||
private final AuthorizationProxyFactory proxyFactory = spy(AuthorizationAdvisorProxyFactory.withDefaults());
|
||||
|
||||
private final SecurityHintsRegistrar registrar = new AuthorizeReturnObjectDataHintsRegistrar(this.proxyFactory);
|
||||
|
||||
@Test
|
||||
public void registerHintsWhenUsingAuthorizeReturnObjectThenRegisters() {
|
||||
GenericApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
|
||||
RuntimeHints hints = new RuntimeHints();
|
||||
this.registrar.registerHints(hints, context.getBeanFactory());
|
||||
assertThat(hints.reflection().typeHints().map((hint) -> hint.getType().getName()))
|
||||
.containsOnly(cglibClassName(MyObject.class), cglibClassName(MySubObject.class));
|
||||
}
|
||||
|
||||
private static String cglibClassName(Class<?> clazz) {
|
||||
return clazz.getName() + "$$SpringCGLIB$$0";
|
||||
}
|
||||
|
||||
@AuthorizeReturnObject
|
||||
public interface MyInterface extends CrudRepository<MyObject, Long> {
|
||||
|
||||
List<MyObject> findAll();
|
||||
|
||||
}
|
||||
|
||||
public static class MyObject {
|
||||
|
||||
@AuthorizeReturnObject
|
||||
public MySubObject get() {
|
||||
return new MySubObject();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class MySubObject {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class AppConfig {
|
||||
|
||||
@Bean
|
||||
RepositoryFactoryBeanSupport<MyInterface, MyObject, Long> bean() {
|
||||
return new RepositoryFactoryBeanSupport<>(MyInterface.class) {
|
||||
@Override
|
||||
public MyInterface getObject() {
|
||||
return mock(MyInterface.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends MyInterface> getObjectType() {
|
||||
return MyInterface.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryFactorySupport createRepositoryFactory() {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user