From 115a78d5f56c0900b9f9c42e6d924b88dd08fc57 Mon Sep 17 00:00:00 2001 From: Steve Riesenberg Date: Fri, 2 Jul 2021 17:24:53 -0500 Subject: [PATCH] Add post processor to register ProviderSettings Bean Closes gh-373 --- ...Auth2AuthorizationServerConfiguration.java | 8 ++ .../RegisterMissingBeanPostProcessor.java | 71 +++++++++++ ...RegisterMissingBeanPostProcessorTests.java | 110 ++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java create mode 100644 oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java index 8341a66e..6591dcbb 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.security.config.annotation.web.configurers.oauth2.ser import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -89,4 +90,11 @@ public class OAuth2AuthorizationServerConfiguration { return new NimbusJwtDecoder(jwtProcessor); } + @Bean + RegisterMissingBeanPostProcessor registerMissingBeanPostProcessor() { + RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor(); + postProcessor.addBeanDefinition(ProviderSettings.class, () -> ProviderSettings.builder().build()); + return postProcessor; + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java new file mode 100644 index 00000000..b0652bc9 --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java @@ -0,0 +1,71 @@ +/* + * Copyright 2020-2021 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.web.configuration; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.BeanFactoryUtils; +import org.springframework.beans.factory.ListableBeanFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.AnnotationBeanNameGenerator; + +/** + * Post processor to register one or more bean definitions on container initialization, if not already present. + * + * @author Steve Riesenberg + * @since 0.2.0 + */ +final class RegisterMissingBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware { + private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); + private final List beanDefinitions = new ArrayList<>(); + private BeanFactory beanFactory; + + @Override + public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { + for (AbstractBeanDefinition beanDefinition : this.beanDefinitions) { + String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( + (ListableBeanFactory) this.beanFactory, beanDefinition.getBeanClass(), false, false); + if (beanNames.length == 0) { + String beanName = this.beanNameGenerator.generateBeanName(beanDefinition, registry); + registry.registerBeanDefinition(beanName, beanDefinition); + } + } + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + } + + void addBeanDefinition(Class beanClass, Supplier beanSupplier) { + this.beanDefinitions.add(new RootBeanDefinition(beanClass, beanSupplier)); + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + } + +} diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java new file mode 100644 index 00000000..fac9f627 --- /dev/null +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java @@ -0,0 +1,110 @@ +/* + * Copyright 2020-2021 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.web.configuration; + +import java.util.function.Supplier; + +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; + +/** + * Tests for {@link RegisterMissingBeanPostProcessor}. + * + * @author Steve Riesenberg + */ +public class RegisterMissingBeanPostProcessorTests { + private final RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor(); + + @Test + public void postProcessBeanDefinitionRegistryWhenClassAddedThenRegisteredWithClass() { + this.postProcessor.addBeanDefinition(SimpleBean.class, null); + this.postProcessor.setBeanFactory(new DefaultListableBeanFactory()); + + BeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + this.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry); + + ArgumentCaptor beanDefinitionCaptor = ArgumentCaptor.forClass(BeanDefinition.class); + verify(beanDefinitionRegistry).registerBeanDefinition(endsWith("SimpleBean"), beanDefinitionCaptor.capture()); + + RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionCaptor.getValue(); + assertThat(beanDefinition.getBeanClass()).isEqualTo(SimpleBean.class); + assertThat(beanDefinition.getInstanceSupplier()).isNull(); + } + + @Test + public void postProcessBeanDefinitionRegistryWhenSupplierAddedThenRegisteredWithSupplier() { + Supplier beanSupplier = () -> new SimpleBean("string"); + this.postProcessor.addBeanDefinition(SimpleBean.class, beanSupplier); + this.postProcessor.setBeanFactory(new DefaultListableBeanFactory()); + + BeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + this.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry); + + ArgumentCaptor beanDefinitionCaptor = ArgumentCaptor.forClass(BeanDefinition.class); + verify(beanDefinitionRegistry).registerBeanDefinition(endsWith("SimpleBean"), beanDefinitionCaptor.capture()); + + RootBeanDefinition beanDefinition = (RootBeanDefinition) beanDefinitionCaptor.getValue(); + assertThat(beanDefinition.getBeanClass()).isEqualTo(SimpleBean.class); + assertThat(beanDefinition.getInstanceSupplier()).isEqualTo(beanSupplier); + } + + @Test + public void postProcessBeanDefinitionRegistryWhenNoBeanDefinitionsAddedThenNoneRegistered() { + this.postProcessor.setBeanFactory(new DefaultListableBeanFactory()); + + BeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + this.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry); + verifyNoInteractions(beanDefinitionRegistry); + } + + @Test + public void postProcessBeanDefinitionRegistryWhenBeanDefinitionAlreadyExistsThenNoneRegistered() { + this.postProcessor.addBeanDefinition(SimpleBean.class, null); + DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); + beanFactory.registerBeanDefinition("simpleBean", new RootBeanDefinition(SimpleBean.class)); + this.postProcessor.setBeanFactory(beanFactory); + + BeanDefinitionRegistry beanDefinitionRegistry = mock(BeanDefinitionRegistry.class); + this.postProcessor.postProcessBeanDefinitionRegistry(beanDefinitionRegistry); + verifyNoInteractions(beanDefinitionRegistry); + } + + private static final class SimpleBean { + private final String field; + + private SimpleBean(String field) { + this.field = field; + } + + private String getField() { + return field; + } + + } + +}