Custom group mapper for LDAP-synced users

Hi,
there’re already a few unanswered similar questions here, here or here. So I’m adding my own :slight_smile:

I’m seeking advice for the use case of mapping users synced with LDAP to non-LDAP groups synced from an external source. Currently I’m thinking about implementing a custom UserFederatedStorageProvider to sync the groups from the external source and link them to the corresponding LDAP-synced users. This approach is outlined in Augmenting external storage and Import implementation strategy.

Now I’m wondering if there’re other approaches possible here, e.g. by extending the list of built-in LDAP group mappers with my own custom group mapper.

Currently I’m thinking about implementing a custom UserFederatedStorageProvider to sync the groups from the external source and link them to the corresponding LDAP-synced users. This approach is outlined in Augmenting external storage and Import implementation strategy.

@dasniko Niko, you’re an expert on custom UserStorageProvider - would you use this approach for the described use case?

And I can second the questions raised here:

My question is now, how can I also add usergroups automatically? Shouldn´t be there also kind of GroupStorageProvider? I don´t really find the entry point how to create groups automatically with keycloak startup.

First: Please don’t mention people here directly in your questions. This has two major downsides:

  1. Mentioned ppl get annoyed
  2. Other ppl won’t help, b/c somebody else was mentioned.

Just ask your question and if there is somebody able and willing to help, he/she will do so!


Now to your question.
You can’t use a user storage provider to just “sync” groups. There must always be a user be involved. And if your user originates from LDAP, another user storage provider wouldn’t be called.
There’s also nothing else ootb to sync just groups.
Possible solution would be to create your own SPI and implement the desired logic to do whatever you want to do. :man_shrugging:

1 Like

Sorry for violating the netiquette - will do better next time.

Thanks for your advice. In my use case the user information and group memberships are stored in different data sources - users are maintained in a directory service and group memberships are maintained a propriatary system. Does “create your own SPI” mean I’ve to skip the LDAP user provider (for syncing the users) and come up with a reimplementation of the LDAP user storage provider to add the group memberships from a different data source?

Isn’t there any chance to augment the users already synced with LDAP user provider?

My naive idea was to come up with a custom UserStorageProvider which implements ImportSynchronization (for syncing the groups and group memberships) and uses AbstractUserAdapterFederatedStorage to reference the users already synced with the LDAP provider. By overriding AbstractUserAdapterFederatedStorage.getGroupsInternal() the group memberships would be added to the users.

Hhm, it seems I totally misunderstood what described in Augmenting external storage:

…Keycloak allows you to augment your external store by storing extra information in Keycloak’s database. This is called federated user storage and is encapsulated within the org.keycloak.storage.federated.UserFederatedStorageProvider class.

You can evaluate various approaches:

  • impement a custom LDAP mapper, which fetches the groups a user belongs from a third system during LDAP sync
  • implement a custom SPI, which runs regularly and fetches groups from a third system and assigns users
  • implement a separate/external service, which creates the groups in Keycloak through the Admin REST API
  • ???

Your naive approach won’t work, as I already wrote:

One user is always associated to one storage only!

I evaluated option 1 - a custom LDAP mapper similar to the ones provided in the package org.keycloak.storage.ldap.mappers.

Implementing the LDAPStorageMapperFactory SPI by extending AbstractLDAPStorageMapper with my own implementation of proxy() and syncDataFromFederationProviderToKeycloak() should do the trick :+1:

Thanks for your advice!

1 Like