diff --git a/src/main/java/com/inteligr8/activiti/ais/AbstractIdentityServiceActivitiAuthenticator.java b/src/main/java/com/inteligr8/activiti/ais/AbstractIdentityServiceActivitiAuthenticator.java index f73d569..ccb3d0f 100644 --- a/src/main/java/com/inteligr8/activiti/ais/AbstractIdentityServiceActivitiAuthenticator.java +++ b/src/main/java/com/inteligr8/activiti/ais/AbstractIdentityServiceActivitiAuthenticator.java @@ -2,19 +2,20 @@ package com.inteligr8.activiti.ais; import java.util.Collection; import java.util.HashSet; +import java.util.Map.Entry; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakSecurityContext; -import org.keycloak.adapters.OidcKeycloakAccount; import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken; import org.keycloak.representations.AccessToken; +import org.keycloak.representations.AccessToken.Access; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; -import com.activiti.security.identity.service.authentication.provider.IdentityServiceAuthenticationToken; import com.inteligr8.activiti.Authenticator; public abstract class AbstractIdentityServiceActivitiAuthenticator implements Authenticator { @@ -23,33 +24,76 @@ public abstract class AbstractIdentityServiceActivitiAuthenticator implements Au - protected AccessToken getOidcAccessToken(Authentication auth) { + protected Set getRoles(Authentication auth) { + Set authorities = this.toSet(auth.getAuthorities()); + this.logger.debug("Auto-parsed authorities: {}", authorities); + + if (authorities.isEmpty()) { + AccessToken atoken = this.getKeycloakAccessToken(auth); + if (atoken == null) { + this.logger.debug("Access token not available"); + return null; + } else if (atoken.getRealmAccess() == null && atoken.getResourceAccess().isEmpty()) { + this.logger.debug("Access token has no role information"); + return null; + } else { + if (atoken.getRealmAccess() != null) { + this.logger.debug("Access token realm roles: {}", atoken.getRealmAccess().getRoles()); + authorities.addAll(atoken.getRealmAccess().getRoles()); + } + + for (Entry resourceAccess : atoken.getResourceAccess().entrySet()) { + this.logger.debug("Access token resources '{}' roles: {}", resourceAccess.getKey(), resourceAccess.getValue().getRoles()); + authorities.addAll(resourceAccess.getValue().getRoles()); + } + + this.logger.debug("Access token authorities: {}", authorities); + } + } + + return authorities; + } + + protected AccessToken getKeycloakAccessToken(Authentication auth) { KeycloakSecurityContext ksc = this.getKeycloakSecurityContext(auth); - return ksc.getToken(); + return ksc == null ? null : ksc.getToken(); } @SuppressWarnings("unchecked") protected KeycloakSecurityContext getKeycloakSecurityContext(Authentication auth) { - if (auth instanceof KeycloakAuthenticationToken) { - this.logger.debug("Fetching KeycloakSecurityContext from KeycloakAuthenticationToken"); - if (auth.getPrincipal() instanceof KeycloakPrincipal) { - return ((KeycloakPrincipal)auth.getPrincipal()).getKeycloakSecurityContext(); - } else { - return null; - } - } else if (auth instanceof IdentityServiceAuthenticationToken) { - this.logger.debug("Fetching KeycloakSecurityContext from IdentityServiceAuthenticationToken"); - OidcKeycloakAccount account = ((IdentityServiceAuthenticationToken)auth).getAccount(); - return account == null ? null : account.getKeycloakSecurityContext(); - } else { + if (auth.getCredentials() instanceof KeycloakSecurityContext) { + this.logger.debug("Found keycloak context in credentials"); + return (KeycloakSecurityContext)auth.getCredentials(); + } else if (auth.getPrincipal() instanceof KeycloakPrincipal) { + this.logger.debug("Found keycloak context in principal: {}", auth.getPrincipal()); + return ((KeycloakPrincipal)auth.getPrincipal()).getKeycloakSecurityContext(); + } else if (!(auth instanceof KeycloakAuthenticationToken)) { + this.logger.warn("Unexpected token: {}", auth.getClass()); return null; } + + KeycloakAuthenticationToken ktoken = (KeycloakAuthenticationToken)auth; + if (ktoken.getAccount() != null) { + this.logger.debug("Found keycloak context in account: {}", ktoken.getAccount().getPrincipal() == null ? null : ktoken.getAccount().getPrincipal().getName()); + return ktoken.getAccount().getKeycloakSecurityContext(); + } else { + this.logger.warn("Unable to find keycloak security context"); + this.logger.debug("Principal: {}", auth.getPrincipal()); + this.logger.debug("Account: {}", ktoken.getAccount()); + if (auth.getPrincipal() != null) + this.logger.debug("Principal type: {}", auth.getPrincipal().getClass()); + return null; + } } protected Set toSet(Collection grantedAuthorities) { - Set authorities = new HashSet<>(grantedAuthorities.size()); - for (GrantedAuthority grantedAuthority : grantedAuthorities) - authorities.add(grantedAuthority.getAuthority()); + Set authorities = new HashSet<>(Math.max(grantedAuthorities.size(), 16)); + for (GrantedAuthority grantedAuthority : grantedAuthorities) { + String authority = StringUtils.trimToNull(grantedAuthority.getAuthority()); + if (authority == null) + this.logger.warn("The granted authorities include an empty authority!?: '{}'", grantedAuthority.getAuthority()); + authorities.add(authority); + } return authorities; } } diff --git a/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiAppAuthenticator.java b/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiAppAuthenticator.java index a8e99ba..3c250ad 100644 --- a/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiAppAuthenticator.java +++ b/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiAppAuthenticator.java @@ -148,9 +148,9 @@ public class IdentityServiceActivitiAppAuthenticator extends AbstractIdentitySer } private User createUser(Authentication auth, Long tenantId) { - AccessToken atoken = this.getOidcAccessToken(auth); + AccessToken atoken = this.getKeycloakAccessToken(auth); if (atoken == null) { - this.logger.debug("The OIDC access token could not be found; using email to determine names: {}", auth.getName()); + this.logger.debug("The keycloak access token could not be found; using email to determine names: {}", auth.getName()); Matcher emailNamesMatcher = this.emailNamesPattern.matcher(auth.getName()); if (!emailNamesMatcher.matches()) { this.logger.warn("The email address could not be parsed for names: {}", auth.getName()); @@ -166,8 +166,11 @@ public class IdentityServiceActivitiAppAuthenticator extends AbstractIdentitySer } private void syncUserAuthorities(User user, Authentication auth, Long tenantId) { - Set authorities = this.toSet(auth.getAuthorities()); - this.logger.debug("OIDC authorities: {}", authorities); + Set authorities = this.getRoles(auth); + if (authorities == null) { + this.logger.debug("The user authorities could not be determined; skipping sync: {}", user.getEmail()); + return; + } // check Activiti groups User userWithGroups = this.userService.findUserByEmailFetchGroups(user.getEmail()); diff --git a/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiEngineAuthenticator.java b/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiEngineAuthenticator.java index ad02ad8..fcfffdd 100644 --- a/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiEngineAuthenticator.java +++ b/src/main/java/com/inteligr8/activiti/ais/IdentityServiceActivitiEngineAuthenticator.java @@ -106,8 +106,11 @@ public class IdentityServiceActivitiEngineAuthenticator extends AbstractIdentity } private void syncUserAuthorities(User user, Authentication auth) { - Set authorities = this.toSet(auth.getAuthorities()); - this.logger.debug("OIDC authorities: {}", authorities); + Set authorities = this.getRoles(auth); + if (authorities == null) { + this.logger.debug("The user authorities could not be determined; skipping sync: {}", user.getEmail()); + return; + } // check Activiti groups List groups = this.identityService.createGroupQuery()