Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
0d402f6014 | |||
f000f1b134 | |||
e7b6bd644e | |||
25093cd822 | |||
70ff0b5c5c | |||
343e1b65b9 | |||
d10ff1103d | |||
f5eefdb544 | |||
28a6f4d101 | |||
07a5ed959a |
15
README.md
15
README.md
@@ -20,6 +20,13 @@ The installation is simple. Just include the JAR in the classpath of your Activ
|
|||||||
|
|
||||||
Notice the use of `PostResources` instead of `PreResources`. This library needs to be loaded after the web application. This is the best way to load any other extensions or customization to the Activiti App, including `JavaDelegate` implementations.
|
Notice the use of `PostResources` instead of `PreResources`. This library needs to be loaded after the web application. This is the best way to load any other extensions or customization to the Activiti App, including `JavaDelegate` implementations.
|
||||||
|
|
||||||
|
## Support Matrix
|
||||||
|
|
||||||
|
| Keycloak Activiti App Extension | Activiti App |
|
||||||
|
| ------------------------------- | --------------- |
|
||||||
|
| v1.0 - v1.2 | v1.11.x |
|
||||||
|
| v1.3+ | v1.11.x - v2.3+ |
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The library is highly configurable. You configure it with properties specified in the `activiti-app.properties` file, which exists somewhere in the root of the classpath. That is typically in the `lib` folder. The properties to configure are enumerated in the table below.
|
The library is highly configurable. You configure it with properties specified in the `activiti-app.properties` file, which exists somewhere in the root of the classpath. That is typically in the `lib` folder. The properties to configure are enumerated in the table below.
|
||||||
@@ -41,10 +48,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.capability.regex.patterns` | | When creating a new group, sync as an APS Organization, except when the specified pattern matches the role. In those cases, sync as an APS Capability. |
|
||||||
| `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
|
||||||
|
|
||||||
|
35
pom.xml
35
pom.xml
@@ -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.1</version>
|
<version>1.3.0</version>
|
||||||
<name>Keycloak Authentication & Authorization for APS</name>
|
<name>Keycloak Authentication & Authorization for APS</name>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@@ -12,9 +12,9 @@
|
|||||||
<maven.compiler.target>11</maven.compiler.target>
|
<maven.compiler.target>11</maven.compiler.target>
|
||||||
<maven.compiler.release>11</maven.compiler.release>
|
<maven.compiler.release>11</maven.compiler.release>
|
||||||
|
|
||||||
<aps.version>1.11.1.1</aps.version>
|
<aps.version>2.0.1</aps.version>
|
||||||
<keycloak.version>6.0.1</keycloak.version>
|
<keycloak.version>10.0.2</keycloak.version>
|
||||||
<spring-security-oauth2.version>2.0.17.RELEASE</spring-security-oauth2.version>
|
<spring-security-oauth2.version>2.5.2.RELEASE</spring-security-oauth2.version>
|
||||||
<slf4j.version>1.7.26</slf4j.version>
|
<slf4j.version>1.7.26</slf4j.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
<version>${keycloak.version}</version>
|
<version>${keycloak.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Needed for Activiti App Identity Service inheritance/override -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.activiti</groupId>
|
<groupId>com.activiti</groupId>
|
||||||
<artifactId>activiti-app</artifactId>
|
<artifactId>activiti-app</artifactId>
|
||||||
@@ -44,6 +45,7 @@
|
|||||||
<classifier>classes</classifier>
|
<classifier>classes</classifier>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- Needed for the Activiti App Public API -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.activiti</groupId>
|
<groupId>com.activiti</groupId>
|
||||||
<artifactId>activiti-app-logic</artifactId>
|
<artifactId>activiti-app-logic</artifactId>
|
||||||
@@ -52,36 +54,15 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>io.repaint.maven</groupId>
|
|
||||||
<artifactId>tiles-maven-plugin</artifactId>
|
|
||||||
<version>2.21</version>
|
|
||||||
<extensions>true</extensions>
|
|
||||||
<configuration>
|
|
||||||
<filtering>true</filtering>
|
|
||||||
<tiles>
|
|
||||||
<tile>com.inteligr8:maven-public-deploy-tile:[1.0.0,2.0.0)</tile>
|
|
||||||
</tiles>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>alfresco-public</id>
|
<id>alfresco-private</id>
|
||||||
<url>https://artifacts.alfresco.com/nexus/content/repositories/public</url>
|
<url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
<repository>
|
||||||
<id>activiti-releases</id>
|
<id>activiti-releases</id>
|
||||||
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-enterprise-releases</url>
|
<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-enterprise-releases</url>
|
||||||
</repository>
|
</repository>
|
||||||
<repository>
|
|
||||||
<id>inteligr8-releases</id>
|
|
||||||
<url>https://repos.inteligr8.com/nexus/repository/inteligr8-private</url>
|
|
||||||
</repository>
|
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
|
@@ -0,0 +1,45 @@
|
|||||||
|
package com.inteligr8.activiti;
|
||||||
|
|
||||||
|
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.User;
|
||||||
|
import com.activiti.service.api.UserService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author brian@inteligr8.com
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ActivitiAppAdminPasswordFixer implements DataFixer {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(this.getClass());
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private TenantFinderService tenantFinderService;
|
||||||
|
|
||||||
|
@Value("${keycloak-ext.reset.admin.username:admin@app.activiti.com}")
|
||||||
|
private String adminUsername;
|
||||||
|
|
||||||
|
@Value("${keycloak-ext.reset.admin.password:#{null}}")
|
||||||
|
private String adminPassword;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void fix() {
|
||||||
|
this.logger.trace("fix()");
|
||||||
|
|
||||||
|
if (this.adminPassword != null) {
|
||||||
|
this.logger.info("Resetting the password for admin user '{}'", this.adminUsername);
|
||||||
|
|
||||||
|
Long tenantId = this.tenantFinderService.findTenantId();
|
||||||
|
User adminUser = this.userService.findUserByEmailAndTenantId(this.adminUsername, tenantId);
|
||||||
|
this.userService.changePassword(adminUser.getId(), this.adminPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -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, ',');
|
||||||
|
@@ -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.capability.regex.patterns:#{null}}")
|
||||||
protected String syncGroupAs;
|
protected String regexCapIncludes;
|
||||||
|
|
||||||
|
protected final Set<Pattern> capIncludes = 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.regexCapIncludes != null) {
|
||||||
}
|
String[] regexPatternStrs = StringUtils.split(this.regexCapIncludes, ',');
|
||||||
|
for (int i = 0; i < regexPatternStrs.length; i++)
|
||||||
|
this.capIncludes.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.capIncludes.isEmpty())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (Pattern regex : this.capIncludes) {
|
||||||
|
Matcher matcher = regex.matcher(role);
|
||||||
|
if (matcher.matches())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,162 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
Reference in New Issue
Block a user