Loading src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java +8 −0 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; Loading Loading @@ -61,10 +62,14 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic @Value("${keycloak-ext.group.exclude.regex.patterns:#{null}}") protected String regexExcludes; @Value("${keycloak-ext.default.admins.users:#{null}}") private String adminUserStrs; protected final List<Pair<Pattern, String>> groupFormatters = new LinkedList<>(); protected final Set<Pattern> resourceIncludes = new HashSet<>(); protected final Set<Pattern> groupIncludes = new HashSet<>(); protected final Set<Pattern> groupExcludes = new HashSet<>(); protected final Set<String> adminUsers = new HashSet<>(); @Override public void afterPropertiesSet() { Loading Loading @@ -95,6 +100,9 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic for (int i = 0; i < regexPatternStrs.length; i++) this.groupExcludes.add(Pattern.compile(regexPatternStrs[i])); } if (this.adminUserStrs != null && this.adminUserStrs.length() > 0) this.adminUsers.addAll(Arrays.asList(this.adminUserStrs.split(","))); } Loading src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java +70 −2 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.NonUniqueResultException; import org.apache.commons.lang3.StringUtils; import org.keycloak.representations.AccessToken; 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; Loading Loading @@ -59,6 +64,33 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu @Autowired private GroupService groupService; @Value("${keycloak-ext.group.admins.validate:false}") private boolean validateAdministratorsGroup; @Override public void afterPropertiesSet() { super.afterPropertiesSet(); if (!this.adminUsers.isEmpty()) { Long tenantId = this.findDefaultTenantId(); List<Group> groups = this.groupService.getSystemGroupWithName("Administrators", tenantId); if (this.validateAdministratorsGroup) { this.logger.info("Validating 'Administrators' group ..."); for (Group group : groups) this.groupService.addCapabilitiesToGroup(group.getId(), Arrays.asList("access-all-models-in-tenant", "access-editor", "access-reports", "publish-app-to-dashboard", "tenant-admin", "tenant-admin-api", "upload-license")); } for (String email : this.adminUsers) { User user = this.userService.findUserByEmail(email); this.logger.debug("Adding {} to {}", user.getEmail(), "Administrators"); for (Group group : groups) this.groupService.addUserToGroup(group, user); } } } /** * This method validates that the user exists, if not, it creates the * missing user. Without this functionality, SSO straight up fails in APS. Loading Loading @@ -159,7 +191,7 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu // check Activiti groups User userWithGroups = this.userService.findUserByEmailFetchGroups(user.getEmail()); for (Group group : userWithGroups.getGroups()) { this.logger.trace("Inspecting group: {} => ", group.getId(), group.getName()); this.logger.trace("Inspecting group: {} => {}", group.getId(), group.getName()); if (group.getExternalId() == null) { // skip APS system groups Loading @@ -179,7 +211,18 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu for (Entry<String, String> role : roles.entrySet()) { this.logger.trace("Syncing group membership: {}", role); Group group = this.groupService.getGroupByExternalId(role.getKey()); Group group; try { group = this.groupService.getGroupByExternalId(role.getKey()); } catch (NonUniqueResultException nure) { if (this.logger.isDebugEnabled()) { // FIXME only added to address a former bug group = this.fixMultipleGroups(role.getKey(), tenantId); } else { throw nure; } } if (group == null) { if (this.createMissingGroup) { this.logger.trace("Creating new group: {}", role); Loading @@ -197,4 +240,29 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu } } } private Group fixMultipleGroups(String externalId, Long tenantId) { List<Group> groupsToDelete = new LinkedList<>(); Date earliestDate = new Date(); Group earliestGroup = null; for (Group group : this.groupService.getSystemGroups(tenantId)) { if (externalId.equals(group.getExternalId())) { if (group.getLastUpdate().before(earliestDate)) { if (earliestGroup != null) groupsToDelete.add(earliestGroup); earliestDate = group.getLastUpdate(); earliestGroup = group; } else { groupsToDelete.add(group); } } } for (Group group : groupsToDelete) this.groupService.deleteGroup(group.getId()); return earliestGroup; } } Loading
src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java +8 −0 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; Loading Loading @@ -61,10 +62,14 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic @Value("${keycloak-ext.group.exclude.regex.patterns:#{null}}") protected String regexExcludes; @Value("${keycloak-ext.default.admins.users:#{null}}") private String adminUserStrs; protected final List<Pair<Pattern, String>> groupFormatters = new LinkedList<>(); protected final Set<Pattern> resourceIncludes = new HashSet<>(); protected final Set<Pattern> groupIncludes = new HashSet<>(); protected final Set<Pattern> groupExcludes = new HashSet<>(); protected final Set<String> adminUsers = new HashSet<>(); @Override public void afterPropertiesSet() { Loading Loading @@ -95,6 +100,9 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic for (int i = 0; i < regexPatternStrs.length; i++) this.groupExcludes.add(Pattern.compile(regexPatternStrs[i])); } if (this.adminUserStrs != null && this.adminUserStrs.length() > 0) this.adminUsers.addAll(Arrays.asList(this.adminUserStrs.split(","))); } Loading
src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java +70 −2 Original line number Diff line number Diff line package com.inteligr8.activiti.keycloak; import java.util.Arrays; import java.util.Date; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.persistence.NonUniqueResultException; import org.apache.commons.lang3.StringUtils; import org.keycloak.representations.AccessToken; 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; Loading Loading @@ -59,6 +64,33 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu @Autowired private GroupService groupService; @Value("${keycloak-ext.group.admins.validate:false}") private boolean validateAdministratorsGroup; @Override public void afterPropertiesSet() { super.afterPropertiesSet(); if (!this.adminUsers.isEmpty()) { Long tenantId = this.findDefaultTenantId(); List<Group> groups = this.groupService.getSystemGroupWithName("Administrators", tenantId); if (this.validateAdministratorsGroup) { this.logger.info("Validating 'Administrators' group ..."); for (Group group : groups) this.groupService.addCapabilitiesToGroup(group.getId(), Arrays.asList("access-all-models-in-tenant", "access-editor", "access-reports", "publish-app-to-dashboard", "tenant-admin", "tenant-admin-api", "upload-license")); } for (String email : this.adminUsers) { User user = this.userService.findUserByEmail(email); this.logger.debug("Adding {} to {}", user.getEmail(), "Administrators"); for (Group group : groups) this.groupService.addUserToGroup(group, user); } } } /** * This method validates that the user exists, if not, it creates the * missing user. Without this functionality, SSO straight up fails in APS. Loading Loading @@ -159,7 +191,7 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu // check Activiti groups User userWithGroups = this.userService.findUserByEmailFetchGroups(user.getEmail()); for (Group group : userWithGroups.getGroups()) { this.logger.trace("Inspecting group: {} => ", group.getId(), group.getName()); this.logger.trace("Inspecting group: {} => {}", group.getId(), group.getName()); if (group.getExternalId() == null) { // skip APS system groups Loading @@ -179,7 +211,18 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu for (Entry<String, String> role : roles.entrySet()) { this.logger.trace("Syncing group membership: {}", role); Group group = this.groupService.getGroupByExternalId(role.getKey()); Group group; try { group = this.groupService.getGroupByExternalId(role.getKey()); } catch (NonUniqueResultException nure) { if (this.logger.isDebugEnabled()) { // FIXME only added to address a former bug group = this.fixMultipleGroups(role.getKey(), tenantId); } else { throw nure; } } if (group == null) { if (this.createMissingGroup) { this.logger.trace("Creating new group: {}", role); Loading @@ -197,4 +240,29 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu } } } private Group fixMultipleGroups(String externalId, Long tenantId) { List<Group> groupsToDelete = new LinkedList<>(); Date earliestDate = new Date(); Group earliestGroup = null; for (Group group : this.groupService.getSystemGroups(tenantId)) { if (externalId.equals(group.getExternalId())) { if (group.getLastUpdate().before(earliestDate)) { if (earliestGroup != null) groupsToDelete.add(earliestGroup); earliestDate = group.getLastUpdate(); earliestGroup = group; } else { groupsToDelete.add(group); } } } for (Group group : groupsToDelete) this.groupService.deleteGroup(group.getId()); return earliestGroup; } }