This is an automated email from the ASF dual-hosted git repository. ahuber pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push: new f96ddd329b ISIS-3283: fixes unreachable code in keycloak integration f96ddd329b is described below commit f96ddd329ba8ab54fa94031f38f0425235ef8ed1 Author: andi-huber <ahu...@apache.org> AuthorDate: Sat Nov 19 08:51:35 2022 +0100 ISIS-3283: fixes unreachable code in keycloak integration --- security/keycloak/src/main/java/module-info.java | 1 + .../services/KeycloakOauth2UserService.java | 112 ++++++++++----------- 2 files changed, 56 insertions(+), 57 deletions(-) diff --git a/security/keycloak/src/main/java/module-info.java b/security/keycloak/src/main/java/module-info.java index ba9fcba7d5..b964fd68d4 100644 --- a/security/keycloak/src/main/java/module-info.java +++ b/security/keycloak/src/main/java/module-info.java @@ -39,4 +39,5 @@ module org.apache.causeway.security.keycloak { requires spring.security.oauth2.jose; requires spring.security.web; requires spring.web; + requires org.apache.causeway.commons; } \ No newline at end of file diff --git a/security/keycloak/src/main/java/org/apache/causeway/security/keycloak/services/KeycloakOauth2UserService.java b/security/keycloak/src/main/java/org/apache/causeway/security/keycloak/services/KeycloakOauth2UserService.java index 88e561b459..8fd40dc317 100644 --- a/security/keycloak/src/main/java/org/apache/causeway/security/keycloak/services/KeycloakOauth2UserService.java +++ b/security/keycloak/src/main/java/org/apache/causeway/security/keycloak/services/KeycloakOauth2UserService.java @@ -26,7 +26,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import org.springframework.lang.Nullable; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; @@ -42,8 +45,11 @@ import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtException; import org.springframework.util.CollectionUtils; +import org.apache.causeway.commons.internal.base._Casts; +import org.apache.causeway.commons.internal.base._NullSafe; import org.apache.causeway.core.config.CausewayConfiguration; +import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.val; @@ -65,7 +71,7 @@ public class KeycloakOauth2UserService extends OidcUserService { * {@link OidcUserRequest}. */ @Override - public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException { + public OidcUser loadUser(final OidcUserRequest userRequest) throws OAuth2AuthenticationException { OidcUser user = super.loadUser(userRequest); @@ -83,75 +89,45 @@ public class KeycloakOauth2UserService extends OidcUserService { * @param userRequest * @return */ - private Collection<? extends GrantedAuthority> extractKeycloakAuthorities(OidcUserRequest userRequest) { + private Collection<? extends GrantedAuthority> extractKeycloakAuthorities(final OidcUserRequest userRequest) { Jwt token = parseJwt(userRequest.getAccessToken().getTokenValue()); - List<String> combinedRoles = new ArrayList<>(); + final List<String> combinedRoles = new ArrayList<>(); if(causewayConfiguration.getSecurity().getKeycloak().isExtractClientRoles()) { - // attempt to parse out 'resource_access.${client_id}.roles' - - val resourceObj = token.getClaims().get("resource_access"); - if (resourceObj instanceof Map) { - @SuppressWarnings("rawtypes") - val resourceMap = (Map) resourceObj; - - val clientId = userRequest.getClientRegistration().getClientId(); - val clientResourceObj = resourceMap.get(clientId); - if(clientResourceObj instanceof Map) { - @SuppressWarnings("rawtypes") - val clientResource = (Map) clientResourceObj; - if (!CollectionUtils.isEmpty(clientResource)) { - val clientRolesObj = clientResource.get("roles"); - if (clientResourceObj instanceof List) { - @SuppressWarnings("unchecked") - val clientRoles = (List<Object>) clientRolesObj; - if (!CollectionUtils.isEmpty(clientRoles)) { - val prefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getClientRolePrefix()).orElse(""); - clientRoles.stream() - .filter(Objects::nonNull) - .map(clientRole -> prefix + clientRole) - .forEach(combinedRoles::add); - } - } - } - } - } + val rolePrefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getClientRolePrefix()).orElse(""); + asNonEmptyMap(token.getClaims().get("resource_access")) + .ifPresent(resourceMap->{ + final String clientId = userRequest.getClientRegistration().getClientId(); + asNonEmptyMap(resourceMap.get(clientId)) + .flatMap(clientResource->asNonEmptyCollection(clientResource.get("roles"))) + .ifPresent(clientRoles-> + forEachNonNullIn(clientRoles, clientRole -> combinedRoles.add(rolePrefix + clientRole)) + ); + }); } if (causewayConfiguration.getSecurity().getKeycloak().isExtractRealmRoles()) { // attempt to parse out 'realm_access.roles' - val realmAccessObj = token.getClaims().get("realm_access"); - if (realmAccessObj instanceof Map) { - @SuppressWarnings("rawtypes") - val realmAccessMap = (Map)realmAccessObj; - Object realmRolesObj = realmAccessMap.get("roles"); - if (realmRolesObj instanceof List) { - @SuppressWarnings("unchecked") - val realmRoles = (List<Object>) realmRolesObj; - val prefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getRealmRolePrefix()).orElse(""); - realmRoles.stream() - .filter(Objects::nonNull) - .map(realmRole -> prefix + realmRole) - .forEach(combinedRoles::add); - } - } + val rolePrefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getRealmRolePrefix()).orElse(""); + asNonEmptyMap(token.getClaims().get("realm_access")) + .ifPresent(realmAccessMap->{ + asNonEmptyCollection(realmAccessMap.get("roles")) + .ifPresent(realmRoles->{ + forEachNonNullIn(realmRoles, realmRole -> combinedRoles.add(rolePrefix + realmRole)); + }); + }); } if (causewayConfiguration.getSecurity().getKeycloak().isExtractRoles()) { // attempt to parse out 'roles' - val rolesObj = token.getClaims().get("roles"); - if (rolesObj instanceof List) { - @SuppressWarnings("unchecked") - val roles = (List<Object>) rolesObj; - val prefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getRolePrefix()).orElse(""); - roles.stream() - .filter(Objects::nonNull) - .map(role -> prefix + role) - .forEach(combinedRoles::add); - } + val rolePrefix = Optional.ofNullable(causewayConfiguration.getSecurity().getKeycloak().getRolePrefix()).orElse(""); + asNonEmptyCollection(token.getClaims().get("roles")) + .ifPresent(roles->{ + forEachNonNullIn(roles, role -> combinedRoles.add(rolePrefix + role)); + }); } Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList(combinedRoles.toArray(new String[]{})); @@ -161,7 +137,29 @@ public class KeycloakOauth2UserService extends OidcUserService { } - private Jwt parseJwt(String accessTokenValue) { + // -- HELPER + + @SuppressWarnings("rawtypes") + private Optional<Map> asNonEmptyMap(final @Nullable Object x){ + return _Casts.castTo(Map.class, x) + .filter(Predicate.not(_NullSafe::isEmpty)); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private void forEachNonNullIn(final @NonNull Collection x, final @NonNull Consumer<Object> _do){ + x.stream() + .filter(Objects::nonNull) + .forEach(_do); + } + + @SuppressWarnings("rawtypes") + private Optional<Collection> asNonEmptyCollection(final @Nullable Object x){ + return _Casts.castTo(Collection.class, x) + .filter(Predicate.not(_NullSafe::isEmpty)); + } + + + private Jwt parseJwt(final String accessTokenValue) { try { // Token is already verified by spring security infrastructure return jwtDecoder.decode(accessTokenValue);