Skip to content
Snippets Groups Projects
Commit 3bf479f0 authored by Jan Zickermann's avatar Jan Zickermann
Browse files

OZG-5176 OZG-5278 Add `convertJwtToGrantedAuthorities`

parent 75b8a517
Branches
Tags
No related merge requests found
...@@ -19,10 +19,13 @@ ...@@ -19,10 +19,13 @@
*/ */
package de.ozgcloud.admin.security; package de.ozgcloud.admin.security;
import static java.util.stream.Collectors.*;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -32,6 +35,8 @@ import org.springframework.security.config.annotation.method.configuration.Enabl ...@@ -32,6 +35,8 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames; import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
...@@ -53,6 +58,8 @@ public class SecurityConfiguration { ...@@ -53,6 +58,8 @@ public class SecurityConfiguration {
static final String RESOURCE_ACCESS_KEY = "resource_access"; static final String RESOURCE_ACCESS_KEY = "resource_access";
static final String SIMPLE_GRANT_AUTHORITY_PREFIX = "ROLE_";
static final String ROLES_KEY = "roles"; static final String ROLES_KEY = "roles";
@Bean @Bean
...@@ -81,11 +88,23 @@ public class SecurityConfiguration { ...@@ -81,11 +88,23 @@ public class SecurityConfiguration {
@Bean @Bean
JwtAuthenticationConverter jwtAuthenticationConverter() { JwtAuthenticationConverter jwtAuthenticationConverter() {
var jwtConverter = new JwtAuthenticationConverter(); var jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(jwt -> List.of(() -> "ROLE_USER")); jwtConverter.setJwtGrantedAuthoritiesConverter(
this::convertJwtToGrantedAuthorities);
jwtConverter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME); jwtConverter.setPrincipalClaimName(StandardClaimNames.PREFERRED_USERNAME);
return jwtConverter; return jwtConverter;
} }
Set<GrantedAuthority> convertJwtToGrantedAuthorities(Jwt jwt) {
return getKeycloakRolesFromJwt(jwt)
.stream()
.map(this::mapRoleStringToGrantedAuthority)
.collect(toSet());
}
private GrantedAuthority mapRoleStringToGrantedAuthority(String role) {
return new SimpleGrantedAuthority(SIMPLE_GRANT_AUTHORITY_PREFIX + role);
}
List<String> getKeycloakRolesFromJwt(Jwt jwt) { List<String> getKeycloakRolesFromJwt(Jwt jwt) {
return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS_KEY)) return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS_KEY))
.flatMap(resourceAccessMap -> getMap(resourceAccessMap, oAuth2Properties.getResource())) .flatMap(resourceAccessMap -> getMap(resourceAccessMap, oAuth2Properties.getResource()))
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
*/ */
package de.ozgcloud.admin.security; package de.ozgcloud.admin.security;
import static de.ozgcloud.admin.security.JwtTestFactory.*;
import static de.ozgcloud.admin.security.SecurityConfiguration.*; import static de.ozgcloud.admin.security.SecurityConfiguration.*;
import static java.util.Collections.*; import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.*;
...@@ -28,6 +29,7 @@ import static org.mockito.Mockito.*; ...@@ -28,6 +29,7 @@ import static org.mockito.Mockito.*;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
...@@ -40,8 +42,13 @@ import org.junit.jupiter.params.provider.MethodSource; ...@@ -40,8 +42,13 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Spy; import org.mockito.Spy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.jwt.Jwt;
import com.thedeanda.lorem.LoremIpsum;
import de.ozgcloud.admin.environment.OAuth2Properties; import de.ozgcloud.admin.environment.OAuth2Properties;
class SecurityConfigurationTest { class SecurityConfigurationTest {
...@@ -49,12 +56,86 @@ class SecurityConfigurationTest { ...@@ -49,12 +56,86 @@ class SecurityConfigurationTest {
@Spy @Spy
@InjectMocks @InjectMocks
private SecurityConfiguration securityConfiguration; private SecurityConfiguration securityConfiguration;
@Mock
private AdminAuthenticationEntryPoint authenticationEntryPoint;
@Mock @Mock
private OAuth2Properties oAuth2Properties; private OAuth2Properties oAuth2Properties;
@DisplayName("jwt authentication converter")
@Nested
class TestJwtAuthenticationConverter {
private final String roleString = "ROLE_Test";
@BeforeEach
void mock() {
doReturn(Set.of(new SimpleGrantedAuthority(roleString))).when(securityConfiguration).convertJwtToGrantedAuthorities(any());
}
@DisplayName("should use preferred_username")
@Test
void shouldUsePreferredUsername() {
var preferredName = LoremIpsum.getInstance().getName();
var jwtWithPreferredName = JwtTestFactory.createBuilder()
.claim(StandardClaimNames.PREFERRED_USERNAME, preferredName)
.build();
var jwtAuthenticationConverter = securityConfiguration.jwtAuthenticationConverter();
var abstractAuthenticationToken = jwtAuthenticationConverter.convert(jwtWithPreferredName);
assertThat(abstractAuthenticationToken.getName()).isEqualTo(preferredName);
}
@DisplayName("should use granted authorities converter")
@Test
void shouldUseGrantedAuthoritiesConverter() {
var jwtWithRoles = JwtTestFactory.create();
var jwtAuthenticationConverter = securityConfiguration.jwtAuthenticationConverter();
var abstractAuthenticationToken = jwtAuthenticationConverter.convert(jwtWithRoles);
var securityRoleStrings = abstractAuthenticationToken.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList();
assertThat(securityRoleStrings).isEqualTo(List.of(roleString));
}
}
@DisplayName("convert jwt to granted authorities")
@Nested
class TestConvertJwtToGrantedAuthorities {
private List<String> expectedSecurityRoleStrings;
@BeforeEach
void mock() {
var keycloakRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
expectedSecurityRoleStrings = keycloakRoles.stream().map(role -> SIMPLE_GRANT_AUTHORITY_PREFIX + role).toList();
doReturn(keycloakRoles).when(securityConfiguration).getKeycloakRolesFromJwt(any());
}
@DisplayName("should call get keycloak roles from jwt")
@Test
void shouldCallGetKeycloakRolesFromJwt() {
var jwt = JwtTestFactory.create();
securityConfiguration.convertJwtToGrantedAuthorities(jwt);
verify(securityConfiguration).getKeycloakRolesFromJwt(jwt);
}
@DisplayName("should return granted authorities with ROLE_ prefix")
@Test
void shouldReturnGrantedAuthoritiesWithRolePrefix() {
var jwt = JwtTestFactory.create();
var grantedAuthorities = securityConfiguration.convertJwtToGrantedAuthorities(jwt);
var securityRoles = grantedAuthorities
.stream()
.map(GrantedAuthority::getAuthority).toList();
assertThat(securityRoles).containsAll(expectedSecurityRoleStrings);
}
}
@DisplayName("get keycloak roles from jwt") @DisplayName("get keycloak roles from jwt")
@Nested @Nested
class TestGetKeycloakRolesFromJwt { class TestGetKeycloakRolesFromJwt {
...@@ -84,7 +165,7 @@ class SecurityConfigurationTest { ...@@ -84,7 +165,7 @@ class SecurityConfigurationTest {
@DisplayName("should return resource_access.admin.roles list") @DisplayName("should return resource_access.admin.roles list")
@Test @Test
void shouldReturnResourceAccessAdminRolesList() { void shouldReturnResourceAccessAdminRolesList() {
var expectedRoles = List.of(JwtTestFactory.ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3); var expectedRoles = List.of(ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
var jwtWithRoles = JwtTestFactory.createWithRoles(expectedRoles).build(); var jwtWithRoles = JwtTestFactory.createWithRoles(expectedRoles).build();
var roleStrings = securityConfiguration.getKeycloakRolesFromJwt(jwtWithRoles); var roleStrings = securityConfiguration.getKeycloakRolesFromJwt(jwtWithRoles);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment