Skip to content

Commit

Permalink
Merge pull request #6 from xenit-eu/feature/spring-boot-starter
Browse files Browse the repository at this point in the history
Spring boot starters for API and Gateway services
  • Loading branch information
tgeens authored Jan 18, 2022
2 parents 52640d9 + f44d096 commit 85c6640
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 0 deletions.
4 changes: 4 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ include 'thunx-model'
include 'thunx-pdp'
include 'thunx-pdp-opa'
include 'thunx-spring'
include 'thunx-autoconfigure'
include 'thunx-api-spring-boot-starter'
include 'thunx-gateway-spring-boot-starter'

include 'thunx-encoding-json'
include 'thunx-predicates-querydsl'
Expand All @@ -18,3 +21,4 @@ include 'thunx-predicates-querydsl'
// substitute module('eu.xenit.contentcloud:rego-java') with project(':rego-java')
// }
//}

14 changes: 14 additions & 0 deletions thunx-api-spring-boot-starter/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id 'java-library'
}

apply from: "${rootDir}/gradle/publish.gradle"

repositories {
mavenCentral()
}

dependencies {
api project(':thunx-spring')
api project(':thunx-autoconfigure')
}
47 changes: 47 additions & 0 deletions thunx-autoconfigure/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
plugins {
id 'java-library'
}

apply from: "${rootDir}/gradle/publish.gradle"

dependencies {

compileOnly platform("org.springframework.boot:spring-boot-dependencies:${springBootBomVersion}")
compileOnly platform("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}")

implementation "org.springframework.boot:spring-boot-autoconfigure"

compileOnly "org.projectlombok:lombok"

compileOnly project(':thunx-spring')
compileOnly project(':thunx-pdp-opa')
compileOnly "eu.xenit.contentcloud:opa-async-java-client:0.2.0"

compileOnly 'org.springframework.security:spring-security-web'

compileOnly 'org.springframework.data:spring-data-rest-core'
compileOnly 'org.springframework.cloud:spring-cloud-starter-gateway'

annotationProcessor "org.springframework.boot:spring-boot-autoconfigure-processor:${springBootBomVersion}"
annotationProcessor "org.projectlombok:lombok:${lombokVersion}"

testImplementation platform("org.springframework.boot:spring-boot-dependencies:${springBootBomVersion}")
testImplementation platform("org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}")

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.assertj:assertj-core:3.21.0'
testImplementation 'org.mockito:mockito-core:2.1.0'
testImplementation "org.springframework.boot:spring-boot-test"
testImplementation "org.springframework.boot:spring-boot-starter-data-jpa"
testImplementation "org.springframework.boot:spring-boot-starter-data-rest"
testImplementation 'org.springframework.security:spring-security-web'
testImplementation 'org.springframework.cloud:spring-cloud-starter-gateway'
testImplementation 'com.h2database:h2:1.4.200'
testImplementation project(':thunx-spring')
testImplementation project(':thunx-pdp-opa')
testImplementation "eu.xenit.contentcloud:opa-async-java-client:0.2.0"
}

test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package eu.xenit.contentcloud.thunx.api.autoconfigure;

