Skip to content

Commit

Permalink
jwt role claim authorization
Browse files Browse the repository at this point in the history
  • Loading branch information
clezag committed Oct 25, 2023
1 parent 26c083e commit 18b9726
Show file tree
Hide file tree
Showing 6 changed files with 59 additions and 21 deletions.
3 changes: 2 additions & 1 deletion calls.http
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&client_id=odh-mobility-datacollector-development
&client_secret=7bd46f8f-c296-416d-a13d-dc81e68d0830
&scope=openid

### Get access token for the writer (TEST DB)
# @name login
Expand All @@ -33,7 +34,7 @@ grant_type=password

#@host=https://mobility.share.opendatahub.com
@host=http://localhost:8999
@host=https://mobility.share.opendatahub.testingmachine.eu
#@host=https://mobility.share.opendatahub.testingmachine.eu
@authtoken = {{login.response.body.access_token}}


Expand Down
14 changes: 9 additions & 5 deletions writer/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ SPDX-License-Identifier: CC0-1.0
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
Expand All @@ -68,10 +72,6 @@ SPDX-License-Identifier: CC0-1.0
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>

<!-- To create JSON schema descriptions out of classes -->
<dependency>
Expand Down Expand Up @@ -111,7 +111,6 @@ SPDX-License-Identifier: CC0-1.0
<scope>runtime</scope>
</dependency>


<!--
Second level cache, to store entities that never change as
for example provenance records.
Expand All @@ -121,6 +120,11 @@ SPDX-License-Identifier: CC0-1.0
<artifactId>hibernate-jcache</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>

<!-- Hibernate: Rangetype support-->
<dependency>
Expand Down
44 changes: 42 additions & 2 deletions writer/src/main/java/it/bz/idm/bdp/writer/config/WebSecurity.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@

package it.bz.idm.bdp.writer.config;

import java.util.ArrayList;
import java.util.Collection;
import java.util.stream.Collectors;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
Expand All @@ -33,17 +42,48 @@ public AuthenticationManager authenticationManager(HttpSecurity http) throws Exc
return http.getSharedObject(AuthenticationManagerBuilder.class).build();
}

// For some reason, spring does not read the role claim from the jwt.
// Since we're basing our authorization on roles, we have to extend the spring security jwt converter to get that functionality.
// see https://stackoverflow.com/questions/65518172/spring-security-cant-extract-roles-from-jwt for reference
//
// Note that this is pretty specific to our use case and only maps roles. If we ever need scope or other claims,
// implement that here (and make it a separate class implementing the Converter interface)
@SuppressWarnings("unchecked")
private Converter<Jwt, AbstractAuthenticationToken> jwtConverter(){
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(jwt -> {
// see org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter.java
// This lambda functions as a replacement / reimplementation for that class
Collection<String> roles = new ArrayList<>();
Object roleClaim = jwt.getClaim("roles");
if (roleClaim instanceof Collection){
roles.addAll((Collection<String>) roleClaim);
}

return roles
.stream()
.map(role -> new SimpleGrantedAuthority(role))
.collect(Collectors.toList());
});
return jwtConverter;
}

@Bean
public SecurityFilterChain oauthFilter(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
// health check always accessible
.requestMatchers("/actuator/**")
.permitAll()
// Authorize based on role claim ROLE_ADMIN
.requestMatchers("/json/**")
.hasRole("ADMIN")
.anyRequest().permitAll());
http.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
// permitAll is ported over from legacy code, not sure if it doesn't make more sense to deny all other requests
.anyRequest().permitAll())
.oauth2Client(Customizer.withDefaults());
// Register the oauth server, and our custom jwt converter
http.oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter())));
return http.build();
}
}
2 changes: 1 addition & 1 deletion writer/src/main/resources/META-INF/persistence.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ SPDX-License-Identifier: CC0-1.0
<persistence-unit name="jpa-persistence" transaction-type="RESOURCE_LOCAL">
<properties>
<property name="hibernate.default_schema" value="intimev2"/>
<property name="hibernate.dialect" value="it.bz.idm.bdp.dal.util.PostgisJsonDialect"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider"/>
<property name="hibernate.hikari.dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource"/>
Expand Down
15 changes: 4 additions & 11 deletions writer/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,11 @@ management.endpoint.health.enabled=true
### Security
security.cors.allowedOrigins=${SECURITY_ALLOWED_ORIGINS:*}

#keycloak.auth-server-url=${KEYCLOAK_URL:https://auth.opendatahub.testingmachine.eu/auth}
#keycloak.ssl-required=${KEYCLOAK_SSL_REQUIRED:none}
#keycloak.realm=${KEYCLOAK_REALM:noi}
#keycloak.resource=${KEYCLOAK_CLIENT_ID:odh-mobility-writer-development}
#keycloak.use-resource-role-mappings=true
#keycloak.public-client=false
#keycloak.bearer-only=true
#keycloak.securityConstraints[0].authRoles[0]=ROLE_ADMIN
#keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/json/*

spring.security.oauth2.client.provider.keycloak.issuer-uri=${KEYCLOAK_URL:https://auth.opendatahub.testingmachine.eu/auth}/realms/${KEYCLOAK_REALM:noi}
spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
#spring.security.oauth2.client.provider.keycloak.user-name-attribute=preferred_username
spring.security.oauth2.resourceserver.jwt.issuer-uri=${KEYCLOAK_URL:https://auth.opendatahub.testingmachine.eu/auth}/realms/${KEYCLOAK_REALM:noi}

spring.security.oauth2.client.registration.keycloak.provider=keycloak
spring.security.oauth2.client.registration.keycloak.client-id=${KEYCLOAK_CLIENT_ID:odh-mobility-writer-development}
spring.security.oauth2.client.registration.keycloak.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.keycloak.scope=openid
Expand All @@ -45,6 +37,7 @@ hibernate.hikari.maximumPoolSize=${HIBERNATE_MAX_POOL_SIZE:2}
spring.datasource.url=jdbc:postgresql://${POSTGRES_SERVER:localhost}:${POSTGRES_PORT:5555}/${POSTGRES_DB:bdp}?currentSchema=${POSTGRES_SCHEMA:intimev2},public
spring.datasource.username=${POSTGRES_USERNAME:bdp}
spring.datasource.password=${POSTGRES_PASSWORD:password}
spring.datasource.hikari.maximum-pool-size=${HIBERNATE_MAX_POOL_SIZE:2}
spring.jpa.hibernate.ddl-auto=none
spring.jpa.hibernate.naming.implicit-strategy=it.bz.idm.bdp.dal.util.SchemaGeneratorImplicitNamingStrategy
spring.jpa.properties.hibernate.format_sql=true
Expand Down
2 changes: 1 addition & 1 deletion writer/src/test/resources/META-INF/persistence.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ SPDX-License-Identifier: CC0-1.0
<class>it.bz.idm.bdp.dal.MeasurementStringHistory</class>
<properties>
<property name="hibernate.default_schema" value="intimev2"/>
<property name="hibernate.dialect" value="it.bz.idm.bdp.dal.util.PostgisJsonDialect"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
<property name="hibernate.hbm2ddl.auto" value="none"/>
<property name="hibernate.connection.provider_class" value="org.hibernate.hikaricp.internal.HikariCPConnectionProvider"/>
<property name="hibernate.hikari.dataSourceClassName" value="org.postgresql.ds.PGSimpleDataSource"/>
Expand Down

0 comments on commit 18b9726

Please sign in to comment.