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

OZG-5176 OZG-5278 Get keycloak roles from jwt

parent 90ffd5d3
Branches
Tags
No related merge requests found
...@@ -19,7 +19,10 @@ ...@@ -19,7 +19,10 @@
*/ */
package de.ozgcloud.admin.security; package de.ozgcloud.admin.security;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -30,10 +33,12 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; ...@@ -30,10 +33,12 @@ 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.oauth2.core.oidc.StandardClaimNames; import org.springframework.security.oauth2.core.oidc.StandardClaimNames;
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;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import de.ozgcloud.admin.common.user.UserRole; import de.ozgcloud.admin.common.user.UserRole;
import de.ozgcloud.admin.environment.OAuth2Properties;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@Configuration @Configuration
...@@ -44,6 +49,12 @@ public class SecurityConfiguration { ...@@ -44,6 +49,12 @@ public class SecurityConfiguration {
private final AdminAuthenticationEntryPoint authenticationEntryPoint; private final AdminAuthenticationEntryPoint authenticationEntryPoint;
private final OAuth2Properties oAuth2Properties;
static final String RESOURCE_ACCESS_KEY = "resource_access";
static final String ROLES_KEY = "roles";
@Bean @Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception { SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
...@@ -75,4 +86,25 @@ public class SecurityConfiguration { ...@@ -75,4 +86,25 @@ public class SecurityConfiguration {
return jwtConverter; return jwtConverter;
} }
List<String> getKeycloakRolesFromJwt(Jwt jwt) {
return Optional.ofNullable(jwt.getClaimAsMap(RESOURCE_ACCESS_KEY))
.flatMap(resourceAccessMap -> getMap(resourceAccessMap, oAuth2Properties.getResource()))
.flatMap(adminClientMap -> getList(adminClientMap, ROLES_KEY))
.orElse(Collections.emptyList());
}
@SuppressWarnings("unchecked")
private Optional<Map<String, Object>> getMap(Map<String, Object> map, String mapKey) {
return Optional.ofNullable(map.get(mapKey))
.filter(Map.class::isInstance)
.map(obj -> (Map<String, Object>) obj);
}
@SuppressWarnings("unchecked")
private Optional<List<String>> getList(Map<String, Object> map, String mapKey) {
return Optional.ofNullable(map.get(mapKey))
.filter(List.class::isInstance)
.map(obj -> (List<String>) obj);
}
} }
/*
* Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur
* Zentrales IT-Management
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.admin.security;
import static de.ozgcloud.admin.security.SecurityConfiguration.*;
import java.util.List;
import java.util.Map;
import org.springframework.security.oauth2.jwt.Jwt;
public class JwtTestFactory {
public static final String ROLE_1 = "ADMIN_ADMIN";
public static final String ROLE_2 = "Lower_case";
public static final String ROLE_3 = "UPPER";
public static final String AUTH_RESOURCE = "admin";
public static Jwt create() {
return createBuilder().build();
}
public static Jwt.Builder createWithRoles(List<String> roles) {
return createBuilder().claim(RESOURCE_ACCESS_KEY, Map.of(AUTH_RESOURCE, Map.of(ROLES_KEY, roles)));
}
// private static Map<String, Object> readResourceAccessClaim() {
// var claimsJson = TestUtils.loadTextFile("jsonTemplates/security/resource_access.template.json");
// var mapper = new ObjectMapper();
// try {
// return mapper.readValue(claimsJson, new TypeReference<Map<String, Object>>() {
// });
// } catch (IOException e) {
// throw new RuntimeException(e);
// }
// }
public static Jwt.Builder createBuilder() {
return Jwt.withTokenValue("AAAA").header("aa", "bb");
}
}
/*
* Copyright (c) 2024. Das Land Schleswig-Holstein vertreten durch das Ministerium für Energiewende, Klimaschutz, Umwelt und Natur
* Zentrales IT-Management
*
* Lizenziert unter der EUPL, Version 1.2 oder - sobald
* diese von der Europäischen Kommission genehmigt wurden -
* Folgeversionen der EUPL ("Lizenz");
* Sie dürfen dieses Werk ausschließlich gemäß
* dieser Lizenz nutzen.
* Eine Kopie der Lizenz finden Sie hier:
*
* https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
*
* Sofern nicht durch anwendbare Rechtsvorschriften
* gefordert oder in schriftlicher Form vereinbart, wird
* die unter der Lizenz verbreitete Software "so wie sie
* ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
* ausdrücklich oder stillschweigend - verbreitet.
* Die sprachspezifischen Genehmigungen und Beschränkungen
* unter der Lizenz sind dem Lizenztext zu entnehmen.
*/
package de.ozgcloud.admin.security;
import static java.util.Collections.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.security.oauth2.jwt.Jwt;
import de.ozgcloud.admin.environment.OAuth2Properties;
class SecurityConfigurationTest {
@Spy
@InjectMocks
private SecurityConfiguration securityConfiguration;
@Mock
private AdminAuthenticationEntryPoint authenticationEntryPoint;
@Mock
private OAuth2Properties oAuth2Properties;
@BeforeEach
void mock() {
when(oAuth2Properties.getResource()).thenReturn(JwtTestFactory.AUTH_RESOURCE);
}
@DisplayName("get keycloak roles from claims")
@Nested
class TestGetKeycloakRolesFromClaims {
@DisplayName("should return empty if resource_access.admin.roles are missing")
@ParameterizedTest
@MethodSource("getIncompleteJwt")
void shouldReturnEmptyIfResourceAccessAdminRolesAreMissing(Jwt incompleteJwt) {
var rolesList = securityConfiguration.getKeycloakRolesFromJwt(incompleteJwt);
assertThat(rolesList).isEmpty();
}
private static Stream<Arguments> getIncompleteJwt() {
return Stream.of(JwtTestFactory.create(),
JwtTestFactory.createBuilder().claim("resource_access", Map.of()).build(),
JwtTestFactory.createBuilder().claim("resource_access", Map.of("admin", Map.of())).build(),
JwtTestFactory.createWithRoles(emptyList()).build())
.map(Arguments::of);
}
@DisplayName("should return resource_access.admin.roles list")
@Test
void shouldReturnEmptyIfResourceAccessAdminRolesList() {
var expectedRoles = List.of(JwtTestFactory.ROLE_1, JwtTestFactory.ROLE_2, JwtTestFactory.ROLE_3);
var jwtWithClaims = JwtTestFactory.createWithRoles(expectedRoles).build();
var roles = securityConfiguration.getKeycloakRolesFromJwt(jwtWithClaims);
assertThat(roles).isEqualTo(expectedRoles);
}
}
}
{
"admin": {
"roles": [
"ADMIN_ADMIN"
]
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment