Loading pom.xml +11 −5 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ <maven.compiler.target>11</maven.compiler.target> <maven.compiler.release>11</maven.compiler.release> <aps.version>1.11.1.1</aps.version> <keycloak.version>6.0.1</keycloak.version> <spring-security-oauth2.version>2.0.17.RELEASE</spring-security-oauth2.version> <aps.version>2.3.2</aps.version> <keycloak.version>10.0.2</keycloak.version> <spring-security-oauth2.version>2.5.2.RELEASE</spring-security-oauth2.version> <slf4j.version>1.7.26</slf4j.version> </properties> Loading @@ -37,6 +37,7 @@ <version>${keycloak.version}</version> <scope>provided</scope> </dependency> <!-- Needed for Activiti App Identity Service inheritance/override --> <dependency> <groupId>com.activiti</groupId> <artifactId>activiti-app</artifactId> Loading @@ -44,6 +45,7 @@ <classifier>classes</classifier> <scope>provided</scope> </dependency> <!-- Needed for the Activiti App Public API --> <dependency> <groupId>com.activiti</groupId> <artifactId>activiti-app-logic</artifactId> Loading @@ -53,6 +55,10 @@ </dependencies> <repositories> <repository> <id>alfresco-private</id> <url>https://artifacts.alfresco.com/nexus/content/groups/private</url> </repository> <repository> <id>activiti-releases</id> <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-enterprise-releases</url> Loading src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiEngineAuthenticator.javadeleted 100644 → 0 +0 −162 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.activiti.engine.IdentityService; import org.activiti.engine.identity.Group; import org.activiti.engine.identity.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; /** * This is an unused implementation for non-APS installation. It is not tested * and probably pointless. * * @author brian.long@yudrio.com */ @Component("keycloak-ext.activiti-engine.authenticator") @Lazy public class KeycloakActivitiEngineAuthenticator extends AbstractKeycloakActivitiAuthenticator { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private IdentityService identityService; @Value("${keycloak-ext.group.prefix:KEYCLOAK_}") private String groupPrefix; /** * This method validates that the user exists, if not, it creates the * missing user. Without this functionality, SSO straight up fails. */ @Override public void preAuthenticate(Authentication auth) throws AuthenticationException { User user = this.findUser(auth); if (user == null) { if (this.createMissingUser) { this.logger.debug("User does not yet exist; creating the user: {}", auth.getName()); user = this.createUser(auth); this.logger.debug("Created user: {} => {}", user.getId(), user.getEmail()); if (this.clearNewUserDefaultGroups) { this.logger.debug("Clearing groups: {}", user.getId()); List<Group> groups = this.identityService.createGroupQuery() .groupMember(user.getId()) .list(); for (Group group : groups) this.identityService.deleteMembership(user.getId(), group.getId()); } } else { this.logger.info("User does not exist; user creation is disabled: {}", auth.getName()); } } } /** * This method validates that the groups exist, if not, it creates the * missing ones. Without this functionality, SSO works, but the user's * authorities are not synchronized. */ @Override public void postAuthenticate(Authentication auth) throws AuthenticationException { User user = this.findUser(auth); this.logger.debug("Inspecting user: {} => {}", user.getId(), user.getEmail()); this.syncUserRoles(user, auth); } private User findUser(Authentication auth) { String email = auth.getName(); User user = this.identityService.createUserQuery() .userEmail(email) .singleResult(); return user; } private User createUser(Authentication auth) { User user = this.identityService.newUser(auth.getName()); user.setEmail(auth.getName()); this.identityService.saveUser(user); return user; } private void syncUserRoles(User user, Authentication auth) { Map<String, String> roles = this.getKeycloakRoles(auth); if (roles == null) { this.logger.debug("The user roles could not be determined; skipping sync: {}", user.getEmail()); return; } // check Activiti groups List<Group> groups = this.identityService.createGroupQuery() .groupMember(user.getEmail()) .list(); this.logger.debug("User is currently a member of {} groups", groups.size()); for (Group group : groups) { if (!group.getId().startsWith(this.groupPrefix) && this.syncInternalGroups) continue; this.logger.trace("Inspecting group: {} => {} ({})", group.getId(), group.getName(), group.getType()); if (roles.remove(this.activitiGroupIdToKeycloakRole(group.getId())) != null) { this.logger.trace("Group and membership already exist: {} => {}", user.getEmail(), group.getName()); // already a member of the group } else { if (this.syncGroupRemove) { this.logger.trace("Group membership not in OIDC token; removing from group: {} => {}", user.getEmail(), group.getName()); this.identityService.deleteMembership(user.getId(), group.getId()); } else { this.logger.debug("User/group membership sync disabled; not removing user from group: {} => {}", user.getId(), group.getId()); } } } this.logger.debug("Unaddressed OIDC roles: {}", roles); // check remainder/unaddressed roles for (Entry<String, String> role : roles.entrySet()) { this.logger.trace("Inspecting role: {}", role); Group group = this.identityService.createGroupQuery() .groupId(this.keycloakRoleToActivitiGroupId(role.getKey())) .singleResult(); if (group == null) { if (this.createMissingGroup) { this.logger.trace("Group does not exist; creating one"); group = this.identityService.newGroup(this.keycloakRoleToActivitiGroupId(role.getKey())); group.setName(role.getValue()); this.identityService.saveGroup(group); } else { this.logger.info("Group does not exist; group creation is disabled: {}", role.getKey()); } } if (group != null && this.syncGroupAdd) { this.logger.trace("Group membership not in Activiti; adding to group: {} => {}", user.getEmail(), group.getName()); this.identityService.createMembership(user.getId(), group.getId()); } else { this.logger.debug("User/group membership sync disabled; not adding user to group: {} => {}", user.getId(), group.getId()); } } } private String keycloakRoleToActivitiGroupId(String role) { return this.groupPrefix + role; } private String activitiGroupIdToKeycloakRole(String groupId) { return groupId.startsWith(this.groupPrefix) ? groupId.substring(this.groupPrefix.length()) : groupId; } } Loading
pom.xml +11 −5 Original line number Diff line number Diff line Loading @@ -12,9 +12,9 @@ <maven.compiler.target>11</maven.compiler.target> <maven.compiler.release>11</maven.compiler.release> <aps.version>1.11.1.1</aps.version> <keycloak.version>6.0.1</keycloak.version> <spring-security-oauth2.version>2.0.17.RELEASE</spring-security-oauth2.version> <aps.version>2.3.2</aps.version> <keycloak.version>10.0.2</keycloak.version> <spring-security-oauth2.version>2.5.2.RELEASE</spring-security-oauth2.version> <slf4j.version>1.7.26</slf4j.version> </properties> Loading @@ -37,6 +37,7 @@ <version>${keycloak.version}</version> <scope>provided</scope> </dependency> <!-- Needed for Activiti App Identity Service inheritance/override --> <dependency> <groupId>com.activiti</groupId> <artifactId>activiti-app</artifactId> Loading @@ -44,6 +45,7 @@ <classifier>classes</classifier> <scope>provided</scope> </dependency> <!-- Needed for the Activiti App Public API --> <dependency> <groupId>com.activiti</groupId> <artifactId>activiti-app-logic</artifactId> Loading @@ -53,6 +55,10 @@ </dependencies> <repositories> <repository> <id>alfresco-private</id> <url>https://artifacts.alfresco.com/nexus/content/groups/private</url> </repository> <repository> <id>activiti-releases</id> <url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-enterprise-releases</url> Loading
src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiEngineAuthenticator.javadeleted 100644 → 0 +0 −162 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.activiti.engine.IdentityService; import org.activiti.engine.identity.Group; import org.activiti.engine.identity.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.stereotype.Component; /** * This is an unused implementation for non-APS installation. It is not tested * and probably pointless. * * @author brian.long@yudrio.com */ @Component("keycloak-ext.activiti-engine.authenticator") @Lazy public class KeycloakActivitiEngineAuthenticator extends AbstractKeycloakActivitiAuthenticator { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private IdentityService identityService; @Value("${keycloak-ext.group.prefix:KEYCLOAK_}") private String groupPrefix; /** * This method validates that the user exists, if not, it creates the * missing user. Without this functionality, SSO straight up fails. */ @Override public void preAuthenticate(Authentication auth) throws AuthenticationException { User user = this.findUser(auth); if (user == null) { if (this.createMissingUser) { this.logger.debug("User does not yet exist; creating the user: {}", auth.getName()); user = this.createUser(auth); this.logger.debug("Created user: {} => {}", user.getId(), user.getEmail()); if (this.clearNewUserDefaultGroups) { this.logger.debug("Clearing groups: {}", user.getId()); List<Group> groups = this.identityService.createGroupQuery() .groupMember(user.getId()) .list(); for (Group group : groups) this.identityService.deleteMembership(user.getId(), group.getId()); } } else { this.logger.info("User does not exist; user creation is disabled: {}", auth.getName()); } } } /** * This method validates that the groups exist, if not, it creates the * missing ones. Without this functionality, SSO works, but the user's * authorities are not synchronized. */ @Override public void postAuthenticate(Authentication auth) throws AuthenticationException { User user = this.findUser(auth); this.logger.debug("Inspecting user: {} => {}", user.getId(), user.getEmail()); this.syncUserRoles(user, auth); } private User findUser(Authentication auth) { String email = auth.getName(); User user = this.identityService.createUserQuery() .userEmail(email) .singleResult(); return user; } private User createUser(Authentication auth) { User user = this.identityService.newUser(auth.getName()); user.setEmail(auth.getName()); this.identityService.saveUser(user); return user; } private void syncUserRoles(User user, Authentication auth) { Map<String, String> roles = this.getKeycloakRoles(auth); if (roles == null) { this.logger.debug("The user roles could not be determined; skipping sync: {}", user.getEmail()); return; } // check Activiti groups List<Group> groups = this.identityService.createGroupQuery() .groupMember(user.getEmail()) .list(); this.logger.debug("User is currently a member of {} groups", groups.size()); for (Group group : groups) { if (!group.getId().startsWith(this.groupPrefix) && this.syncInternalGroups) continue; this.logger.trace("Inspecting group: {} => {} ({})", group.getId(), group.getName(), group.getType()); if (roles.remove(this.activitiGroupIdToKeycloakRole(group.getId())) != null) { this.logger.trace("Group and membership already exist: {} => {}", user.getEmail(), group.getName()); // already a member of the group } else { if (this.syncGroupRemove) { this.logger.trace("Group membership not in OIDC token; removing from group: {} => {}", user.getEmail(), group.getName()); this.identityService.deleteMembership(user.getId(), group.getId()); } else { this.logger.debug("User/group membership sync disabled; not removing user from group: {} => {}", user.getId(), group.getId()); } } } this.logger.debug("Unaddressed OIDC roles: {}", roles); // check remainder/unaddressed roles for (Entry<String, String> role : roles.entrySet()) { this.logger.trace("Inspecting role: {}", role); Group group = this.identityService.createGroupQuery() .groupId(this.keycloakRoleToActivitiGroupId(role.getKey())) .singleResult(); if (group == null) { if (this.createMissingGroup) { this.logger.trace("Group does not exist; creating one"); group = this.identityService.newGroup(this.keycloakRoleToActivitiGroupId(role.getKey())); group.setName(role.getValue()); this.identityService.saveGroup(group); } else { this.logger.info("Group does not exist; group creation is disabled: {}", role.getKey()); } } if (group != null && this.syncGroupAdd) { this.logger.trace("Group membership not in Activiti; adding to group: {} => {}", user.getEmail(), group.getName()); this.identityService.createMembership(user.getId(), group.getId()); } else { this.logger.debug("User/group membership sync disabled; not adding user to group: {} => {}", user.getId(), group.getId()); } } } private String keycloakRoleToActivitiGroupId(String role) { return this.groupPrefix + role; } private String activitiGroupIdToKeycloakRole(String groupId) { return groupId.startsWith(this.groupPrefix) ? groupId.substring(this.groupPrefix.length()) : groupId; } }