import eu.xenit.contentcloud.thunx.spring.data.EnableAbac;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@Configuration
@ConditionalOnClass(RepositoryRestResource.class)
public class AbacAutoConfiguration {

@Configuration
@EnableAbac
public static class EnableAbacAutoConfiguration {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package eu.xenit.contentcloud.thunx.gateway.autoconfigure;

import eu.xenit.contentcloud.opa.client.OpaClient;
import eu.xenit.contentcloud.opa.client.rest.RestClientConfiguration;
import eu.xenit.contentcloud.thunx.pdp.PolicyDecisionComponentImpl;
import eu.xenit.contentcloud.thunx.pdp.PolicyDecisionPointClient;
import eu.xenit.contentcloud.thunx.pdp.opa.OpaQueryProvider;
import eu.xenit.contentcloud.thunx.pdp.opa.OpenPolicyAgentPDPClient;
import eu.xenit.contentcloud.thunx.spring.gateway.filter.AbacGatewayFilterFactory;
import eu.xenit.contentcloud.thunx.spring.security.ReactivePolicyAuthorizationManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.web.server.authorization.AuthorizationContext;

@Configuration
@ConditionalOnClass({OpaClient.class, AbstractGatewayFilterFactory.class})
@EnableConfigurationProperties(OpaProperties.class)
public class GatewayAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public OpaClient opaClient(OpaProperties opaProperties) {
return OpaClient.builder()
.httpLogging(RestClientConfiguration.LogSpecification::all)
.url(opaProperties.getService().getUrl())
.build();
}

@Bean
@ConditionalOnMissingBean
public OpaQueryProvider propertyBasedOpaQueryProvider(OpaProperties opaProperties) {
return request -> opaProperties.getQuery();
}

@Bean
@ConditionalOnMissingBean
public PolicyDecisionPointClient pdpClient(OpaClient opaClient, OpaQueryProvider queryProvider) {
return new OpenPolicyAgentPDPClient(opaClient, queryProvider);
}

@Bean
@ConditionalOnMissingBean
public ReactiveAuthorizationManager<AuthorizationContext> reactiveAuthenticationManager(
PolicyDecisionPointClient pdpClient) {
return new ReactivePolicyAuthorizationManager(new PolicyDecisionComponentImpl(pdpClient));
}

@Bean
@ConditionalOnMissingBean
public AbacGatewayFilterFactory abacGatewayFilterFactory() {
return new AbacGatewayFilterFactory();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package eu.xenit.contentcloud.thunx.gateway.autoconfigure;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@ConfigurationProperties(prefix = "opa")
public class OpaProperties {
private OpaServiceProperties service;
private String query;

@Data
public static class OpaServiceProperties {
private String url;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
eu.xenit.contentcloud.thunx.api.autoconfigure.AbacAutoConfiguration,\
eu.xenit.contentcloud.thunx.gateway.autoconfigure.GatewayAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package eu.xenit.contentcloud.thunx.api.autoconfigure;

import eu.xenit.contentcloud.thunx.encoding.ThunkExpressionDecoder;
import eu.xenit.contentcloud.thunx.spring.data.rest.AbacExceptionHandler;
import eu.xenit.contentcloud.thunx.spring.data.rest.AbacRequestFilter;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.repository.support.Repositories;

import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;

public class AbacAutoConfigurationTest {

@Test
public void shouldEnableAbac() {

ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
AbacAutoConfiguration.class
));

contextRunner.withUserConfiguration(TestContext.class).run((context) -> {
assertThat(context.getBean(ThunkExpressionDecoder.class), is(not(nullValue())));
assertThat(context.getBean(AbacExceptionHandler.class), is(not(nullValue())));
assertThat(context.getBean(AbacRequestFilter.class), is(not(nullValue())));
assertThat(context.getBean("abacFilterRegistration"), is(not(nullValue())));
assertThat(context.getBean("interceptRepositoryRestMvcConfiguration"), is(not(nullValue())));
assertThat(context.getBean("ensureQueryDslPredication"), is(not(nullValue())));
});
}

@Configuration
@EnableAutoConfiguration(exclude={
org.springframework.cloud.gateway.config.GatewayAutoConfiguration.class,
eu.xenit.contentcloud.thunx.gateway.autoconfigure.GatewayAutoConfiguration.class
})
public static class TestContext {

@Bean
public Repositories repositories(ApplicationContext context) {
return new Repositories(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package eu.xenit.contentcloud.thunx.gateway.autoconfigure;

import eu.xenit.contentcloud.opa.client.OpaClient;
import eu.xenit.contentcloud.thunx.pdp.PolicyDecisionPointClient;
import eu.xenit.contentcloud.thunx.pdp.opa.OpaQueryProvider;
import eu.xenit.contentcloud.thunx.spring.gateway.filter.AbacGatewayFilterFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.web.server.authorization.AuthorizationContext;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;

public class GatewayAutoConfigurationTest {

@Test
public void shouldEnableGatewayBeans() {

ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
GatewayAutoConfiguration.class
));

contextRunner.withUserConfiguration(TestContext.class)
.withPropertyValues("opa.service.url=https://some/opa/service")
.run((context) -> {
assertThat(context.getBean(OpaClient.class)).isNotNull();
assertThat(context.getBean(OpaQueryProvider.class)).isNotNull();
assertThat(context.getBean(PolicyDecisionPointClient.class)).isNotNull();
assertThat(context.getBean(ReactiveAuthorizationManager.class)).isNotNull();
assertThat(context.getBean(AbacGatewayFilterFactory.class)).isNotNull();
});
}

@Test
public void shouldUseProvidedGatewayBeans() {

ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(
GatewayAutoConfiguration.class
));

contextRunner.withUserConfiguration(TestContextWithBeans.class)
.run((context) -> {
assertThat(context.getBean(OpaClient.class)).isSameAs(context.getBean(TestContextWithBeans.class).opaClient());
assertThat(context.getBean(OpaQueryProvider.class)).isSameAs(context.getBean(TestContextWithBeans.class).customQueryProvider());
assertThat(context.getBean(PolicyDecisionPointClient.class)).isSameAs(context.getBean(TestContextWithBeans.class).pdpClient());
assertThat(context.getBean(ReactiveAuthorizationManager.class)).isSameAs(context.getBean(TestContextWithBeans.class).reactiveAuthenticationManager());
assertThat(context.getBean(AbacGatewayFilterFactory.class)).isSameAs(context.getBean(TestContextWithBeans.class).abacGatewayFilterFactory());
});
}

@Configuration
@EnableAutoConfiguration(exclude={
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.class,
org.springframework.cloud.gateway.config.GatewayAutoConfiguration.class,
eu.xenit.contentcloud.thunx.api.autoconfigure.AbacAutoConfiguration.class
})
public static class TestContext {
}

@Configuration
@EnableAutoConfiguration(exclude={
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.class,
org.springframework.cloud.gateway.config.GatewayAutoConfiguration.class,
eu.xenit.contentcloud.thunx.api.autoconfigure.AbacAutoConfiguration.class
})
public static class TestContextWithBeans {

@Bean
public OpaClient opaClient() {
return mock(OpaClient.class);
}

@Bean
@ConditionalOnMissingBean
public PolicyDecisionPointClient pdpClient() {
return mock(PolicyDecisionPointClient.class);
}

@Bean
public ReactiveAuthorizationManager<AuthorizationContext> reactiveAuthenticationManager() {
return mock(ReactiveAuthorizationManager.class);
}

@Bean
public AbacGatewayFilterFactory abacGatewayFilterFactory() {
return mock(AbacGatewayFilterFactory.class);
}

@Bean
public OpaQueryProvider customQueryProvider() {
return mock(OpaQueryProvider.class);
}
}
}
14 changes: 14 additions & 0 deletions thunx-gateway-spring-boot-starter/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
plugins {
id 'java-library'
}

apply from: "${rootDir}/gradle/publish.gradle"

repositories {
mavenCentral()
}

dependencies {
api project(':thunx-pdp-opa')
api project(':thunx-autoconfigure')
}

0 comments on commit 85c6640

Please sign in to comment.