User Synchronization in APS from Keycloak

TL;DR : Using keycloak as an IDM or LDAP Domain Aggregator

Introduction

Alfresco Process Services (APS) 1.9 provides support for authentication through Keycloak. The integration with Keycloak gives us access to many advanced Identity Provider features, such as SAML 2.0, oAuth, OpenID, Identity Brokering and User Federation. In this article, we will review the integration of APS, and AIS (Alfresco Identity Service)  in a multi-domain LDAP environment.

With the introduction of AIS into the architecture, APS can now support authentication through multiple LDAP Domains, or other federated Identity Providers. However, in order to use APS integration with AIS, users are required to be preloaded in APS. Currently the APS AIS Integration provided in APS 1.9 does not support user synchronization, so users will need to be loaded into APS via other means, such as the LDAP User Sync feature that APS supports.

Multi-Domain Use Case

In a multi-domain environment, where connecting to multiple domains are not supported by the application, this may be handled by dedicated Identity Management Services such as Microsoft Identity Manager, Forgerock Identity Management, Okta Multi-Domain and provisioning feature; ETL services like Talend Open Studio with LDAP connectors, EAI utilities like Apache Camel, Spring Integration with Spring LDAP, or MuleSoft, etc.; or a directory passthrough/aggregation as described in this article: https://ltb-project.org/documentation/general/sasl_delegation. However, since APS provides the extension points to support authority provisioning, we will leverage that in this example, to synchronize users from AIS into APS via Keycloak’s REST API.

Implementation

Design

Figure 1 – Component Diagram of APS integration with Keycloak, and Multiple LDAP services or domains

An extension module in APS is executed via Quartz Cron settings, to poll the Keycloak REST Admin API for users, then add those users into the APS directory. Because Keycloak does not support querying via last-modify, or last-created, it is not possible to ask keycloak for a subset of users. Instead, we take all users, and eliminate the users that were already synchronized, based on created timestamp stored in Actitivi.

Figure 2 – Sequence Diagram of APS, Keycloak, and LDAP interactions

As noted in the above diagram, Keycloak does re-query ldap on each request to the USER REST Admin API, instead of just returning users from it’s internal database cache. Additionally, Keycloak does not handle duplicated users across multiple domains. These are two main improvements that are needed in Keycloak, in order to efficiently act as an IDM for multiple domains.

Usage

In the following project: https://github.com/alex4u2nv/aps-ais-authority-sync the released JAR file can be downloaded and installed into the activiti-app.war, where it can then be configured to synchronize users through keycloak. The configurations with keycloak starts with setting up APS – AIS (Keycloak) integration as described in the APS documentation: http://docs.alfresco.com/process-services1.9/concepts/is-intro.html with some additional properties that tell the APS keycloak sync module how to work.

Local Testing Via Docker

The project is bundled with Docker configurations to test against 3 OpenLDAP images that were extended from osixia/openldap, with demo users generated by LDIFGen.

FROM osixia/openldap

RUN mkdir -p /usr/local/bin
COPY  initialize.sh /usr/local/bin/initialize.sh
RUN chmod 755 /usr/local/bin/initialize.sh

ENTRYPOINT  /usr/local/bin/initialize.sh

To get started, checkout the project, and execute the following command in the directory.

docker-compose up

Which should produce an output like the following:

Example Output of docker command
Docker Ports
Docker accounts
  • Activiti admin:
    • User: admin@activiti-app.com
    • password: admin
  • Keycloak admin:
    • User: admin
    • Password: admin
  • LDAP1
    • User: admin@abc.com
    • password: admin
  • LDAP2
    • User: admin@efg.com
    • Password: admin
  • LDAP3
    • User: admin@hij.com
    • password: admin
Post Startup
  • Log into keycloak, and modify the admin user to have the admin email address of activiti.
  • Add LDAP services to user federation service of keycloak

OpenLDAP directories can be managed using Apache Directory Studio https://directory.apache.org/studio/

The docker-compose file can also be adjusted to test with the default AIS docker container, or a custom container built directly from Keycloak’s official container with the following Dockerfile

FROM jboss/keycloak
MAINTAINER https://gitter.im/Alfresco/platform-services


ADD springboot-realm.json /opt/jboss/keycloak/

ENTRYPOINT [ "/opt/jboss/docker-entrypoint.sh" ]

ENV PORT_OFFSET 300

EXPOSE 8380 30081

CMD ["-b", "0.0.0.0", "-Dkeycloak.import=/opt/jboss/keycloak/springboot-realm.json -Djboss.socket.binding.port-offset=${env.PORT_OFFSET}"]

Install Extension

The following code snippet is an example of installing the jar file into the activiti-app.war, which is installed in a Tomcat Application container, located under /usr/local/ in a Linux environment:

mkdir -p /usr/local/tomcat/webapps/activiti-app; cd $_ ; unzip ../activiti-app.war; mv ../activiti-app.war ../activiti-app.war.orig
cp  target/*with-dependencies.jar  /usr/local/tomcat/webapps/activiti-app/WEB-INF/lib/
cd /usr/local/tomcat/webapps/activiti-app; jar  -cMf ../activiti-app.war .

Configure APS

The following is an example of activiti-identity-service.properties that supports integration with Keycloak, including user authentication and synchronization from Keycloak.

keycloak.enabled=true
keycloak.realm=springboot
keycloak.auth-server-url=http://${keycloak.host}:${keycloak.port}/auth
keycloak.ssl-required=none
keycloak.resource=activiti
keycloak.principal-attribute=email
# set to true if access type is public for this client in keycloak
keycloak.public-client=true
# set secret key if access type is not public for this client in keycloak
#keycloak.credentials.secret=
keycloak.always-refresh-token=true
keycloak.autodetect-bearer-only=true
keycloak.token-store=session
keycloak.enable-basic-auth=true

### Using different namespace; as keycloak.* is mapped to an config bean
kc.sync.serviceAccount=admin
kc.sync.serviceAccountPassword=admin
kc.syncOnStartup=true
kc.sync.batch.size=1000
kc.full.sync.enabled=true
kc.diff.sync.enabled=true
kc.tenant.managers=admin@app.activiti.com
kc.tenant.admins=admin@app.activiti.com
kc.full.sync.cron=0 * * ? * *
kc.diff.sync.cron=0 * * ? * *

Due to some limitations in how keycloak REST APIs, the synchronization executes as full sync. In that, the synchronization logic will integrate over all users on every run. Therefore, it is recommended that this utility is only executed nightly, or outside of peak system usage window.

Configure Keycloak

Users can be created and managed directly in Keycloak. However, as mentioned in this article, we will look at multi-domain support.

For multi-domain support, we will follow the official Keycloak documentation for adding LDAP based user federation, which can be setup to periodically synchronize users from an LDAP directory into APS.

Test and validate.

  • Verify that users synchronized into Keycloak.
  • Log into APS verify that users and groups are synchronized into APS

3 responses to “User Synchronization in APS from Keycloak”

  1. Hi Alex, I’m not trying to be a necromancer here… but I have a question:
    Do you know a way for the user sync to update users previously synchronized by LDAP, instead of it creating users with duplicate email addresses?

    This would be useful in cases were Process Services was previously being synced directly against the LDAP.

    Like

Leave a comment