Update to Keycloack 26 and scim-dsk 1.26
This commit is contained in:
parent
7d87d23345
commit
e8177237e0
7 changed files with 67 additions and 44 deletions
80
README.md
80
README.md
|
@ -1,35 +1,59 @@
|
|||
# keycloak-scim-client
|
||||
|
||||
This extension add [SCIM2](http://www.simplecloud.info) client capabilities to Keycloak. (See [RFC7643](https://datatracker.ietf.org/doc/html/rfc7643) and [RFC7644](https://datatracker.ietf.org/doc/html/rfc7644)).
|
||||
This extension add [SCIM2](http://www.simplecloud.info) client capabilities to Keycloak.
|
||||
|
||||
It allows to :
|
||||
|
||||
* Declare SCIM endpoints (through the identity federation UI). Any tool implementing SCIM protocol can be wired to the
|
||||
Keycloack instance through this declaration.
|
||||
* Propagate users and groups from Keycloack to SCIM endpoints : when a user/group gets created or modified in Keycloack,
|
||||
the modification is fowarded to all declared SCIM endpoints through SCIM calls within the transaction scope. If
|
||||
propagation fails, changes can be rolled back or not according to a configurable rollback strategy.
|
||||
* Import users and groups from SCIM endpoints (through the Keycloack synchronization mechanism).
|
||||
|
||||
See [RFC7643](https://datatracker.ietf.org/doc/html/rfc7643)
|
||||
and [RFC7644](https://datatracker.ietf.org/doc/html/rfc7644)) for further details
|
||||
|
||||
## Overview
|
||||
|
||||
### Motivation
|
||||
|
||||
We want to build a unified collaborative platform based on multiple applications. To do that, we need a way to propagate immediately changes made in Keycloak to all these applications. And we want to keep using OIDC or SAML as the authentication protocol.
|
||||
We want to build a unified collaborative platform based on multiple applications. To do that, we need a way to propagate
|
||||
immediately changes made in Keycloak to all these applications. And we want to keep using OIDC or SAML as the
|
||||
authentication protocol.
|
||||
|
||||
This will allow users to collaborate seamlessly across the platform without requiring every user to have connected once to each application. This will also ease GDRP compliance because deleting a user in Keycloak will delete the user from every app.
|
||||
This will allow users to collaborate seamlessly across the platform without requiring every user to have connected once
|
||||
to each application. This will also ease GDRP compliance because deleting a user in Keycloak will delete the user from
|
||||
every app. The SCIM protocol is standard, comprehensible and easy to implement. It's a perfect fit for our goal.
|
||||
|
||||
### Technical choices
|
||||
|
||||
The SCIM protocol is standard, comprehensible and easy to implement. It's a perfect fit for our goal.
|
||||
|
||||
We chose to build application extensions/plugins because it's easier to deploy and thus will benefit to a larger portion of the FOSS community.
|
||||
We chose to build application extensions/plugins because it's easier to deploy and thus will benefit to a larger portion
|
||||
of the FOSS community.
|
||||
|
||||
#### Keycloak specific
|
||||
|
||||
This extension uses 3 concepts in KC :
|
||||
- Event Listener : it's used to listens for changes and transform them in SCIM calls.
|
||||
- Federation Provider : it's used to set up all the SCIM service providers without creating our own UI.
|
||||
- JPA Entity Provider : it's used to save the mapping between the local IDs and the service providers IDs.
|
||||
This extension uses 3 concepts in KeyCloack :
|
||||
|
||||
Because the event listener is the source of the SCIM flow, and it is not cancelable, we can't have strictly consistent behavior in case of SCIM service provider failure.
|
||||
- Event Listener : used to listen for changes within Keycloack (e.g. User creation, Group deletion...) and propagate
|
||||
them to registered SCIM service providers through SCIM requests.
|
||||
- Federation Provider : used to set up all the SCIM service providers endpoint without creating our own UI.
|
||||
- JPA Entity Provider : used to save the mapping between the local IDs and the service providers IDs.
|
||||
|
||||
## Usage
|
||||
|
||||
### Installation (quick)
|
||||
### Development mode
|
||||
|
||||
1. Download the [latest version](https://lab.libreho.st/libre.sh/scim/keycloak-scim/-/jobs/artifacts/main/raw/build/libs/keycloak-scim-1.0-SNAPSHOT-all.jar?job=package)
|
||||
From the repository root :
|
||||
|
||||
* Launch the docker-compose image (composed of a postgre and keycloack instance runing on localhost:8080) :
|
||||
``docker compose up -d``
|
||||
* Execute ``gradle jar shadowJar && docker compose restart keycloak`` to build extension and update the Keycloack
|
||||
instance
|
||||
* You can access extension logs through ``docker compose logs -f``
|
||||
|
||||
### Installation
|
||||
|
||||
1. Download
|
||||
the [latest version](https://lab.libreho.st/libre.sh/scim/keycloak-scim/-/jobs/artifacts/main/raw/build/libs/keycloak-scim-1.0-SNAPSHOT-all.jar?job=package)
|
||||
2. Put it in `/opt/keycloak/providers/`.
|
||||
|
||||
It's also possible to build your own custom image if you run Keycloak in a [container](/docs/container.md).
|
||||
|
@ -38,7 +62,7 @@ Other [installation options](/docs/installation.md) are available.
|
|||
|
||||
### Setup
|
||||
|
||||
#### Add the event listerner
|
||||
#### Enable SCIM Event listeners
|
||||
|
||||
1. Go to `Admin Console > Events > Config`.
|
||||
2. Add `scim` in `Event Listeners`.
|
||||
|
@ -46,37 +70,35 @@ Other [installation options](/docs/installation.md) are available.
|
|||
|
||||
![Event listener page](/docs/img/event-listener-page.png)
|
||||
|
||||
#### Create a federation provider
|
||||
#### Register SCIM Service Providers
|
||||
|
||||
1. Go to `Admin Console > User Federation`.
|
||||
2. Click on `Add provider`.
|
||||
3. Select `scim`.
|
||||
4. Configure the provider ([see](#configuration)).
|
||||
5. Save.
|
||||
1. Go to `Admin Console > Realm Settings > Events`.
|
||||
2. Add `scim` to the list of event listers
|
||||
3. Save
|
||||
|
||||
![Federation provider page](/docs/img/federation-provider-page.png)
|
||||
|
||||
### Configuration
|
||||
|
||||
Add the endpoint - for a local set up you have to add the two containers in a docker network and use the container ip see [here](https://docs.docker.com/engine/reference/commandline/network/)
|
||||
If you use the [rocketchat app](https://lab.libreho.st/libre.sh/scim/rocketchat-scim) you get the endpoint from your rocket Chat Scim Adapter App Details.
|
||||
Add the endpoint - for a local set up you have to add the two containers in a docker network and use the container ip
|
||||
see [here](https://docs.docker.com/engine/reference/commandline/network/)
|
||||
If you use the [rocketchat app](https://lab.libreho.st/libre.sh/scim/rocketchat-scim) you get the endpoint from your
|
||||
rocket Chat Scim Adapter App Details.
|
||||
Endpoint content type is application/json.
|
||||
Auth mode Bearer or None for local test setup.
|
||||
Copy the bearer token from your app details in rocketchat.
|
||||
|
||||
If you enable import during sync then you can choose between to following import actions:
|
||||
|
||||
- Create Local - adds users to keycloak
|
||||
- Nothing
|
||||
- Delete Remote - deletes users from the remote application
|
||||
|
||||
|
||||
|
||||
|
||||
### Sync
|
||||
|
||||
You can set up a periodic sync for all users or just changed users - it's not necesarry. You can either do:
|
||||
You can set up a periodic sync for all users or just changed users - it's not mandatory. You can either do:
|
||||
|
||||
- Periodic Full Sync
|
||||
- Periodic Changed User Sync
|
||||
|
||||
|
||||
**[License AGPL](/LICENSE)**
|
||||
|
|
17
build.gradle
17
build.gradle
|
@ -1,7 +1,8 @@
|
|||
plugins {
|
||||
id 'java'
|
||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
||||
id "org.sonarqube" version "5.0.0.4638"
|
||||
id "org.sonarqube" version "5.1.0.4882"
|
||||
id "com.github.ben-manes.versions" version "0.51.0"
|
||||
}
|
||||
|
||||
group = 'sh.libre.scim'
|
||||
|
@ -16,12 +17,12 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly 'org.keycloak:keycloak-core:23.0.4'
|
||||
compileOnly 'org.keycloak:keycloak-server-spi:23.0.4'
|
||||
compileOnly 'org.keycloak:keycloak-server-spi-private:23.0.4'
|
||||
compileOnly 'org.keycloak:keycloak-services:23.0.4'
|
||||
compileOnly 'org.keycloak:keycloak-model-jpa:23.0.4'
|
||||
compileOnly 'org.keycloak:keycloak-core:26.0.1'
|
||||
compileOnly 'org.keycloak:keycloak-server-spi:26.0.1'
|
||||
compileOnly 'org.keycloak:keycloak-server-spi-private:26.0.1'
|
||||
compileOnly 'org.keycloak:keycloak-services:26.0.1'
|
||||
compileOnly 'org.keycloak:keycloak-model-jpa:26.0.1'
|
||||
implementation 'io.github.resilience4j:resilience4j-retry:2.2.0'
|
||||
implementation 'de.captaingoldfish:scim-sdk-common:1.21.1'
|
||||
implementation 'de.captaingoldfish:scim-sdk-client:1.21.1'
|
||||
implementation 'de.captaingoldfish:scim-sdk-common:1.26.0'
|
||||
implementation 'de.captaingoldfish:scim-sdk-client:1.26.0'
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ services:
|
|||
ports:
|
||||
- 5432:5432
|
||||
keycloak:
|
||||
image: quay.io/keycloak/keycloak:23.0.3
|
||||
image: quay.io/keycloak/keycloak:26.0.1
|
||||
build: .
|
||||
command: start-dev
|
||||
volumes:
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 81 KiB |
Binary file not shown.
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 104 KiB |
|
@ -37,7 +37,7 @@ public class ScimExceptionHandler {
|
|||
session.getTransactionManager().rollback();
|
||||
LOGGER.error("TRANSACTION ROLLBACK - " + errorMessage, e);
|
||||
} else {
|
||||
LOGGER.warn(errorMessage);
|
||||
LOGGER.warn(errorMessage, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,13 +119,13 @@ public abstract class AbstractScimService<K extends RoleMapperModel, S extends R
|
|||
syncRes.increaseUpdated();
|
||||
} catch (InvalidResponseFromScimEndpointException e) {
|
||||
if (skipOrStopStrategy.allowPartialSynchronizationWhenPushingToScim(this.getConfiguration())) {
|
||||
LOGGER.warn("Error while syncing " + id + " to endpoint " + getConfiguration().getEndPoint());
|
||||
LOGGER.warn("Error while syncing " + id + " to endpoint " + getConfiguration().getEndPoint(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (InconsistentScimMappingException e) {
|
||||
if (skipOrStopStrategy.allowPartialSynchronizationWhenPushingToScim(this.getConfiguration())) {
|
||||
LOGGER.warn("Inconsistent data for element " + id + " and endpoint " + getConfiguration().getEndPoint());
|
||||
LOGGER.warn("Inconsistent data for element " + id + " and endpoint " + getConfiguration().getEndPoint(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
@ -155,20 +155,20 @@ public abstract class AbstractScimService<K extends RoleMapperModel, S extends R
|
|||
}
|
||||
} catch (UnexpectedScimDataException e) {
|
||||
if (skipOrStopStrategy.skipInvalidDataFromScimEndpoint(getConfiguration())) {
|
||||
LOGGER.warn("[SCIM] Skipping element synchronisation because of invalid Scim Data for element " + resource.getId() + " : " + e.getMessage());
|
||||
LOGGER.warn("[SCIM] Skipping element synchronisation because of invalid Scim Data for element " + resource.getId() + " : " + e.getMessage(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (InconsistentScimMappingException e) {
|
||||
if (skipOrStopStrategy.allowPartialSynchronizationWhenPullingFromScim(getConfiguration())) {
|
||||
LOGGER.warn("[SCIM] Skipping element synchronisation because of inconsistent mapping for element " + resource.getId() + " : " + e.getMessage());
|
||||
LOGGER.warn("[SCIM] Skipping element synchronisation because of inconsistent mapping for element " + resource.getId() + " : " + e.getMessage(), e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
} catch (InvalidResponseFromScimEndpointException e) {
|
||||
// Can only occur in case of a DELETE_REMOTE conflict action
|
||||
if (skipOrStopStrategy.allowPartialSynchronizationWhenPullingFromScim(getConfiguration())) {
|
||||
LOGGER.warn("[SCIM] Could not delete SCIM resource " + resource.getId() + " during synchronisation");
|
||||
LOGGER.warn("[SCIM] Could not delete SCIM resource " + resource.getId() + " during synchronisation", e);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue