diff --git a/etc/checkstyle/checkstyle.xml b/etc/checkstyle/checkstyle.xml index c827af5f4d..3cdb447a11 100644 --- a/etc/checkstyle/checkstyle.xml +++ b/etc/checkstyle/checkstyle.xml @@ -20,6 +20,7 @@ + diff --git a/test/src/main/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHints.java b/test/src/main/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHints.java new file mode 100644 index 0000000000..5b41094dce --- /dev/null +++ b/test/src/main/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHints.java @@ -0,0 +1,52 @@ +/* + * Copyright 2002-2022 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.test.aot.hint; + +import java.util.Arrays; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.security.test.context.support.WithSecurityContext; +import org.springframework.test.context.aot.TestRuntimeHintsRegistrar; + +import static org.springframework.core.annotation.MergedAnnotations.SearchStrategy.SUPERCLASS; + +/** + * {@link TestRuntimeHintsRegistrar} implementation that register runtime hints for + * {@link WithSecurityContext#factory()} classes. + * + * @author Marcus da Coregio + * @since 6.0 + */ +class WithSecurityContextTestRuntimeHints implements TestRuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, Class testClass, ClassLoader classLoader) { + Arrays.stream(testClass.getDeclaredMethods()) + .map((method) -> MergedAnnotations.from(method, SUPERCLASS).get(WithSecurityContext.class)) + .filter(MergedAnnotation::isPresent) + .map((withSecurityContext) -> withSecurityContext.getClass("factory")) + .forEach((factory) -> registerDeclaredConstructors(hints, factory)); + } + + private void registerDeclaredConstructors(RuntimeHints hints, Class factory) { + hints.reflection().registerType(factory, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS); + } + +} diff --git a/test/src/main/resources/META-INF/spring/aot.factories b/test/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 0000000000..8fb5958d70 --- /dev/null +++ b/test/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.test.context.aot.TestRuntimeHintsRegistrar=\ +org.springframework.security.test.aot.hint.WithSecurityContextTestRuntimeHints diff --git a/test/src/test/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHintsTests.java b/test/src/test/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHintsTests.java new file mode 100644 index 0000000000..1eb5fc3f3f --- /dev/null +++ b/test/src/test/java/org/springframework/security/test/aot/hint/WithSecurityContextTestRuntimeHintsTests.java @@ -0,0 +1,103 @@ +/* + * Copyright 2002-2022 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.test.aot.hint; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.TypeReference; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithAnonymousUser; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.security.test.context.support.WithSecurityContext; +import org.springframework.security.test.context.support.WithSecurityContextFactory; +import org.springframework.security.test.context.support.WithUserDetails; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link WithSecurityContextTestRuntimeHints}. + */ +class WithSecurityContextTestRuntimeHintsTests { + + private final RuntimeHints hints = new RuntimeHints(); + + private final WithSecurityContextTestRuntimeHints registrar = new WithSecurityContextTestRuntimeHints(); + + @BeforeEach + void setup() { + this.registrar.registerHints(this.hints, WithSecurityContextTestRuntimeHintsTests.class, + WithSecurityContextTestRuntimeHintsTests.class.getClassLoader()); + } + + @Test + @WithMockUser + void withMockUserHasHints() { + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference + .of("org.springframework.security.test.context.support.WithMockUserSecurityContextFactory")) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); + } + + @Test + @WithAnonymousUser + void withAnonymousUserHasHints() { + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference.of( + "org.springframework.security.test.context.support.WithAnonymousUserSecurityContextFactory")) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); + } + + @Test + @WithUserDetails + void withUserDetailsHasHints() { + assertThat(RuntimeHintsPredicates.reflection() + .onType(TypeReference + .of("org.springframework.security.test.context.support.WithUserDetailsSecurityContextFactory")) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); + } + + @Test + @WithMockTestUser + void withMockTestUserHasHints() { + assertThat(RuntimeHintsPredicates.reflection().onType(WithMockTestUserSecurityContextFactory.class) + .withMemberCategory(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(this.hints); + } + + @Retention(RetentionPolicy.RUNTIME) + @WithSecurityContext(factory = WithMockTestUserSecurityContextFactory.class) + @interface WithMockTestUser { + + } + + static class WithMockTestUserSecurityContextFactory implements WithSecurityContextFactory { + + @Override + public SecurityContext createSecurityContext(WithMockTestUser annotation) { + return SecurityContextHolder.createEmptyContext(); + } + + } + +}