Przeglądaj źródła

Add SecurityHintsRegistrar

An interface for registering hints based on Security infrastructure
beans.

Closes gh-15772
Josh Cummings 11 miesięcy temu
rodzic
commit
da38b13a17

+ 2 - 0
core/spring-security-core.gradle

@@ -1,4 +1,5 @@
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
 import java.util.concurrent.Callable
 
 apply plugin: 'io.spring.convention.spring-module'
@@ -30,6 +31,7 @@ dependencies {
 	testImplementation "org.junit.jupiter:junit-jupiter-engine"
 	testImplementation "org.mockito:mockito-core"
 	testImplementation "org.mockito:mockito-junit-jupiter"
+	testImplementation "org.springframework:spring-core-test"
 	testImplementation "org.springframework:spring-test"
 	testImplementation 'org.skyscreamer:jsonassert'
 	testImplementation 'org.springframework:spring-test'

+ 49 - 0
core/src/main/java/org/springframework/security/aot/hint/SecurityHintsAotProcessor.java

@@ -0,0 +1,49 @@
+/*
+ * 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.aot.hint;
+
+import org.springframework.aot.generate.GenerationContext;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor;
+import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+final class SecurityHintsAotProcessor implements BeanFactoryInitializationAotProcessor {
+
+	@Override
+	public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
+		return new AuthorizationProxyFactoryAotContribution(beanFactory);
+	}
+
+	private static final class AuthorizationProxyFactoryAotContribution
+			implements BeanFactoryInitializationAotContribution {
+
+		private final ConfigurableListableBeanFactory beanFactory;
+
+		private AuthorizationProxyFactoryAotContribution(ConfigurableListableBeanFactory beanFactory) {
+			this.beanFactory = beanFactory;
+		}
+
+		@Override
+		public void applyTo(GenerationContext context, BeanFactoryInitializationCode code) {
+			this.beanFactory.getBeanProvider(SecurityHintsRegistrar.class)
+				.forEach((provider) -> provider.registerHints(context.getRuntimeHints(), this.beanFactory));
+		}
+
+	}
+
+}

+ 66 - 0
core/src/main/java/org/springframework/security/aot/hint/SecurityHintsRegistrar.java

@@ -0,0 +1,66 @@
+/*
+ * 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.aot.hint;
+
+import org.springframework.aot.hint.RuntimeHints;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+
+/**
+ * An interface for registering AOT hints.
+ *
+ * <p>
+ * This interface is helpful because it allows for basing hints on Spring Security's
+ * infrastructural beans like so:
+ *
+ * <pre>
+ *	&#064;Bean
+ *	&#064;Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+ *	static SecurityHintsRegistrar proxyThese(AuthorizationProxyFactory proxyFactory) {
+ *		return new AuthorizationProxyFactoryHintsRegistrar(proxyFactory, MyClass.class);
+ *	}
+ * </pre>
+ *
+ * <p>
+ * The collection of beans that implement {@link SecurityHintsRegistrar} are serially
+ * invoked by {@link SecurityHintsAotProcessor}, a
+ * {@link org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor}.
+ *
+ * <p>
+ * Since this is used in a
+ * {@link org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor},
+ * the Spring Framework recommendation to only depend on infrastructural beans applies.
+ *
+ * <p>
+ * If you do not need Security's infrastructural beans, consider either implementing
+ * {@link org.springframework.aot.hint.RuntimeHintsRegistrar} or another AOT component as
+ * indicated in the Spring Framework AOT reference documentation.
+ *
+ * @author Josh Cummings
+ * @since 6.4
+ * @see AuthorizeReturnObjectHintsRegistrar
+ * @see SecurityHintsAotProcessor
+ */
+public interface SecurityHintsRegistrar {
+
+	/**
+	 * Register hints after preparing them through Security's infrastructural beans
+	 * @param hints the registration target for any AOT hints
+	 * @param beanFactory the bean factory
+	 */
+	void registerHints(RuntimeHints hints, ConfigurableListableBeanFactory beanFactory);
+
+}

+ 2 - 0
core/src/main/resources/META-INF/spring/aot.factories

@@ -1,2 +1,4 @@
 org.springframework.aot.hint.RuntimeHintsRegistrar=\
 org.springframework.security.aot.hint.CoreSecurityRuntimeHints
+org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor=\
+org.springframework.security.aot.hint.SecurityHintsAotProcessor

+ 60 - 0
core/src/test/java/org/springframework/security/aot/hint/SecurityHintsAotProcessorTests.java

@@ -0,0 +1,60 @@
+/*
+ * 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.aot.hint;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.aot.generate.GenerationContext;
+import org.springframework.aot.test.generate.TestGenerationContext;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Role;
+import org.springframework.context.aot.ApplicationContextAotGenerator;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+/**
+ * Tests for {@link SecurityHintsAotProcessor}
+ */
+public class SecurityHintsAotProcessorTests {
+
+	@Test
+	void applyToWhenSecurityHintsRegistrarThenInvokes() {
+		GenerationContext generationContext = new TestGenerationContext();
+		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
+		context.register(AppConfig.class);
+		ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator();
+		generator.processAheadOfTime(context, generationContext);
+		verify(context.getBean(SecurityHintsRegistrar.class)).registerHints(any(), any());
+	}
+
+	@Configuration
+	static class AppConfig {
+
+		@Bean
+		@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
+		static SecurityHintsRegistrar hints() {
+			return mock(SecurityHintsRegistrar.class);
+		}
+
+	}
+
+}