diff --git a/README.md b/README.md
index a358832..151f1c4 100644
--- a/README.md
+++ b/README.md
@@ -41,10 +41,10 @@ The library is highly configurable. You configure it with properties specified
### For Activiti App Only
-| 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.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. |
+| Property | Default | Description |
+| ------------------------------------------------ | ------- | ----------- |
+| `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. |
### Rare
diff --git a/pom.xml b/pom.xml
index 8f57c21..5a0a69c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.inteligr8.activiti
keycloak-activiti-app-ext
- 1.2-SNAPSHOT
+ 1.3-SNAPSHOT
Keycloak Authentication & Authorization for APS
diff --git a/src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java b/src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java
index 9610660..935cd2b 100644
--- a/src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java
+++ b/src/main/java/com/inteligr8/activiti/keycloak/AbstractKeycloakActivitiAuthenticator.java
@@ -12,6 +12,8 @@ import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.OverridingMethodsMustInvokeSuper;
+
import org.apache.commons.lang3.StringUtils;
import org.keycloak.KeycloakPrincipal;
import org.keycloak.KeycloakSecurityContext;
@@ -71,6 +73,7 @@ public abstract class AbstractKeycloakActivitiAuthenticator implements Authentic
protected final Set groupExcludes = new HashSet<>();
@Override
+ @OverridingMethodsMustInvokeSuper
public void afterPropertiesSet() {
if (this.regexPatterns != null) {
String[] regexPatternStrs = StringUtils.split(this.regexPatterns, ',');
diff --git a/src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java b/src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java
index 94705dc..ea8220a 100644
--- a/src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java
+++ b/src/main/java/com/inteligr8/activiti/keycloak/KeycloakActivitiAppAuthenticator.java
@@ -1,12 +1,15 @@
package com.inteligr8.activiti.keycloak;
import java.util.Date;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.persistence.NonUniqueResultException;
import org.apache.commons.lang3.StringUtils;
@@ -59,16 +62,22 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
@Value("${keycloak-ext.external.id:ais}")
protected String externalIdmSource;
- @Value("${keycloak-ext.syncGroupAs:organization}")
- protected String syncGroupAs;
+ @Value("${keycloak-ext.group.organization.regex.patterns:.*}")
+ protected String regexOrgIncludes;
+
+ protected final Set orgIncludes = new HashSet<>();
- protected boolean syncGroupAsOrganization() {
- return !this.syncGroupAsCapability();
- }
-
- protected boolean syncGroupAsCapability() {
- return this.syncGroupAs != null && this.syncGroupAs.toLowerCase().startsWith("cap");
- }
+ @Override
+ @OverridingMethodsMustInvokeSuper
+ public void afterPropertiesSet() {
+ super.afterPropertiesSet();
+
+ 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
@@ -163,8 +172,6 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
return;
}
- boolean syncAsOrg = this.syncGroupAsOrganization();
-
// check Activiti groups
User userWithGroups = this.userService.getUser(user.getId(), true);
for (Group group : userWithGroups.getGroups()) {
@@ -228,6 +235,8 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
if (group == null) {
if (this.createMissingGroup) {
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 externalId = this.keycloakRoleToApsGroupExternalId(role.getKey());
int type = syncAsOrg ? Group.TYPE_FUNCTIONAL_GROUP : Group.TYPE_SYSTEM_GROUP;
@@ -264,4 +273,17 @@ public class KeycloakActivitiAppAuthenticator extends AbstractKeycloakActivitiAu
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;
+ }
+
}