diff --git a/pom.xml b/pom.xml
index 462c62c..c098496 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.inteligr8.activiti
keycloak-activiti-app-ext
- 1.1.4
+ 1.2.0
Keycloak Authentication & Authorization for APS
diff --git a/src/main/java/com/inteligr8/activiti/ActivitiAppAdminGroupFixer.java b/src/main/java/com/inteligr8/activiti/ActivitiAppAdminGroupFixer.java
new file mode 100644
index 0000000..e54ff5c
--- /dev/null
+++ b/src/main/java/com/inteligr8/activiti/ActivitiAppAdminGroupFixer.java
@@ -0,0 +1,128 @@
+package com.inteligr8.activiti;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+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.stereotype.Component;
+
+import com.activiti.domain.idm.Group;
+import com.activiti.domain.idm.GroupCapability;
+import com.activiti.domain.idm.Tenant;
+import com.activiti.service.api.GroupService;
+
+/**
+ * This class/bean overrides the APS security configuration with a collection
+ * of implementations. The OOTB extension only provides one override. This
+ * uses that extension point, but delegates it out to multiple possible
+ * implementations.
+ *
+ * Order cannot be controlled, so it should not be assumed in any adapter
+ * implementation.
+ *
+ * @author brian@inteligr8.com
+ */
+@Component
+public class ActivitiAppAdminGroupFixer implements DataFixer {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ private final List adminCapabilities = Arrays.asList(
+ "access-all-models-in-tenant",
+ "access-editor",
+ "access-reports",
+ "publish-app-to-dashboard",
+ "tenant-admin",
+ "tenant-admin-api",
+ "upload-license");
+
+ @Autowired(required = false)
+ private GroupService groupService;
+
+ @Autowired
+ private TenantFinderService tenantFinderService;
+
+ @Value("${keycloak-ext.group.admins.name:admins}")
+ private String adminGroupName;
+
+ @Value("${keycloak-ext.group.admins.externalId:#{null}}")
+ private String adminGroupExternalId;
+
+ @Value("${keycloak-ext.group.admins.validate:false}")
+ private boolean validateAdministratorsGroup;
+
+ @Override
+ public void fix() {
+ this.logger.trace("fix()");
+
+ if (this.logger.isTraceEnabled())
+ this.logGroups();
+ if (this.validateAdministratorsGroup)
+ this.validateAdmins();
+ }
+
+ private void logGroups() {
+ if (this.groupService == null)
+ return;
+
+ Collection tenants = this.tenantFinderService.getTenants();
+ for (Tenant tenant : tenants) {
+ this.logger.trace("Tenant: {} => {}", tenant.getId(), tenant.getName());
+ this.logger.trace("Functional groups: {}", this.toGroupNames(this.groupService.getFunctionalGroups(tenant.getId())));
+ this.logger.trace("System groups: {}", this.toGroupNames(this.groupService.getSystemGroups(tenant.getId())));
+ }
+
+ this.logger.trace("Tenant: null");
+ this.logger.trace("Functional groups: {}", this.toGroupNames(this.groupService.getFunctionalGroups(null)));
+ this.logger.trace("System groups: {}", this.toGroupNames(this.groupService.getSystemGroups(null)));
+ }
+
+ private void validateAdmins() {
+ if (this.groupService == null)
+ return;
+
+ Long tenantId = this.tenantFinderService.findTenantId();
+ Group group = this.groupService.getGroupByExternalIdAndTenantId(this.adminGroupExternalId, tenantId);
+ if (group == null) {
+ List groups = this.groupService.getGroupByNameAndTenantId(this.adminGroupName, tenantId);
+ if (!groups.isEmpty())
+ group = groups.iterator().next();
+ }
+
+ if (group == null) {
+ this.logger.info("Creating group: {} ({})", this.adminGroupName, this.adminGroupExternalId);
+ if (this.adminGroupExternalId != null) {
+ group = this.groupService.createGroupFromExternalStore(
+ this.adminGroupExternalId, tenantId, Group.TYPE_SYSTEM_GROUP, null, this.adminGroupName, new Date());
+ } else {
+ group = this.groupService.createGroup(this.adminGroupName, tenantId, Group.TYPE_SYSTEM_GROUP, null);
+ }
+ }
+
+ this.logger.debug("Checking group capabilities: {}", group.getName());
+ Group groupWithCaps = this.groupService.getGroup(group.getId(), false, true, false, false);
+ Set adminCaps = new HashSet<>(this.adminCapabilities);
+ for (GroupCapability cap : groupWithCaps.getCapabilities())
+ adminCaps.remove(cap.getName());
+ if (!adminCaps.isEmpty()) {
+ this.logger.info("Granting group '{}' capabilities: {}", group.getName(), adminCaps);
+ this.groupService.addCapabilitiesToGroup(group.getId(), new ArrayList<>(adminCaps));
+ }
+ }
+
+ private Collection toGroupNames(Collection groups) {
+ List groupNames = new ArrayList<>(groups.size());
+ for (Group group : groups)
+ groupNames.add(group.getName() + " [" + group.getExternalId() + "]");
+ return groupNames;
+ }
+
+}
diff --git a/src/main/java/com/inteligr8/activiti/ActivitiAppAdminMembersFixer.java b/src/main/java/com/inteligr8/activiti/ActivitiAppAdminMembersFixer.java
new file mode 100644
index 0000000..068a7cc
--- /dev/null
+++ b/src/main/java/com/inteligr8/activiti/ActivitiAppAdminMembersFixer.java
@@ -0,0 +1,86 @@
+package com.inteligr8.activiti;
+
+import java.util.Arrays;
+import java.util.List;
+
+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.stereotype.Component;
+
+import com.activiti.domain.idm.Group;
+import com.activiti.domain.idm.User;
+import com.activiti.service.api.GroupService;
+import com.activiti.service.api.UserService;
+
+/**
+ * This class/bean overrides the APS security configuration with a collection
+ * of implementations. The OOTB extension only provides one override. This
+ * uses that extension point, but delegates it out to multiple possible
+ * implementations.
+ *
+ * Order cannot be controlled, so it should not be assumed in any adapter
+ * implementation.
+ *
+ * @author brian@inteligr8.com
+ */
+@Component
+public class ActivitiAppAdminMembersFixer implements DataFixer {
+
+ private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+ @Autowired(required = false)
+ private UserService userService;
+
+ @Autowired(required = false)
+ private GroupService groupService;
+
+ @Autowired
+ private TenantFinderService tenantFinderService;
+
+ @Value("${keycloak-ext.default.admins.users:#{null}}")
+ private String adminUserStrs;
+
+ @Value("${keycloak-ext.group.admins.name:admins}")
+ private String adminGroupName;
+
+ @Value("${keycloak-ext.group.admins.externalId:#{null}}")
+ private String adminGroupExternalId;
+
+ @Override
+ public void fix() {
+ this.logger.trace("fix()");
+
+ if (this.adminUserStrs != null && this.adminUserStrs.length() > 0)
+ this.associateAdmins();
+ }
+
+ private void associateAdmins() {
+ if (this.userService == null || this.groupService == null)
+ return;
+
+ List adminUsers = Arrays.asList(this.adminUserStrs.split(","));
+ if (adminUsers.isEmpty())
+ return;
+
+ Long tenantId = this.tenantFinderService.findTenantId();
+ List groups;
+ Group group1 = this.groupService.getGroupByExternalIdAndTenantId(this.adminGroupExternalId, tenantId);
+ if (group1 != null) {
+ groups = Arrays.asList(group1);
+ } else {
+ groups = this.groupService.getGroupByNameAndTenantId(this.adminGroupName, tenantId);
+ }
+ this.logger.debug("Found {} admin group(s)", groups.size());
+
+ for (String email : adminUsers) {
+ User user = this.userService.findUserByEmail(email);
+
+ this.logger.debug("Adding {} to admin group(s)", user.getEmail());
+ for (Group group : groups)
+ this.groupService.addUserToGroup(group, user);
+ }
+ }
+
+}
diff --git a/src/main/java/com/inteligr8/activiti/DataFixer.java b/src/main/java/com/inteligr8/activiti/DataFixer.java
new file mode 100644
index 0000000..cc4888c
--- /dev/null
+++ b/src/main/java/com/inteligr8/activiti/DataFixer.java
@@ -0,0 +1,7 @@
+package com.inteligr8.activiti;
+
+public interface DataFixer {
+
+ void fix();
+
+}
diff --git a/src/main/java/com/inteligr8/activiti/Inteligr8SecurityConfigurationRegistry.java b/src/main/java/com/inteligr8/activiti/Inteligr8SecurityConfigurationRegistry.java
index 130bfde..ac80d78 100644
--- a/src/main/java/com/inteligr8/activiti/Inteligr8SecurityConfigurationRegistry.java
+++ b/src/main/java/com/inteligr8/activiti/Inteligr8SecurityConfigurationRegistry.java
@@ -1,31 +1,16 @@
package com.inteligr8.activiti;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
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.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import com.activiti.api.security.AlfrescoSecurityConfigOverride;
-import com.activiti.domain.idm.Group;
-import com.activiti.domain.idm.GroupCapability;
-import com.activiti.domain.idm.Tenant;
-import com.activiti.domain.idm.User;
-import com.activiti.service.api.GroupService;
-import com.activiti.service.api.UserService;
-import com.activiti.service.idm.TenantService;
-import com.activiti.service.license.LicenseService;
/**
* This class/bean overrides the APS security configuration with a collection
@@ -43,44 +28,11 @@ public class Inteligr8SecurityConfigurationRegistry implements AlfrescoSecurityC
private final Logger logger = LoggerFactory.getLogger(this.getClass());
- private final List adminCapabilities = Arrays.asList(
- "access-all-models-in-tenant",
- "access-editor",
- "access-reports",
- "publish-app-to-dashboard",
- "tenant-admin",
- "tenant-admin-api",
- "upload-license");
-
@Autowired
private List adapters;
@Autowired(required = false)
- private LicenseService licenseService;
-
- @Autowired(required = false)
- private TenantService tenantService;
-
- @Autowired(required = false)
- private UserService userService;
-
- @Autowired(required = false)
- private GroupService groupService;
-
- @Value("${keycloak-ext.tenant:#{null}}")
- private String tenant;
-
- @Value("${keycloak-ext.default.admins.users:#{null}}")
- private String adminUserStrs;
-
- @Value("${keycloak-ext.group.admins.name:admins}")
- private String adminGroupName;
-
- @Value("${keycloak-ext.group.admins.externalId:#{null}}")
- private String adminGroupExternalId;
-
- @Value("${keycloak-ext.group.admins.validate:false}")
- private boolean validateAdministratorsGroup;
+ private List fixers;
@Override
public void configureGlobal(AuthenticationManagerBuilder authmanBuilder, UserDetailsService userDetailsService) {
@@ -88,12 +40,10 @@ public class Inteligr8SecurityConfigurationRegistry implements AlfrescoSecurityC
Collections.sort(this.adapters);
- if (this.logger.isTraceEnabled())
- this.logGroups();
- if (this.validateAdministratorsGroup)
- this.validateAdmins();
- if (this.adminUserStrs != null && this.adminUserStrs.length() > 0)
- this.associateAdmins();
+ if (this.fixers != null) {
+ for (DataFixer fixer : this.fixers)
+ fixer.fix();
+ }
for (ActivitiSecurityConfigAdapter adapter : this.adapters) {
if (adapter.isEnabled()) {
@@ -105,102 +55,5 @@ public class Inteligr8SecurityConfigurationRegistry implements AlfrescoSecurityC
}
}
}
-
- private void logGroups() {
- if (this.groupService == null)
- return;
-
- List