allowing for more dynamic organization/capability sync control

This commit is contained in:
2021-11-10 13:56:42 -05:00
parent 07a5ed959a
commit 28a6f4d101
4 changed files with 41 additions and 16 deletions

View File

@@ -41,10 +41,10 @@ The library is highly configurable. You configure it with properties specified
### For Activiti App Only ### For Activiti App Only
| Property | Default | Description | | Property | Default | Description |
| ----------------------------------------- | -------------- | ----------- | | ------------------------------------------------ | ------- | ----------- |
| `keycloak-ext.syncGroupAs` | `organization` | When creating a new group, should it be a functional (`organization`) group or a system (`capability`) group? | | `keycloak-ext.group.organization.regex.patterns` | `.*` | When creating a new group, sync as APS Organization (functional group) when the role matches the specified regular expression. If it doesn't, add as APS Capability (system group). |
| `keycloak-ext.external.id` | `ais` | When creating a new group or registering an internal group as external, use this ID as a prefix to the external group ID. | | `keycloak-ext.external.id` | `ais` | When creating a new group or registering an internal group as external, use this ID as a prefix to the external group ID. |
### Rare ### Rare

View File

@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8.activiti</groupId> <groupId>com.inteligr8.activiti</groupId>
<artifactId>keycloak-activiti-app-ext</artifactId> <artifactId>keycloak-activiti-app-ext</artifactId>
<version>1.2-SNAPSHOT</version> <version>1.3-SNAPSHOT</version>
<name>Keycloak Authentication &amp; Authorization for APS</name> <name>Keycloak Authentication &amp; Authorization for APS</name>
<properties> <properties>

View File

@@ -12,6 +12,8 @@ import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.keycloak.KeycloakPrincipal; import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext; import org.keycloak.KeycloakSecurityContext;
@@ -71,6 +73,7 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic
protected final Set<Pattern> groupExcludes = new HashSet<>(); protected final Set<Pattern> groupExcludes = new HashSet<>();
@Override @Override
@OverridingMethodsMustInvokeSuper
public void afterPropertiesSet() { public void afterPropertiesSet() {
if (this.regexPatterns != null) { if (this.regexPatterns != null) {
String[] regexPatternStrs = StringUtils.split(this.regexPatterns, ','); String[] regexPatternStrs = StringUtils.split(this.regexPatterns, ',');

View File

@@ -1,12 +1,15 @@
package com.inteligr8.activiti.keycloak; package com.inteligr8.activiti.keycloak;
import java.util.Date; import java.util.Date;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.persistence.NonUniqueResultException; import javax.persistence.NonUniqueResultException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -59,16 +62,22 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
@Value("${keycloak-ext.external.id:ais}") @Value("${keycloak-ext.external.id:ais}")
protected String externalIdmSource; protected String externalIdmSource;
@Value("${keycloak-ext.syncGroupAs:organization}") @Value("${keycloak-ext.group.organization.regex.patterns:.*}")
protected String syncGroupAs; protected String regexOrgIncludes;
protected final Set<Pattern> orgIncludes = new HashSet<>();
protected boolean syncGroupAsOrganization() { @Override
return !this.syncGroupAsCapability(); @OverridingMethodsMustInvokeSuper
} public void afterPropertiesSet() {
super.afterPropertiesSet();
protected boolean syncGroupAsCapability() {
return this.syncGroupAs != null && this.syncGroupAs.toLowerCase().startsWith("cap"); if (this.regexOrgIncludes != null) {
} String[] regexPatternStrs = StringUtils.split(this.regexOrgIncludes, ',');
for (int i = 0; i < regexPatternStrs.length; i++)
this.orgIncludes.add(Pattern.compile(regexPatternStrs[i]));
}
}
/** /**
* This method validates that the user exists, if not, it creates the * This method validates that the user exists, if not, it creates the
@@ -163,8 +172,6 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
return; return;
} }
boolean syncAsOrg = this.syncGroupAsOrganization();
// check Activiti groups // check Activiti groups
User userWithGroups = this.userService.getUser(user.getId(), true); User userWithGroups = this.userService.getUser(user.getId(), true);
for (Group group : userWithGroups.getGroups()) { for (Group group : userWithGroups.getGroups()) {
@@ -228,6 +235,8 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
if (group == null) { if (group == null) {
if (this.createMissingGroup) { if (this.createMissingGroup) {
this.logger.trace("Creating new group for role: {}", role); this.logger.trace("Creating new group for role: {}", role);
boolean syncAsOrg = this.isRoleToBeOrganization(role.getKey());
this.logger.trace("Creating new group as {}: {}", syncAsOrg ? "organization" : "capability", role);
String name = this.keycloakRoleToApsGroupName(role.getValue()); String name = this.keycloakRoleToApsGroupName(role.getValue());
String externalId = this.keycloakRoleToApsGroupExternalId(role.getKey()); String externalId = this.keycloakRoleToApsGroupExternalId(role.getKey());
int type = syncAsOrg ? Group.TYPE_FUNCTIONAL_GROUP : Group.TYPE_SYSTEM_GROUP; int type = syncAsOrg ? Group.TYPE_FUNCTIONAL_GROUP : Group.TYPE_SYSTEM_GROUP;
@@ -264,4 +273,17 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
return externalId; return externalId;
} }
private boolean isRoleToBeOrganization(String role) {
if (this.orgIncludes.isEmpty())
return false;
for (Pattern regex : this.orgIncludes) {
Matcher matcher = regex.matcher(role);
if (matcher.matches())
return true;
}
return false;
}
} }