Use enum interfaces for RollbackStrategy and SkipOrStopStrategy

This commit is contained in:
Alex Morel 2024-06-27 11:15:59 +02:00
parent 620c9e84bb
commit 387abc30f8
6 changed files with 51 additions and 92 deletions

View file

@ -5,8 +5,8 @@ import org.keycloak.component.ComponentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import sh.libre.scim.core.exceptions.ScimExceptionHandler; import sh.libre.scim.core.exceptions.ScimExceptionHandler;
import sh.libre.scim.core.exceptions.ScimPropagationException; import sh.libre.scim.core.exceptions.ScimPropagationException;
import sh.libre.scim.core.exceptions.SkipOrStopApproach;
import sh.libre.scim.core.exceptions.SkipOrStopStrategy; import sh.libre.scim.core.exceptions.SkipOrStopStrategy;
import sh.libre.scim.core.exceptions.SkipOrStopStrategyFactory;
import sh.libre.scim.storage.ScimEndpointConfigurationStorageProviderFactory; import sh.libre.scim.storage.ScimEndpointConfigurationStorageProviderFactory;
import java.util.ArrayList; import java.util.ArrayList;
@ -34,9 +34,7 @@ public class ScimDispatcher {
this.session = session; this.session = session;
this.exceptionHandler = new ScimExceptionHandler(session); this.exceptionHandler = new ScimExceptionHandler(session);
// By default, use a permissive Skip or Stop strategy // By default, use a permissive Skip or Stop strategy
this.skipOrStopStrategy = SkipOrStopStrategyFactory.create( this.skipOrStopStrategy = SkipOrStopApproach.ALWAYS_SKIP_AND_CONTINUE;
SkipOrStopStrategyFactory.SkipOrStopApproach.ALWAYS_SKIP_AND_CONTINUE
);
} }
/** /**

View file

@ -0,0 +1,44 @@
package sh.libre.scim.core.exceptions;
import sh.libre.scim.core.ScrimEndPointConfiguration;
public enum RollbackApproach implements RollbackStrategy {
ALWAYS_ROLLBACK {
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
return true;
}
},
NEVER_ROLLBACK {
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
return false;
}
},
CRITICAL_ONLY_ROLLBACK {
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
if (e instanceof InconsistentScimMappingException) {
// Occurs when mapping between a SCIM resource and a keycloak user failed (missing, ambiguous..)
// Log can be sufficient here, no rollback required
return false;
}
if (e instanceof UnexpectedScimDataException) {
// Occurs when a SCIM endpoint sends invalid date (e.g. group with empty name, user without ids...)
// No rollback required : we cannot recover. This needs to be fixed in the SCIM endpoint data
return false;
}
if (e instanceof InvalidResponseFromScimEndpointException invalidResponseFromScimEndpointException) {
return shouldRollbackBecauseOfResponse(invalidResponseFromScimEndpointException);
}
// Should not occur
throw new IllegalStateException("Unkown ScimPropagationException", e);
}
private boolean shouldRollbackBecauseOfResponse(InvalidResponseFromScimEndpointException e) {
int httpStatus = e.getResponse().getHttpStatus();
return httpStatus == 500;
}
}
}

View file

@ -1,32 +0,0 @@
package sh.libre.scim.core.exceptions;
import sh.libre.scim.core.ScrimEndPointConfiguration;
public class RollbackOnlyForCriticalErrorsStrategy implements RollbackStrategy {
private boolean shouldRollback(InvalidResponseFromScimEndpointException e) {
int httpStatus = e.getResponse().getHttpStatus();
return httpStatus == 500;
}
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
if (e instanceof InvalidResponseFromScimEndpointException invalidResponseFromScimEndpointException) {
return shouldRollback(invalidResponseFromScimEndpointException);
}
if (e instanceof InconsistentScimMappingException) {
// Occurs when mapping between a SCIM resource and a keycloak user failed (missing, ambiguous..)
// Log can be sufficient here, no rollback required
return false;
}
if (e instanceof UnexpectedScimDataException) {
// Occurs when a SCIM endpoint sends invalid date (e.g. group with empty name, user without ids...)
// No rollback required : we cannot recover. This needs to be fixed in the SCIM endpoint data
return false;
}
// Should not occur
throw new IllegalStateException("Unkown ScimPropagationException", e);
}
}

View file

@ -1,36 +0,0 @@
package sh.libre.scim.core.exceptions;
import sh.libre.scim.core.ScrimEndPointConfiguration;
public class RollbackStrategyFactory {
public static RollbackStrategy create(RollbackApproach approach) {
// We could imagine more fine-grained rollback strategies (e.g. based on each Scim endpoint configuration)
return switch (approach) {
case ALWAYS_ROLLBACK -> new AlwaysRollbackStrategy();
case NEVER_ROLLBACK -> new NeverRollbackStrategy();
case CRITICAL_ONLY_ROLLBACK -> new RollbackOnlyForCriticalErrorsStrategy();
};
}
public enum RollbackApproach {
ALWAYS_ROLLBACK, NEVER_ROLLBACK, CRITICAL_ONLY_ROLLBACK
}
private static final class AlwaysRollbackStrategy implements RollbackStrategy {
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
return true;
}
}
private static final class NeverRollbackStrategy implements RollbackStrategy {
@Override
public boolean shouldRollback(ScrimEndPointConfiguration configuration, ScimPropagationException e) {
return true;
}
}
}

View file

@ -17,7 +17,7 @@ public class ScimExceptionHandler {
private final RollbackStrategy rollbackStrategy; private final RollbackStrategy rollbackStrategy;
public ScimExceptionHandler(KeycloakSession session) { public ScimExceptionHandler(KeycloakSession session) {
this(session, RollbackStrategyFactory.create(RollbackStrategyFactory.RollbackApproach.CRITICAL_ONLY_ROLLBACK)); this(session, RollbackApproach.CRITICAL_ONLY_ROLLBACK);
} }
public ScimExceptionHandler(KeycloakSession session, RollbackStrategy rollbackStrategy) { public ScimExceptionHandler(KeycloakSession session, RollbackStrategy rollbackStrategy) {

View file

@ -2,22 +2,9 @@ package sh.libre.scim.core.exceptions;
import sh.libre.scim.core.ScrimEndPointConfiguration; import sh.libre.scim.core.ScrimEndPointConfiguration;
public class SkipOrStopStrategyFactory {
public static SkipOrStopStrategy create(SkipOrStopApproach approach) {
// We could imagine more fine-grained strategies (e.g. based on each Scim endpoint configuration)
return switch (approach) {
case ALWAYS_STOP -> new AlwaysStopStrategy();
case ALWAYS_SKIP_AND_CONTINUE -> new AlwaysSkipAndContinueStrategy();
};
}
public enum SkipOrStopApproach {
ALWAYS_SKIP_AND_CONTINUE, ALWAYS_STOP
}
private static final class AlwaysStopStrategy implements SkipOrStopStrategy {
public enum SkipOrStopApproach implements SkipOrStopStrategy {
ALWAYS_SKIP_AND_CONTINUE {
@Override @Override
public boolean allowPartialSynchronizationWhenPushingToScim(ScrimEndPointConfiguration configuration) { public boolean allowPartialSynchronizationWhenPushingToScim(ScrimEndPointConfiguration configuration) {
return false; return false;
@ -42,10 +29,8 @@ public class SkipOrStopStrategyFactory {
public boolean skipInvalidDataFromScimEndpoint(ScrimEndPointConfiguration configuration) { public boolean skipInvalidDataFromScimEndpoint(ScrimEndPointConfiguration configuration) {
return false; return false;
} }
} },
ALWAYS_STOP {
private static final class AlwaysSkipAndContinueStrategy implements SkipOrStopStrategy {
@Override @Override
public boolean allowPartialSynchronizationWhenPushingToScim(ScrimEndPointConfiguration configuration) { public boolean allowPartialSynchronizationWhenPushingToScim(ScrimEndPointConfiguration configuration) {
return true; return true;