Skip to content

Commit

Permalink
Added Keycloak integration test (#6085)
Browse files Browse the repository at this point in the history
Adds Keycloak integration test to DataflowOAuthIT
Adds Authorities mapping test similar to keycloak role usage.
Added scripts to src/local for testing keycloak locally with preconfigured roles / group and user.
Fix duplicate output for skipper and dataflow and some output frames.
Refactor DefaultAuthoritiesMapperTests for JUnit 5 conventions.
Rename DataflowOAuthIT methods for JUnit 5 conventions.
Add testcontainers-keycloak to dependency management in spring-cloud-dataflow-parent.
Updates src/local/README.md with information on the scripts.
Removes create-containers.sh that used jib
Adds comment to application-dataflow-keycloak.yaml on client-secret.
  • Loading branch information
corneil authored Dec 12, 2024
1 parent 3eb2b80 commit 5e5edc5
Show file tree
Hide file tree
Showing 28 changed files with 4,986 additions and 281 deletions.
1 change: 0 additions & 1 deletion .github/workflows/ci-it-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ jobs:
- name: Run Security IT
shell: bash
run: |
./mvnw clean install -DskipTests -T 1C -s .settings.xml -pl spring-cloud-dataflow-server -am -B --no-transfer-progress
./mvnw -s .settings.xml \
-pl spring-cloud-dataflow-server \
-Dgroups=oauth \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@
/**
* @author Gunnar Hillert
*/
public class DefaultAuthoritiesMapperTests {
class DefaultAuthoritiesMapperTests {

@Test
public void testNullConstructor() throws Exception {
void nullConstructor() throws Exception {
assertThatThrownBy(() -> {
new DefaultAuthoritiesMapper(null, "");
}).isInstanceOf(IllegalArgumentException.class).hasMessageContaining("providerRoleMappings must not be null.");
}

@Test
public void testMapScopesToAuthoritiesWithNullParameters() throws Exception {
void mapScopesToAuthoritiesWithNullParameters() throws Exception {
DefaultAuthoritiesMapper authoritiesMapper = new DefaultAuthoritiesMapper(Collections.emptyMap(), "");

assertThatThrownBy(() -> {
Expand All @@ -56,18 +56,19 @@ public void testMapScopesToAuthoritiesWithNullParameters() throws Exception {
}

@Test
public void testThat7AuthoritiesAreReturned() throws Exception {
void that7AuthoritiesAreReturned() throws Exception {
DefaultAuthoritiesMapper authoritiesMapper = new DefaultAuthoritiesMapper("uaa", false);
Set<GrantedAuthority> authorities = authoritiesMapper.mapScopesToAuthorities("uaa", Collections.emptySet(), null);

assertThat(authorities).hasSize(7);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_MANAGE", "ROLE_CREATE", "ROLE_VIEW", "ROLE_DEPLOY", "ROLE_MODIFY",
"ROLE_SCHEDULE", "ROLE_DESTROY");
}

@Test
public void testEmptyMapConstructor() throws Exception {
void emptyMapConstructor() throws Exception {
Set<String> scopes = new HashSet<>();
scopes.add("dataflow.manage");
scopes.add("dataflow.view");
Expand All @@ -77,12 +78,13 @@ public void testEmptyMapConstructor() throws Exception {
Collection<? extends GrantedAuthority> authorities = authoritiesMapper.mapScopesToAuthorities("uaa", scopes, null);

assertThat(authorities).hasSize(3);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_MANAGE", "ROLE_CREATE", "ROLE_VIEW");
}

@Test
public void testMapConstructorWithIncompleteRoleMappings() throws Exception {
void mapConstructorWithIncompleteRoleMappings() throws Exception {
ProviderRoleMapping roleMapping = new ProviderRoleMapping();
roleMapping.setMapOauthScopes(true);
roleMapping.addRoleMapping("ROLE_MANAGE", "foo-scope-in-oauth");
Expand All @@ -93,80 +95,115 @@ public void testMapConstructorWithIncompleteRoleMappings() throws Exception {
}

@Test
public void testThat7MappedAuthoritiesAreReturned() throws Exception {
Map<String, String> roleMappings = new HashMap<>();
roleMappings.put("ROLE_MANAGE", "foo-manage");
roleMappings.put("ROLE_VIEW", "bar-view");
roleMappings.put("ROLE_CREATE", "blubba-create");
roleMappings.put("ROLE_MODIFY", "foo-modify");
roleMappings.put("ROLE_DEPLOY", "foo-deploy");
roleMappings.put("ROLE_DESTROY", "foo-destroy");
roleMappings.put("ROLE_SCHEDULE", "foo-schedule");
void that3MappedAuthoritiesAreReturned() throws Exception {
Map<String, String> roleMappings = Map.of(
"ROLE_MANAGE", "dataflow_manage",
"ROLE_VIEW", "dataflow_view",
"ROLE_CREATE", "dataflow_create",
"ROLE_MODIFY", "dataflow_modify",
"ROLE_DEPLOY", "dataflow_deploy",
"ROLE_DESTROY", "dataflow_destroy",
"ROLE_SCHEDULE", "dataflow_schedule"
);

ProviderRoleMapping providerRoleMapping = new ProviderRoleMapping();
providerRoleMapping.setMapOauthScopes(true);
providerRoleMapping.getRoleMappings().putAll(roleMappings);

Set<String> scopes = new HashSet<>();
scopes.add("foo-manage");
scopes.add("bar-view");
scopes.add("blubba-create");
scopes.add("foo-modify");
scopes.add("foo-deploy");
scopes.add("foo-destroy");
scopes.add("foo-schedule");
Set<String> roles = Set.of("dataflow_manage", "dataflow_view", "dataflow_deploy");

DefaultAuthoritiesMapper defaultAuthoritiesMapper = new DefaultAuthoritiesMapper("uaa", providerRoleMapping);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesMapper.mapScopesToAuthorities("uaa",
roles, null);

assertThat(authorities).hasSize(3);
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_DEPLOY", "ROLE_MANAGE", "ROLE_VIEW");
}
@Test
void that7MappedAuthoritiesAreReturned() throws Exception {
Map<String, String> roleMappings = Map.of(
"ROLE_MANAGE", "foo-manage",
"ROLE_VIEW", "bar-view",
"ROLE_CREATE", "blubba-create",
"ROLE_MODIFY", "foo-modify",
"ROLE_DEPLOY", "foo-deploy",
"ROLE_DESTROY", "foo-destroy",
"ROLE_SCHEDULE", "foo-schedule"
);

ProviderRoleMapping providerRoleMapping = new ProviderRoleMapping();
providerRoleMapping.setMapOauthScopes(true);
providerRoleMapping.getRoleMappings().putAll(roleMappings);

Set<String> scopes = Set.of(
"foo-manage",
"bar-view",
"blubba-create",
"foo-modify",
"foo-deploy",
"foo-destroy",
"foo-schedule"
);

DefaultAuthoritiesMapper defaultAuthoritiesMapper = new DefaultAuthoritiesMapper("uaa", providerRoleMapping);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesMapper.mapScopesToAuthorities("uaa",
scopes, null);

assertThat(authorities).hasSize(7);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_CREATE", "ROLE_DEPLOY", "ROLE_DESTROY", "ROLE_MANAGE", "ROLE_MODIFY",
"ROLE_SCHEDULE", "ROLE_VIEW");
}

@Test
public void testThat3MappedAuthoritiesAreReturnedForDefaultMapping() throws Exception {
void that3MappedAuthoritiesAreReturnedForDefaultMapping() throws Exception {
ProviderRoleMapping providerRoleMapping = new ProviderRoleMapping();
providerRoleMapping.setMapOauthScopes(true);

Set<String> scopes = new HashSet<>();
scopes.add("dataflow.manage");
scopes.add("dataflow.view");
scopes.add("dataflow.create");
Set<String> scopes = Set.of(
"dataflow.manage",
"dataflow.view",
"dataflow.create"
);

DefaultAuthoritiesMapper defaultAuthoritiesExtractor = new DefaultAuthoritiesMapper("uaa", providerRoleMapping);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesExtractor.mapScopesToAuthorities("uaa",
scopes, null);

assertThat(authorities).hasSize(3);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_MANAGE", "ROLE_CREATE", "ROLE_VIEW");
}

@Test
public void testThat7MappedAuthoritiesAreReturnedForDefaultMappingWithoutMappingScopes() throws Exception {
Set<String> scopes = new HashSet<>();
scopes.add("dataflow.manage");
scopes.add("dataflow.view");
scopes.add("dataflow.create");
void that7MappedAuthoritiesAreReturnedForDefaultMappingWithoutMappingScopes() throws Exception {
Set<String> scopes = Set.of(
"dataflow.manage",
"dataflow.view",
"dataflow.create"
);

DefaultAuthoritiesMapper defaultAuthoritiesExtractor = new DefaultAuthoritiesMapper("uaa", false);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesExtractor.mapScopesToAuthorities("uaa",
scopes, null);

assertThat(authorities).hasSize(7);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_CREATE", "ROLE_DEPLOY", "ROLE_DESTROY", "ROLE_MANAGE", "ROLE_MODIFY",
"ROLE_SCHEDULE", "ROLE_VIEW");
}

@Test
public void testThat2MappedAuthoritiesAreReturnedForDefaultMapping() throws Exception {
Set<String> scopes = new HashSet<>();
scopes.add("dataflow.view");
scopes.add("dataflow.create");
void that2MappedAuthoritiesAreReturnedForDefaultMapping() throws Exception {
Set<String> scopes = Set.of(
"dataflow.view",
"dataflow.create"
);

DefaultAuthoritiesMapper defaultAuthoritiesExtractor = new DefaultAuthoritiesMapper("uaa", true);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesExtractor.mapScopesToAuthorities("uaa",
Expand All @@ -178,19 +215,18 @@ public void testThat2MappedAuthoritiesAreReturnedForDefaultMapping() throws Exce
}

@Test
public void testThat7AuthoritiesAreReturnedAndOneOAuthScopeCoversMultipleServerRoles() throws Exception {
Map<String, String> roleMappings = new HashMap<>();
roleMappings.put("ROLE_MANAGE", "foo-manage");
roleMappings.put("ROLE_VIEW", "foo-manage");
roleMappings.put("ROLE_DEPLOY", "foo-manage");
roleMappings.put("ROLE_DESTROY", "foo-manage");
roleMappings.put("ROLE_MODIFY", "foo-manage");
roleMappings.put("ROLE_SCHEDULE", "foo-manage");
roleMappings.put("ROLE_CREATE", "blubba-create");

Set<String> scopes = new HashSet<>();
scopes.add("foo-manage");
scopes.add("blubba-create");
void that7AuthoritiesAreReturnedAndOneOAuthScopeCoversMultipleServerRoles() throws Exception {
Map<String, String> roleMappings = Map.of(
"ROLE_MANAGE", "foo-manage",
"ROLE_VIEW", "foo-manage",
"ROLE_DEPLOY", "foo-manage",
"ROLE_DESTROY", "foo-manage",
"ROLE_MODIFY", "foo-manage",
"ROLE_SCHEDULE", "foo-manage",
"ROLE_CREATE", "blubba-create"
);

Set<String> scopes = Set.of("foo-manage", "blubba-create");

DefaultAuthoritiesMapper defaultAuthoritiesExtractor = new DefaultAuthoritiesMapper("uaa", true, roleMappings);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesExtractor.mapScopesToAuthorities("uaa",
Expand All @@ -203,61 +239,64 @@ public void testThat7AuthoritiesAreReturnedAndOneOAuthScopeCoversMultipleServerR
}

@Test
public void testThatUriStyleScopeRemovesLeadingPart() throws Exception {
Map<String, String> roleMappings = new HashMap<>();
roleMappings.put("ROLE_MANAGE", "foo-manage");
roleMappings.put("ROLE_VIEW", "foo-manage");
roleMappings.put("ROLE_DEPLOY", "foo-manage");
roleMappings.put("ROLE_DESTROY", "foo-manage");
roleMappings.put("ROLE_MODIFY", "foo-manage");
roleMappings.put("ROLE_SCHEDULE", "foo-manage");
roleMappings.put("ROLE_CREATE", "blubba-create");

Set<String> scopes = new HashSet<>();
scopes.add("api://foobar/foo-manage");
scopes.add("blubba-create");
void thatUriStyleScopeRemovesLeadingPart() throws Exception {
Map<String, String> roleMappings = Map.of(
"ROLE_MANAGE", "foo-manage",
"ROLE_VIEW", "foo-manage",
"ROLE_DEPLOY", "foo-manage",
"ROLE_DESTROY", "foo-manage",
"ROLE_MODIFY", "foo-manage",
"ROLE_SCHEDULE", "foo-manage",
"ROLE_CREATE", "blubba-create"
);

Set<String> scopes = Set.of("api://foobar/foo-manage", "blubba-create");

DefaultAuthoritiesMapper defaultAuthoritiesExtractor = new DefaultAuthoritiesMapper("uaa", true, roleMappings);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesExtractor.mapScopesToAuthorities("uaa",
scopes, null);

assertThat(authorities).hasSize(7);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_CREATE", "ROLE_DEPLOY", "ROLE_DESTROY", "ROLE_MANAGE", "ROLE_MODIFY",
"ROLE_SCHEDULE", "ROLE_VIEW");
}

@Test
public void testThatUriStyleScopeParsingCanBeDisabled() throws Exception {
Map<String, String> roleMappings = new HashMap<>();
roleMappings.put("ROLE_MANAGE", "/ROLE/2000803042");
roleMappings.put("ROLE_VIEW", "/ROLE/2000803036");
roleMappings.put("ROLE_DEPLOY", "/ROLE/2000803039");
roleMappings.put("ROLE_DESTROY", "/ROLE/20008030340");
roleMappings.put("ROLE_MODIFY", "/ROLE/2000803037");
roleMappings.put("ROLE_SCHEDULE", "/ROLE/2000803038");
roleMappings.put("ROLE_CREATE", "/ROLE/2000803041");
void thatUriStyleScopeParsingCanBeDisabled() throws Exception {
Map<String, String> roleMappings = Map.of(
"ROLE_MANAGE", "/ROLE/2000803042",
"ROLE_VIEW", "/ROLE/2000803036",
"ROLE_DEPLOY", "/ROLE/2000803039",
"ROLE_DESTROY", "/ROLE/20008030340",
"ROLE_MODIFY", "/ROLE/2000803037",
"ROLE_SCHEDULE", "/ROLE/2000803038",
"ROLE_CREATE", "/ROLE/2000803041"
);

ProviderRoleMapping providerRoleMapping = new ProviderRoleMapping();
providerRoleMapping.setMapOauthScopes(true);
providerRoleMapping.setParseOauthScopePathParts(false);
providerRoleMapping.getRoleMappings().putAll(roleMappings);

Set<String> scopes = new HashSet<>();
scopes.add("/ROLE/2000803042");
scopes.add("/ROLE/2000803036");
scopes.add("/ROLE/2000803039");
scopes.add("/ROLE/20008030340");
scopes.add("/ROLE/2000803037");
scopes.add("/ROLE/2000803038");
scopes.add("/ROLE/2000803041");
Set<String> scopes = Set.of(
"/ROLE/2000803042",
"/ROLE/2000803036",
"/ROLE/2000803039",
"/ROLE/20008030340",
"/ROLE/2000803037",
"/ROLE/2000803038",
"/ROLE/2000803041"
);

DefaultAuthoritiesMapper defaultAuthoritiesMapper = new DefaultAuthoritiesMapper("uaa", providerRoleMapping);
Collection<? extends GrantedAuthority> authorities = defaultAuthoritiesMapper.mapScopesToAuthorities("uaa",
scopes, null);

assertThat(authorities).hasSize(7);
assertThat(authorities.stream().map(authority -> authority.getAuthority()).collect(Collectors.toList()))
assertThat(authorities)
.extracting(GrantedAuthority::getAuthority)
.containsExactlyInAnyOrder("ROLE_CREATE", "ROLE_DEPLOY", "ROLE_DESTROY", "ROLE_MANAGE", "ROLE_MODIFY",
"ROLE_SCHEDULE", "ROLE_VIEW");
}
Expand Down
6 changes: 6 additions & 0 deletions spring-cloud-dataflow-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<!-- database driver versions -->
<oracle-ojdbc8.version>21.9.0.0</oracle-ojdbc8.version>
<db2-jcc.version>11.5.9.0</db2-jcc.version>
<testcontainers-keycloak.version>3.4.0</testcontainers-keycloak.version>
<!-- questionable testing libs -->
<org-json.version>20240303</org-json.version>
<littleproxy.version>1.1.2</littleproxy.version>
Expand Down Expand Up @@ -257,6 +258,11 @@
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.github.dasniko</groupId>
<artifactId>testcontainers-keycloak</artifactId>
<version>${testcontainers-keycloak.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
auth.userInfoEndpoint(customizer -> {
customizer.userService(plainOauth2UserService).oidcUserService(oidcUserService);
});
auth.defaultSuccessUrl(authorizationProperties.getDashboardUrl());
});

http.oauth2ResourceServer(resourceserver -> {
Expand Down
10 changes: 10 additions & 0 deletions spring-cloud-dataflow-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
Expand Down Expand Up @@ -104,6 +109,11 @@
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.dasniko</groupId>
<artifactId>testcontainers-keycloak</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
Expand Down
Loading

0 comments on commit 5e5edc5

Please sign in to comment.