Exception Handler - Step 1: basic wiring
This commit is contained in:
parent
51d4837c19
commit
72e597a09c
5 changed files with 53 additions and 19 deletions
|
@ -132,7 +132,7 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
||||||
|
|
||||||
protected abstract Stream<RMM> getResourceStream();
|
protected abstract Stream<RMM> getResourceStream();
|
||||||
|
|
||||||
public void importResources(SynchronizationResult syncRes) {
|
public void importResources(SynchronizationResult syncRes) throws ScimPropagationException {
|
||||||
LOGGER.info("Import");
|
LOGGER.info("Import");
|
||||||
try {
|
try {
|
||||||
for (S resource : scimClient.listResources()) {
|
for (S resource : scimClient.listResources()) {
|
||||||
|
@ -180,13 +180,17 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
// TODO should we stop and throw ScimPropagationException here ?
|
||||||
LOGGER.error(e);
|
LOGGER.error(e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
syncRes.increaseFailed();
|
syncRes.increaseFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ResponseException e) {
|
} catch (ResponseException e) {
|
||||||
throw new RuntimeException(e);
|
// TODO should we stop and throw ScimPropagationException here ?
|
||||||
|
LOGGER.error(e);
|
||||||
|
e.printStackTrace();
|
||||||
|
syncRes.increaseFailed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,7 +220,7 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
|
||||||
try {
|
try {
|
||||||
return new URI("%s/%s".formatted(type.getEndpoint(), externalId.asString()));
|
return new URI("%s/%s".formatted(type.getEndpoint(), externalId.asString()));
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
throw new IllegalStateException("should never occur: can not format URI for type %s and id %s".formatted(type, externalId) , e);
|
throw new IllegalStateException("should never occur: can not format URI for type %s and id %s".formatted(type, externalId), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ScimClient<S extends ResourceNode> implements AutoCloseable {
|
public class ScimClient<S extends ResourceNode> implements AutoCloseable {
|
||||||
private static final Logger LOGGER = Logger.getLogger(ScimClient.class);
|
private static final Logger LOGGER = Logger.getLogger(ScimClient.class);
|
||||||
|
@ -62,10 +63,11 @@ public class ScimClient<S extends ResourceNode> implements AutoCloseable {
|
||||||
return new ScimClient(scimRequestBuilder, scimResourceType);
|
return new ScimClient(scimRequestBuilder, scimResourceType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public EntityOnRemoteScimId create(KeycloakId id, S scimForCreation) {
|
public EntityOnRemoteScimId create(KeycloakId id, S scimForCreation) throws ScimPropagationException {
|
||||||
if (scimForCreation.getId().isPresent()) {
|
Optional<String> scimForCreationId = scimForCreation.getId();
|
||||||
throw new IllegalArgumentException(
|
if (scimForCreationId.isPresent()) {
|
||||||
"%s is already created on remote with id %s".formatted(id, scimForCreation.getId().get())
|
throw new ScimPropagationException(
|
||||||
|
"%s is already created on remote with id %s".formatted(id, scimForCreationId.get())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Retry retry = retryRegistry.retry("create-%s".formatted(id.asString()));
|
Retry retry = retryRegistry.retry("create-%s".formatted(id.asString()));
|
||||||
|
|
|
@ -19,6 +19,7 @@ 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 ScimExceptionHandler exceptionHandler;
|
||||||
private boolean clientsInitialized = false;
|
private boolean clientsInitialized = false;
|
||||||
private final List<UserScimService> userScimServices = new ArrayList<>();
|
private final List<UserScimService> userScimServices = new ArrayList<>();
|
||||||
private final List<GroupScimService> groupScimServices = new ArrayList<>();
|
private final List<GroupScimService> groupScimServices = new ArrayList<>();
|
||||||
|
@ -26,6 +27,7 @@ public class ScimDispatcher {
|
||||||
|
|
||||||
public ScimDispatcher(KeycloakSession session) {
|
public ScimDispatcher(KeycloakSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
this.exceptionHandler = new ScimExceptionHandler(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -78,7 +80,7 @@ public class ScimDispatcher {
|
||||||
operationToDispatch.acceptThrows(userScimService);
|
operationToDispatch.acceptThrows(userScimService);
|
||||||
servicesCorrectlyPropagated.add(userScimService);
|
servicesCorrectlyPropagated.add(userScimService);
|
||||||
} catch (ScimPropagationException e) {
|
} catch (ScimPropagationException e) {
|
||||||
logAndRollback(userScimService.getConfiguration(), e);
|
exceptionHandler.handleException(userScimService.getConfiguration(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// TODO we could iterate on servicesCorrectlyPropagated to undo modification
|
// TODO we could iterate on servicesCorrectlyPropagated to undo modification
|
||||||
|
@ -93,7 +95,7 @@ public class ScimDispatcher {
|
||||||
operationToDispatch.acceptThrows(groupScimService);
|
operationToDispatch.acceptThrows(groupScimService);
|
||||||
servicesCorrectlyPropagated.add(groupScimService);
|
servicesCorrectlyPropagated.add(groupScimService);
|
||||||
} catch (ScimPropagationException e) {
|
} catch (ScimPropagationException e) {
|
||||||
logAndRollback(groupScimService.getConfiguration(), e);
|
exceptionHandler.handleException(groupScimService.getConfiguration(), e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// TODO we could iterate on servicesCorrectlyPropagated to undo modification
|
// TODO we could iterate on servicesCorrectlyPropagated to undo modification
|
||||||
|
@ -109,10 +111,10 @@ public class ScimDispatcher {
|
||||||
operationToDispatch.acceptThrows(matchingClient.get());
|
operationToDispatch.acceptThrows(matchingClient.get());
|
||||||
logger.infof("[SCIM] User operation dispatched to SCIM server %s", matchingClient.get().getConfiguration().getId());
|
logger.infof("[SCIM] User operation dispatched to SCIM server %s", matchingClient.get().getConfiguration().getId());
|
||||||
} catch (ScimPropagationException e) {
|
} catch (ScimPropagationException e) {
|
||||||
logAndRollback(matchingClient.get().getConfiguration(), e);
|
exceptionHandler.handleException(matchingClient.get().getConfiguration(), e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error("[SCIM] Could not find a Scim Client matching endpoint configuration" + scimServerConfiguration.getId());
|
logger.error("[SCIM] Could not find a Scim Client matching User endpoint configuration" + scimServerConfiguration.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,10 +128,10 @@ public class ScimDispatcher {
|
||||||
operationToDispatch.acceptThrows(matchingClient.get());
|
operationToDispatch.acceptThrows(matchingClient.get());
|
||||||
logger.infof("[SCIM] Group operation dispatched to SCIM server %s", matchingClient.get().getConfiguration().getId());
|
logger.infof("[SCIM] Group operation dispatched to SCIM server %s", matchingClient.get().getConfiguration().getId());
|
||||||
} catch (ScimPropagationException e) {
|
} catch (ScimPropagationException e) {
|
||||||
logAndRollback(matchingClient.get().getConfiguration(), e);
|
exceptionHandler.handleException(matchingClient.get().getConfiguration(), e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.error("[SCIM] Could not find a Scim Client matching endpoint configuration" + scimServerConfiguration.getId());
|
logger.error("[SCIM] Could not find a Scim Client matching Group endpoint configuration" + scimServerConfiguration.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,11 +146,6 @@ public class ScimDispatcher {
|
||||||
userScimServices.clear();
|
userScimServices.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logAndRollback(ScrimProviderConfiguration scimServerConfiguration, ScimPropagationException e) {
|
|
||||||
logger.error("[SCIM] Error while propagating to SCIM endpoint " + scimServerConfiguration.getId(), e);
|
|
||||||
// TODO session.getTransactionManager().rollback();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeClientsIfNeeded() {
|
private void initializeClientsIfNeeded() {
|
||||||
if (!clientsInitialized) {
|
if (!clientsInitialized) {
|
||||||
clientsInitialized = true;
|
clientsInitialized = true;
|
||||||
|
|
31
src/main/java/sh/libre/scim/core/ScimExceptionHandler.java
Normal file
31
src/main/java/sh/libre/scim/core/ScimExceptionHandler.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package sh.libre.scim.core;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In charge of dealing with SCIM exceptions by ignoring, logging or rollback transaction according to :
|
||||||
|
* - The context in which it occurs (sync, user creation...)
|
||||||
|
* - The related SCIM endpoint and its configuration
|
||||||
|
* - The thrown exception itself
|
||||||
|
*/
|
||||||
|
public class ScimExceptionHandler {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(ScimDispatcher.class);
|
||||||
|
private final KeycloakSession session;
|
||||||
|
|
||||||
|
public ScimExceptionHandler(KeycloakSession session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the given exception by loggin and/or rollback transaction.
|
||||||
|
*
|
||||||
|
* @param scimProviderConfiguration the configuration of the endpoint for which the propagation exception occured
|
||||||
|
* @param e the occuring exception
|
||||||
|
*/
|
||||||
|
public void handleException(ScrimProviderConfiguration scimProviderConfiguration, ScimPropagationException e) {
|
||||||
|
logger.error("[SCIM] Error while propagating to SCIM endpoint " + scimProviderConfiguration.getId(), e);
|
||||||
|
// TODO session.getTransactionManager().rollback();
|
||||||
|
}
|
||||||
|
}
|
|
@ -107,7 +107,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
||||||
ScimResourceType type = switch (rawResourceType) {
|
ScimResourceType type = switch (rawResourceType) {
|
||||||
case "users" -> ScimResourceType.USER;
|
case "users" -> ScimResourceType.USER;
|
||||||
case "groups" -> ScimResourceType.GROUP;
|
case "groups" -> ScimResourceType.GROUP;
|
||||||
default -> throw new IllegalArgumentException("Unsuported resource type: " + rawResourceType);
|
default -> throw new IllegalArgumentException("Unsupported resource type: " + rawResourceType);
|
||||||
};
|
};
|
||||||
KeycloakId id = new KeycloakId(matcher.group(2));
|
KeycloakId id = new KeycloakId(matcher.group(2));
|
||||||
handleRoleMappingEvent(event, type, id);
|
handleRoleMappingEvent(event, type, id);
|
||||||
|
|
Loading…
Reference in a new issue