Avoid recreating ScimClients at every change propagation : code refactoring
This commit is contained in:
parent
f00130d37a
commit
633291d401
4 changed files with 88 additions and 42 deletions
|
@ -30,6 +30,11 @@ public class GroupScimClient implements ScimClientInterface<GroupModel> {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScrimProviderConfiguration getConfiguration() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
@ -39,4 +39,9 @@ public interface ScimClientInterface<M extends RoleMapperModel> extends AutoClos
|
||||||
* @param result the synchronization result to update for indicating triggered operations (e.g. user deletions)
|
* @param result the synchronization result to update for indicating triggered operations (e.g. user deletions)
|
||||||
*/
|
*/
|
||||||
void sync(SynchronizationResult result);
|
void sync(SynchronizationResult result);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the {@link ScrimProviderConfiguration} corresponding to this client.
|
||||||
|
*/
|
||||||
|
ScrimProviderConfiguration getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,66 +5,97 @@ import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import sh.libre.scim.storage.ScimStorageProviderFactory;
|
import sh.libre.scim.storage.ScimStorageProviderFactory;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In charge of sending SCIM Request to all registered Scim endpoints.
|
* In charge of sending SCIM Request to all registered Scim endpoints.
|
||||||
*/
|
*/
|
||||||
public class ScimDispatcher {
|
public class ScimDispatcher {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
|
private static final Logger logger = Logger.getLogger(ScimDispatcher.class);
|
||||||
|
|
||||||
private final KeycloakSession session;
|
private final KeycloakSession session;
|
||||||
|
private final List<UserScimClient> userScimClients = new ArrayList<>();
|
||||||
|
private final List<GroupScimClient> groupScimClients = new ArrayList<>();
|
||||||
|
|
||||||
public ScimDispatcher(KeycloakSession session) {
|
public ScimDispatcher(KeycloakSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
}
|
refreshActiveScimEndpoints();
|
||||||
|
|
||||||
public void dispatchUserModificationToAll(Consumer<UserScimClient> operationToDispatch) {
|
|
||||||
getAllSCIMServer(Scope.USER).forEach(scimServerConfiguration -> dispatchUserModificationToOne(scimServerConfiguration, operationToDispatch));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatchUserModificationToOne(ComponentModel scimServerConfiguration, Consumer<UserScimClient> operationToDispatch) {
|
|
||||||
LOGGER.infof("%s %s %s %s", scimServerConfiguration.getId(), scimServerConfiguration.getName(), scimServerConfiguration.getProviderId(), scimServerConfiguration.getProviderType());
|
|
||||||
try (UserScimClient client = UserScimClient.newUserScimClient(scimServerConfiguration, session)) {
|
|
||||||
operationToDispatch.accept(client);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatchGroupModificationToAll(Consumer<GroupScimClient> operationToDispatch) {
|
|
||||||
getAllSCIMServer(Scope.GROUP).forEach(scimServerConfiguration -> dispatchGroupModificationToOne(scimServerConfiguration, operationToDispatch));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispatchGroupModificationToOne(ComponentModel scimServerConfiguration, Consumer<GroupScimClient> operationToDispatch) {
|
|
||||||
LOGGER.infof("%s %s %s %s", scimServerConfiguration.getId(), scimServerConfiguration.getName(), scimServerConfiguration.getProviderId(), scimServerConfiguration.getProviderType());
|
|
||||||
try (GroupScimClient client = GroupScimClient.newGroupScimClient(scimServerConfiguration, session)) {
|
|
||||||
operationToDispatch.accept(client);
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param scope The {@link Scope} to consider (User or Group)
|
* Lists all active ScimStorageProviderFactory and create new ScimClients for each of them
|
||||||
* @return all enabled registered Scim endpoints with propagation enabled for the given scope
|
|
||||||
*/
|
*/
|
||||||
private Stream<ComponentModel> getAllSCIMServer(Scope scope) {
|
public void refreshActiveScimEndpoints() {
|
||||||
// TODO : we could initiative this list once and invalidate it when configuration changes
|
try {
|
||||||
|
// Step 1: close existing clients
|
||||||
|
for (GroupScimClient c : groupScimClients) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
for (UserScimClient c : userScimClients) {
|
||||||
|
c.close();
|
||||||
|
}
|
||||||
|
|
||||||
String propagationConfKey = switch (scope) {
|
// Step 2: Get All SCIM endpoints defined in Admin Console (enabled ScimStorageProviderFactory)
|
||||||
case GROUP -> ScrimProviderConfiguration.CONF_KEY_PROPAGATION_GROUP;
|
session.getContext().getRealm().getComponentsStream()
|
||||||
case USER -> ScrimProviderConfiguration.CONF_KEY_PROPAGATION_USER;
|
.filter(m -> ScimStorageProviderFactory.ID.equals(m.getProviderId())
|
||||||
};
|
&& m.get("enabled", true))
|
||||||
return session.getContext().getRealm().getComponentsStream()
|
.forEach(scimEndpoint -> {
|
||||||
.filter(m -> ScimStorageProviderFactory.ID.equals(m.getProviderId())
|
// Step 3 : create scim clients for each endpoint
|
||||||
&& m.get("enabled", true)
|
if (scimEndpoint.get(ScrimProviderConfiguration.CONF_KEY_PROPAGATION_GROUP, false)) {
|
||||||
&& m.get(propagationConfKey, false));
|
GroupScimClient groupScimClient = GroupScimClient.newGroupScimClient(scimEndpoint, session);
|
||||||
|
groupScimClients.add(groupScimClient);
|
||||||
|
}
|
||||||
|
if (scimEndpoint.get(ScrimProviderConfiguration.CONF_KEY_PROPAGATION_USER, false)) {
|
||||||
|
UserScimClient userScimClient = UserScimClient.newUserScimClient(scimEndpoint, session);
|
||||||
|
userScimClients.add(userScimClient);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("[SCIM] Error while refreshing scim clients ", e);
|
||||||
|
// TODO : how to handle exception here ?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Scope {
|
public void dispatchUserModificationToAll(Consumer<UserScimClient> operationToDispatch) {
|
||||||
USER, GROUP
|
// TODO should not be required to launch a refresh here : we should refresh clients only if an endpoint configuration changes
|
||||||
|
refreshActiveScimEndpoints();
|
||||||
|
userScimClients.forEach(operationToDispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatchGroupModificationToAll(Consumer<GroupScimClient> operationToDispatch) {
|
||||||
|
// TODO should not be required to launch a refresh here : we should refresh clients only if an endpoint configuration changes
|
||||||
|
refreshActiveScimEndpoints();
|
||||||
|
groupScimClients.forEach(operationToDispatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dispatchUserModificationToOne(ComponentModel scimServerConfiguration, Consumer<UserScimClient> operationToDispatch) {
|
||||||
|
// TODO should not be required to launch a refresh here : we should refresh clients only if an endpoint configuration changes
|
||||||
|
refreshActiveScimEndpoints();
|
||||||
|
|
||||||
|
// Scim client should already have been created
|
||||||
|
Optional<UserScimClient> matchingClient = userScimClients.stream().filter(u -> u.getConfiguration().getId().equals(scimServerConfiguration.getId())).findFirst();
|
||||||
|
if (matchingClient.isPresent()) {
|
||||||
|
operationToDispatch.accept(matchingClient.get());
|
||||||
|
} else {
|
||||||
|
logger.error("[SCIM] Could not find a Scim Client matching endpoint configuration" + scimServerConfiguration.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void dispatchGroupModificationToOne(ComponentModel scimServerConfiguration, Consumer<GroupScimClient> operationToDispatch) {
|
||||||
|
// TODO should not be required to launch a refresh here : we should refresh clients only if an endpoint configuration changes
|
||||||
|
refreshActiveScimEndpoints();
|
||||||
|
|
||||||
|
// Scim client should already have been created
|
||||||
|
Optional<GroupScimClient> matchingClient = groupScimClients.stream().filter(u -> u.getConfiguration().getId().equals(scimServerConfiguration.getId())).findFirst();
|
||||||
|
if (matchingClient.isPresent()) {
|
||||||
|
operationToDispatch.accept(matchingClient.get());
|
||||||
|
} else {
|
||||||
|
logger.error("[SCIM] Could not find a Scim Client matching endpoint configuration" + scimServerConfiguration.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,6 +275,11 @@ public class UserScimClient implements ScimClientInterface<UserModel> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScrimProviderConfiguration getConfiguration() {
|
||||||
|
return this.scimProviderConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
|
Loading…
Reference in a new issue