Skip to content

Commit

Permalink
Couple minor configuration tweaks to add Spring Boot 2.2 support
Browse files Browse the repository at this point in the history
* work around configuration processor issue spring-projects/spring-boot#17035
* Adjust AutoConfig tests to apply EnvironmentPostProcessor when context is loaded.
  • Loading branch information
bdemers committed Jun 4, 2019
1 parent 95b6c20 commit 91a8d50
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ public void init(HttpSecurity http) throws Exception {
log.debug("OAuth/OIDC Login not configured due to missing issuer, client-id, or client-secret property");
}

if (!context.getBeansOfType(OAuth2ResourceServerProperties.class).isEmpty()
&& !isEmpty(oktaOAuth2Properties.getIssuer())) {
// configure Okta specific auth converter (extracts authorities from `groupsClaim`
configureResourceServer(http, oktaOAuth2Properties);
// resource server configuration
if (!context.getBeansOfType(OAuth2ResourceServerProperties.class).isEmpty()) {
OAuth2ResourceServerProperties resourceServerProperties = context.getBean(OAuth2ResourceServerProperties.class);
if (!isEmpty(resourceServerProperties.getJwt().getIssuerUri())) {
// configure Okta specific auth converter (extracts authorities from `groupsClaim`
configureResourceServer(http, oktaOAuth2Properties);
} else {
log.debug("OAuth resource server not configured due to missing issuer property");
}
} else {
log.debug("OAuth resource server not configured due to missing issuer or client-id property");
log.debug("OAuth resource server not configured due to missing OAuth2ResourceServerProperties bean");
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ public final class OktaOAuth2Properties implements Validator {
*/
private String groupsClaim = "groups";

// work around for https://github.com/spring-projects/spring-boot/issues/17035

This comment has been minimized.

Copy link
@snicoll

snicoll Jun 8, 2019

How about adding the following instead?

@Autowired
void setOAuth2ClientProperties(ObjectProvider<OAuth2ClientProperties> oAuth2ClientProperties) { ... }

(and removing the constructors)

Is the constructor that take the properties bean public API? If it is you could move it to a @Beanexplicit definition instead and move the @ConfigurationProperties declaration there.

This comment has been minimized.

Copy link
@bdemers

bdemers Jun 12, 2019

Author Contributor

Thanks @snicoll I like that idea!

I did notice the constructor annotation was used (possibly related to how i'm using WebApplicationContextRunner) without the required = false I was able to force tests to fail.

That said, the bean option seems like the clearer path all around, thanks!

This comment has been minimized.

Copy link
@snicoll

snicoll Jun 12, 2019

You're right, I wasn't aware we've started to honour those for consistency when we opened up use to constructor parameter.

private OktaOAuth2Properties() {
this(null);
}

@Autowired
public OktaOAuth2Properties(@Autowired(required = false) OAuth2ClientProperties clientProperties) {

This comment has been minimized.

Copy link
@snicoll

snicoll Jun 8, 2019

That @Autowired on constructor parameter is completely ignored.

this.clientProperties = clientProperties;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ import org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDeta
import org.springframework.boot.test.context.runner.AbstractApplicationContextRunner
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner
import org.springframework.boot.test.context.runner.WebApplicationContextRunner
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebApplicationContext
import org.springframework.context.ApplicationContext
import org.springframework.context.ApplicationContextInitializer
import org.springframework.context.ConfigurableApplicationContext
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.ConfigurableEnvironment
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService
Expand All @@ -54,6 +56,7 @@ import org.testng.annotations.BeforeClass
import org.testng.annotations.Test

import javax.servlet.Filter
import java.util.function.Supplier
import java.util.stream.Collectors
import java.util.stream.Stream

Expand Down Expand Up @@ -88,13 +91,11 @@ class AutoConfigConditionalTest {
}

@Test
void webResourceServerConfig() {

WebApplicationContextRunner contextRunner = webContextRunner()
void webResourceServerConfig_emptyProperties() {

// missing properties, component does not load
contextRunner
.run {context ->
webContextRunner()
.run { context ->
assertThat(context).doesNotHaveBean(OktaOAuth2ResourceServerAutoConfig)
assertThat(context).doesNotHaveBean(JwtDecoder)
assertThat(context).doesNotHaveBean(OktaOAuth2AutoConfig)
Expand All @@ -104,12 +105,15 @@ class AutoConfigConditionalTest {
assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2ServerHttpServerAutoConfig)
assertThat(context).doesNotHaveBean(OAuth2AuthorizedClientService)


assertFiltersDisabled(context, OAuth2LoginAuthenticationFilter, BearerTokenAuthenticationFilter)
assertFiltersDisabled(context, OAuth2LoginAuthenticationFilter, BearerTokenAuthenticationFilter)
}
}

@Test
void webResourceServerConfig_withIssuer() {

// with properties it loads correctly
contextRunner.withPropertyValues(
webContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/")
.run {context ->
assertThat(context).hasSingleBean(OktaOAuth2ResourceServerAutoConfig)
Expand All @@ -127,11 +131,11 @@ class AutoConfigConditionalTest {
}

@Test
void webLoginConfig() {
void webLoginConfig_withIssuer() {

webContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/")
.run { context ->
.run { context ->

assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2AutoConfig)
assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2ResourceServerAutoConfig)
Expand All @@ -147,12 +151,16 @@ class AutoConfigConditionalTest {
assertFiltersEnabled(context, BearerTokenAuthenticationFilter)
assertFiltersDisabled(context, OAuth2LoginAuthenticationFilter)
}
}

@Test
void webLoginConfig_withIssuerAndClientInfo() {

webContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/",
"okta.oauth2.issuer=https://test.example.com",
"okta.oauth2.client-id=test-client-id",
"okta.oauth2.client-secret=test-client-secret")
.run { context ->
.run { context ->
assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2AutoConfig)
assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2ResourceServerAutoConfig)
assertThat(context).doesNotHaveBean(ReactiveOktaOAuth2ResourceServerHttpServerAutoConfig)
Expand All @@ -169,13 +177,11 @@ class AutoConfigConditionalTest {
}

@Test
void reactiveResourceServerTest() {

ReactiveWebApplicationContextRunner contextRunner = reactiveContextRunner()
void reactiveResourceServerTest_emptyProperties() {

// missing properties, component does not load
contextRunner
.run {context ->
reactiveContextRunner()
.run { context ->
assertThat(context).doesNotHaveBean(OktaOAuth2ResourceServerAutoConfig)
assertThat(context).doesNotHaveBean(JwtDecoder)
assertThat(context).doesNotHaveBean(OktaOAuth2AutoConfig)
Expand All @@ -187,12 +193,15 @@ class AutoConfigConditionalTest {

assertWebFiltersDisabled(context, OAuth2LoginAuthenticationWebFilter)
assertJwtBearerWebFilterDisabled(context)

}
}

@Test
void reactiveResourceServerTest_withIssuer() {

// with properties it loads correctly
contextRunner.withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/")
reactiveContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com")
.run {context ->
assertThat(context).doesNotHaveBean(OktaOAuth2ResourceServerAutoConfig)
assertThat(context).doesNotHaveBean(JwtDecoder)
Expand All @@ -212,14 +221,12 @@ class AutoConfigConditionalTest {
}

@Test
void reactiveLoginConfig() {
void reactiveLoginConfig_withIssuer() {

reactiveContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/",
"okta.oauth2.client-id=test-client-id"
)
reactiveContextRunner()
.withPropertyValues(
"okta.oauth2.issuer=https://test.example.com")
.run { context ->

assertThat(context).doesNotHaveBean(OktaOAuth2ResourceServerAutoConfig)
assertThat(context).doesNotHaveBean(JwtDecoder)
assertThat(context).doesNotHaveBean(OktaOAuth2AutoConfig)
Expand All @@ -235,6 +242,10 @@ class AutoConfigConditionalTest {
assertWebFiltersEnabled(context, AuthenticationWebFilter)
assertJwtBearerWebFilterEnabled(context)
}
}

@Test
void reactiveLoginConfig_withIssuerAndClientInfo() {

reactiveContextRunner().withPropertyValues(
"okta.oauth2.issuer=https://test.example.com/",
Expand Down Expand Up @@ -321,22 +332,38 @@ class AutoConfigConditionalTest {
}

private reactiveContextRunner(Class<?>... appClasses = [SimpleReactiveApp]) {

Class[] autoConfigs = [oktaAutoConfigs, appClasses].flatten()
ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(autoConfigs))
ReactiveWebApplicationContextRunner contextRunner = new ReactiveWebApplicationContextRunner(
new Supplier<AnnotationConfigReactiveWebApplicationContext>() {
@Override
AnnotationConfigReactiveWebApplicationContext get() {
return new AnnotationConfigReactiveWebApplicationContext() {
@Override
protected ConfigurableEnvironment createEnvironment() {
def configurableEnv = super.createEnvironment()
new OktaOAuth2PropertiesMappingEnvironmentPostProcessor().postProcessEnvironment(configurableEnv, null)
return configurableEnv
}
}
}
})

return withOktaProperties(contextRunner)
return contextRunner
.withConfiguration(AutoConfigurations.of(autoConfigs))
.withInitializer(new ConditionEvaluationReportLoggingListener())
}

private static <T extends AbstractApplicationContextRunner> T withOktaProperties(T contextRunner) {

return (T) contextRunner.withInitializer(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
@Override
void initialize(ConfigurableApplicationContext applicationContext) {
new OktaOAuth2PropertiesMappingEnvironmentPostProcessor().postProcessEnvironment(applicationContext.environment, null)
}
})
return (T) contextRunner.withInitializer(new OktaPropertiesContextInitializer())
}

static class OktaPropertiesContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
void initialize(ConfigurableApplicationContext applicationContext) {
new OktaOAuth2PropertiesMappingEnvironmentPostProcessor().postProcessEnvironment(applicationContext.getEnvironment(), null)
}
}

@Configuration
Expand Down
15 changes: 14 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<packaging>pom</packaging>

<properties>
<spring-boot.version>2.1.4.RELEASE</spring-boot.version>
<spring-boot.version>2.1.5.RELEASE</spring-boot.version>
<spring-cloud.version>2.1.2.RELEASE</spring-cloud.version>
<github.slug>okta/okta-spring-boot</github.slug>
<okta.sdk.version>1.5.2</okta.sdk.version>
Expand Down Expand Up @@ -140,4 +140,17 @@
</dependency>
</dependencies>
</dependencyManagement>

<profiles>
<profile>
<id>spring-milestones</id>
<repositories>
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>http://repo.spring.io/milestone</url>
</repository>
</repositories>
</profile>
</profiles>
</project>

0 comments on commit 91a8d50

Please sign in to comment.