diff --git a/build.gradle b/build.gradle index cd70a99..41d35f8 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { apply plugin: 'io.spring.dependency-management' group = 'de.jottyfan' -version = '1.2.9' +version = '1.3.0' description = """timetrack""" diff --git a/src/main/java/de/jottyfan/timetrack/config/AuthorizationConfiguration.java b/src/main/java/de/jottyfan/timetrack/config/AuthorizationConfiguration.java new file mode 100644 index 0000000..0af8b08 --- /dev/null +++ b/src/main/java/de/jottyfan/timetrack/config/AuthorizationConfiguration.java @@ -0,0 +1,78 @@ +package de.jottyfan.timetrack.config; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; +import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority; +import org.springframework.security.oauth2.core.user.OAuth2UserAuthority; + +/** + * + * @author jotty + * + */ +@Configuration +public class AuthorizationConfiguration { + + private static final String REALM_ACCESS_CLAIM = "realm_access"; + private static final String ROLES_CLAIM = "roles"; + private static final String RESOURCE_ACCESS_CLAIM = "resource_access"; + + @Value("${spring.security.oauth2.client.registration.keycloak.client-id}") + private String clientId; + + @Bean + GrantedAuthoritiesMapper userAuthoritiesMapperForKeycloak() { + return authorities -> { + Set mappedAuthorities = new HashSet<>(); + var authority = authorities.iterator().next(); + boolean isOidc = authority instanceof OidcUserAuthority; + + if (isOidc) { + var oidcUserAuthority = (OidcUserAuthority) authority; + var userInfo = oidcUserAuthority.getUserInfo(); + + if (userInfo.hasClaim(REALM_ACCESS_CLAIM)) { + var realmAccess = userInfo.getClaimAsMap(REALM_ACCESS_CLAIM); + @SuppressWarnings("unchecked") + var roles = (Collection) realmAccess.get(ROLES_CLAIM); + mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles)); + } + if (userInfo.hasClaim(RESOURCE_ACCESS_CLAIM)) { + var resourceAccess = userInfo.getClaimAsMap(RESOURCE_ACCESS_CLAIM); + if (resourceAccess.containsKey(clientId)) { + @SuppressWarnings("unchecked") + var roles = (Collection) ((Map) resourceAccess.get(clientId)).get(ROLES_CLAIM); + mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles)); + } + } + } else { + var oauth2UserAuthority = (OAuth2UserAuthority) authority; + Map userAttributes = oauth2UserAuthority.getAttributes(); + + if (userAttributes.containsKey(REALM_ACCESS_CLAIM)) { + @SuppressWarnings("unchecked") + var realmAccess = (Map) userAttributes.get(REALM_ACCESS_CLAIM); + @SuppressWarnings("unchecked") + var roles = (Collection) realmAccess.get(ROLES_CLAIM); + mappedAuthorities.addAll(generateAuthoritiesFromClaim(roles)); + } + } + + return mappedAuthorities; + }; + } + + private Collection generateAuthoritiesFromClaim(Collection roles) { + return roles.stream().map(role -> new SimpleGrantedAuthority("ROLE_" + role)).collect(Collectors.toList()); + } +} diff --git a/src/main/java/de/jottyfan/timetrack/config/SecurityConfiguration.java b/src/main/java/de/jottyfan/timetrack/config/SecurityConfiguration.java index b44f747..ed30710 100644 --- a/src/main/java/de/jottyfan/timetrack/config/SecurityConfiguration.java +++ b/src/main/java/de/jottyfan/timetrack/config/SecurityConfiguration.java @@ -2,20 +2,19 @@ package de.jottyfan.timetrack.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.client.oidc.web.logout.OidcClientInitiatedLogoutSuccessHandler; import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; -import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; -import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.session.NullAuthenticatedSessionStrategy; import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; /** * - * @author henkej + * @author jotty * */ @Configuration @@ -36,19 +35,9 @@ public class SecurityConfiguration { .oauth2Login(o -> o.defaultSuccessUrl("/")) .logout(o -> o.logoutSuccessHandler(new OidcClientInitiatedLogoutSuccessHandler(crr))) .authorizeHttpRequests(o -> o.requestMatchers("/public/**").permitAll().anyRequest().authenticated()) - .oauth2ResourceServer(o -> o.jwt(j -> roleConverter())) + .oauth2ResourceServer(o -> o.jwt(Customizer.withDefaults())) .sessionManagement(o -> o.init(sec)); // @formatter:on return sec.build(); } - - @Bean - public JwtAuthenticationConverter roleConverter() { - JwtGrantedAuthoritiesConverter gac = new JwtGrantedAuthoritiesConverter(); - gac.setAuthorityPrefix("ROLE_"); - gac.setAuthoritiesClaimName("roles"); - JwtAuthenticationConverter jac = new JwtAuthenticationConverter(); - jac.setJwtGrantedAuthoritiesConverter(gac); - return jac; } -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 937ea55..bded151 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -20,5 +20,5 @@ spring.security.oauth2.client.provider.keycloak.jwk-set-uri = ${keycloak.openid- spring.security.oauth2.client.provider.keycloak.user-name-attribute = preferred_username # application -server.port = 8083 +server.port = 9001 server.servlet.context-path = /timetrack diff --git a/src/main/resources/templates/calendar/calendar.html b/src/main/resources/templates/calendar/calendar.html index 3ec5428..f0a6701 100644 --- a/src/main/resources/templates/calendar/calendar.html +++ b/src/main/resources/templates/calendar/calendar.html @@ -6,7 +6,7 @@ Kalender
    - +
-
+