Simplify ScimStorageProvider
This commit is contained in:
parent
764767185e
commit
f00130d37a
6 changed files with 161 additions and 162 deletions
|
@ -5,14 +5,14 @@ import org.keycloak.storage.user.SynchronizationResult;
|
|||
|
||||
/**
|
||||
* An interface for defining ScimClient.
|
||||
* A ScimClient provides methods for propagating CRUD and sync of a dedicated SCIM Resource (e.g. {@link de.captaingoldfish.scim.sdk.common.resources.User}). to a SCIM server defined in a {@link sh.libre.scim.storage.ScimStorageProvider}.
|
||||
* A ScimClient provides methods for propagating CRUD and sync of a dedicated SCIM Resource (e.g. {@link de.captaingoldfish.scim.sdk.common.resources.User}). to a Scim endpoint defined in a {@link sh.libre.scim.storage.ScimStorageProvider}.
|
||||
*
|
||||
* @param <M> the keycloack model to synchronize (e.g. UserModel or GroupModel)
|
||||
*/
|
||||
public interface ScimClientInterface<M extends RoleMapperModel> extends AutoCloseable {
|
||||
|
||||
/**
|
||||
* Propagates the creation of the given keycloack model to a SCIM server.
|
||||
* Propagates the creation of the given keycloack model to a Scim endpoint.
|
||||
*
|
||||
* @param resource the created resource to propagate (e.g. a new UserModel)
|
||||
*/
|
||||
|
@ -20,21 +20,21 @@ public interface ScimClientInterface<M extends RoleMapperModel> extends AutoClos
|
|||
void create(M resource);
|
||||
|
||||
/**
|
||||
* Propagates the update of the given keycloack model to a SCIM server.
|
||||
* Propagates the update of the given keycloack model to a Scim endpoint.
|
||||
*
|
||||
* @param resource the resource creation to propagate (e.g. a UserModel)
|
||||
*/
|
||||
void replace(M resource);
|
||||
|
||||
/**
|
||||
* Propagates the deletion of an element to a SCIM server.
|
||||
* Propagates the deletion of an element to a Scim endpoint.
|
||||
*
|
||||
* @param id the deleted resource's id to propagate (e.g. id of a UserModel)
|
||||
*/
|
||||
void delete(String id);
|
||||
|
||||
/**
|
||||
* Synchronizes resources between SCIM server and keycloack, according to configuration.
|
||||
* Synchronizes resources between Scim endpoint and keycloack, according to configuration.
|
||||
*
|
||||
* @param result the synchronization result to update for indicating triggered operations (e.g. user deletions)
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,7 @@ import java.util.function.Consumer;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* In charge of sending SCIM Request to all registered SCIM servers.
|
||||
* In charge of sending SCIM Request to all registered Scim endpoints.
|
||||
*/
|
||||
public class ScimDispatcher {
|
||||
|
||||
|
@ -22,12 +22,12 @@ public class ScimDispatcher {
|
|||
}
|
||||
|
||||
public void dispatchUserModificationToAll(Consumer<UserScimClient> operationToDispatch) {
|
||||
getAllSCIMServer(Scope.USER).forEach(scimServer -> dispatchUserModificationToOne(scimServer, operationToDispatch));
|
||||
getAllSCIMServer(Scope.USER).forEach(scimServerConfiguration -> dispatchUserModificationToOne(scimServerConfiguration, operationToDispatch));
|
||||
}
|
||||
|
||||
public void dispatchUserModificationToOne(ComponentModel scimServer, Consumer<UserScimClient> operationToDispatch) {
|
||||
LOGGER.infof("%s %s %s %s", scimServer.getId(), scimServer.getName(), scimServer.getProviderId(), scimServer.getProviderType());
|
||||
try (UserScimClient client = UserScimClient.newUserScimClient(scimServer, session)) {
|
||||
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);
|
||||
|
@ -35,12 +35,12 @@ public class ScimDispatcher {
|
|||
}
|
||||
|
||||
public void dispatchGroupModificationToAll(Consumer<GroupScimClient> operationToDispatch) {
|
||||
getAllSCIMServer(Scope.GROUP).forEach(scimServer -> dispatchGroupModificationToOne(scimServer, operationToDispatch));
|
||||
getAllSCIMServer(Scope.GROUP).forEach(scimServerConfiguration -> dispatchGroupModificationToOne(scimServerConfiguration, operationToDispatch));
|
||||
}
|
||||
|
||||
public void dispatchGroupModificationToOne(ComponentModel scimServer, Consumer<GroupScimClient> operationToDispatch) {
|
||||
LOGGER.infof("%s %s %s %s", scimServer.getId(), scimServer.getName(), scimServer.getProviderId(), scimServer.getProviderType());
|
||||
try (GroupScimClient client = GroupScimClient.newGroupScimClient(scimServer, session)) {
|
||||
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);
|
||||
|
@ -49,14 +49,19 @@ public class ScimDispatcher {
|
|||
|
||||
/**
|
||||
* @param scope The {@link Scope} to consider (User or Group)
|
||||
* @return all enabled registered SCIM Servers with propagation enabled for the given scope
|
||||
* @return all enabled registered Scim endpoints with propagation enabled for the given scope
|
||||
*/
|
||||
private Stream<ComponentModel> getAllSCIMServer(Scope scope) {
|
||||
// TODO : we could initiative this list once and invalidate it when configuration changes
|
||||
|
||||
String propagationConfKey = switch (scope) {
|
||||
case GROUP -> ScrimProviderConfiguration.CONF_KEY_PROPAGATION_GROUP;
|
||||
case USER -> ScrimProviderConfiguration.CONF_KEY_PROPAGATION_USER;
|
||||
};
|
||||
return session.getContext().getRealm().getComponentsStream()
|
||||
.filter(m -> ScimStorageProviderFactory.ID.equals(m.getProviderId())
|
||||
&& m.get("enabled", true)
|
||||
&& m.get("propagation-" + scope.name(), false));
|
||||
&& m.get(propagationConfKey, false));
|
||||
}
|
||||
|
||||
public enum Scope {
|
||||
|
|
|
@ -4,6 +4,17 @@ import de.captaingoldfish.scim.sdk.client.http.BasicAuth;
|
|||
import org.keycloak.component.ComponentModel;
|
||||
|
||||
public class ScrimProviderConfiguration {
|
||||
// Configuration keys : also used in Admin Console page
|
||||
public static final String CONF_KEY_AUTH_MODE = "auth-mode";
|
||||
public static final String CONF_KEY_AUTH_PASSWORD = "auth-pass";
|
||||
public static final String CONF_KEY_AUTH_USER = "auth-user";
|
||||
public static final String CONF_KEY_CONTENT_TYPE = "content-type";
|
||||
public static final String CONF_KEY_ENDPOINT = "endpoint";
|
||||
public static final String CONF_KEY_SYNC_IMPORT_ACTION = "sync-import-action";
|
||||
public static final String CONF_KEY_SYNC_IMPORT = "sync-import";
|
||||
public static final String CONF_KEY_SYNC_REFRESH = "sync-refresh";
|
||||
public static final String CONF_KEY_PROPAGATION_USER = "propagation-user";
|
||||
public static final String CONF_KEY_PROPAGATION_GROUP = "propagation-group";
|
||||
|
||||
private final String endPoint;
|
||||
private final String id;
|
||||
|
@ -14,25 +25,25 @@ public class ScrimProviderConfiguration {
|
|||
private final boolean syncRefresh;
|
||||
|
||||
public ScrimProviderConfiguration(ComponentModel scimProviderConfiguration) {
|
||||
AuthMode authMode = AuthMode.valueOf(scimProviderConfiguration.get("auth-mode"));
|
||||
AuthMode authMode = AuthMode.valueOf(scimProviderConfiguration.get(CONF_KEY_AUTH_MODE));
|
||||
authorizationHeaderValue = switch (authMode) {
|
||||
case BEARER -> "Bearer " + scimProviderConfiguration.get("auth-pass");
|
||||
case BEARER -> "Bearer " + scimProviderConfiguration.get(CONF_KEY_AUTH_PASSWORD);
|
||||
case BASIC_AUTH -> {
|
||||
BasicAuth basicAuth = BasicAuth.builder()
|
||||
.username(scimProviderConfiguration.get("auth-user"))
|
||||
.password(scimProviderConfiguration.get("auth-pass"))
|
||||
.username(scimProviderConfiguration.get(CONF_KEY_AUTH_USER))
|
||||
.password(scimProviderConfiguration.get(CONF_KEY_AUTH_PASSWORD))
|
||||
.build();
|
||||
yield basicAuth.getAuthorizationHeaderValue();
|
||||
}
|
||||
default ->
|
||||
throw new IllegalArgumentException("authMode " + scimProviderConfiguration + " is not supported");
|
||||
};
|
||||
contentType = scimProviderConfiguration.get("content-type");
|
||||
endPoint = scimProviderConfiguration.get("endpoint");
|
||||
contentType = scimProviderConfiguration.get(CONF_KEY_CONTENT_TYPE);
|
||||
endPoint = scimProviderConfiguration.get(CONF_KEY_ENDPOINT);
|
||||
id = scimProviderConfiguration.getId();
|
||||
importAction = ImportAction.valueOf(scimProviderConfiguration.get("sync-import-action"));
|
||||
syncImport = scimProviderConfiguration.get("sync-import", false);
|
||||
syncRefresh = scimProviderConfiguration.get("sync-refresh", false);
|
||||
importAction = ImportAction.valueOf(scimProviderConfiguration.get(CONF_KEY_SYNC_IMPORT_ACTION));
|
||||
syncImport = scimProviderConfiguration.get(CONF_KEY_SYNC_IMPORT, false);
|
||||
syncRefresh = scimProviderConfiguration.get(CONF_KEY_SYNC_REFRESH, false);
|
||||
}
|
||||
|
||||
public boolean isSyncRefresh() {
|
||||
|
@ -67,10 +78,6 @@ public class ScrimProviderConfiguration {
|
|||
BEARER, BASIC_AUTH, NONE
|
||||
}
|
||||
|
||||
public enum EndpointContentType {
|
||||
JSON, SCIM_JSON
|
||||
}
|
||||
|
||||
public enum ImportAction {
|
||||
CREATE_LOCAL, DELETE_REMOTE, NOTHING
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ import java.util.regex.Pattern;
|
|||
/**
|
||||
* An {@link java.util.EventListener} in charge of reaction to Keycloak models
|
||||
* modification (e.g. User creation, Group deletion, membership modifications...)
|
||||
* by propagating it to all registered SCIM servers.
|
||||
* by propagating it to all registered Scim endpoints.
|
||||
*/
|
||||
public class ScimEventListenerProvider implements EventListenerProvider {
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
|||
}
|
||||
case DELETE_ACCOUNT -> dispatcher.dispatchUserModificationToAll(client -> client.delete(eventUserId));
|
||||
default -> {
|
||||
// No other event has to be propagated to SCIM Servers
|
||||
// No other event has to be propagated to Scim endpoints
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
|||
handleRoleMappingEvent(event, type, id);
|
||||
}
|
||||
default -> {
|
||||
// No other resource modification has to be propagated to SCIM Servers
|
||||
// No other resource modification has to be propagated to Scim endpoints
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Propagating the given group-related event to SCIM servers.
|
||||
* Propagating the given group-related event to Scim endpoints.
|
||||
*
|
||||
* @param event the event to propagate
|
||||
* @param groupId event target's id
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
package sh.libre.scim.storage;
|
||||
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
|
||||
public class ScimStorageProvider implements UserStorageProvider {
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
|
@ -8,146 +8,52 @@ import org.keycloak.component.ComponentModel;
|
|||
import org.keycloak.models.GroupModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakSessionTask;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderConfigurationBuilder;
|
||||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderFactory;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.user.ImportSynchronization;
|
||||
import org.keycloak.storage.user.SynchronizationResult;
|
||||
import org.keycloak.timer.TimerProvider;
|
||||
import sh.libre.scim.core.GroupAdapter;
|
||||
import sh.libre.scim.core.ScimDispatcher;
|
||||
import sh.libre.scim.core.UserAdapter;
|
||||
import sh.libre.scim.core.ScrimProviderConfiguration;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
public class ScimStorageProviderFactory
|
||||
implements UserStorageProviderFactory<ScimStorageProvider>, ImportSynchronization {
|
||||
|
||||
private final Logger LOGGER = Logger.getLogger(ScimStorageProviderFactory.class);
|
||||
|
||||
implements UserStorageProviderFactory<ScimStorageProviderFactory.ScimStorageProvider>, ImportSynchronization {
|
||||
public static final String ID = "scim";
|
||||
|
||||
private static final List<ProviderConfigProperty> CONFIG_METADATA;
|
||||
|
||||
static {
|
||||
CONFIG_METADATA = ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name("endpoint")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.required(true)
|
||||
.label("SCIM 2.0 endpoint")
|
||||
.helpText("External SCIM 2.0 base " +
|
||||
"URL (/ServiceProviderConfig /Schemas and /ResourcesTypes should be accessible)")
|
||||
.add()
|
||||
.property()
|
||||
.name("content-type")
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Endpoint content type")
|
||||
.helpText("Only used when endpoint doesn't support application/scim+json")
|
||||
.options(MediaType.APPLICATION_JSON, HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.defaultValue(HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.add()
|
||||
.property()
|
||||
.name("auth-mode")
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Auth mode")
|
||||
.helpText("Select the authorization mode")
|
||||
.options("NONE", "BASIC_AUTH", "BEARER")
|
||||
.defaultValue("NONE")
|
||||
.add()
|
||||
.property()
|
||||
.name("auth-user")
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Auth username")
|
||||
.helpText("Required for basic authentication.")
|
||||
.add()
|
||||
.property()
|
||||
.name("auth-pass")
|
||||
.type(ProviderConfigProperty.PASSWORD)
|
||||
.label("Auth password/token")
|
||||
.helpText("Password or token required for basic or bearer authentication.")
|
||||
.add()
|
||||
.property()
|
||||
.name("propagation-user")
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable user propagation")
|
||||
.helpText("Should operation on users be propagated to this provider?")
|
||||
.defaultValue(BooleanUtils.TRUE)
|
||||
.add()
|
||||
.property()
|
||||
.name("propagation-group")
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable group propagation")
|
||||
.helpText("Should operation on groups be propagated to this provider?")
|
||||
.defaultValue(BooleanUtils.TRUE)
|
||||
.add()
|
||||
.property()
|
||||
.name("sync-import")
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable import during sync")
|
||||
.add()
|
||||
.property()
|
||||
.name("sync-import-action")
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Import action")
|
||||
.helpText("What to do when the user doesn't exists in Keycloak.")
|
||||
.options("NOTHING", "CREATE_LOCAL", "DELETE_REMOTE")
|
||||
.defaultValue("CREATE_LOCAL")
|
||||
.add()
|
||||
.property()
|
||||
.name("sync-refresh")
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable refresh during sync")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScimStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
LOGGER.info("create");
|
||||
return new ScimStorageProvider();
|
||||
}
|
||||
private final Logger logger = Logger.getLogger(ScimStorageProviderFactory.class);
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
return CONFIG_METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SynchronizationResult sync(KeycloakSessionFactory sessionFactory, String realmId,
|
||||
UserStorageProviderModel model) {
|
||||
LOGGER.info("sync");
|
||||
// TODO if this should be kept here, better document prupose & usage
|
||||
logger.infof("[SCIM] Sync from ScimStorageProvider - Realm %s", realmId);
|
||||
SynchronizationResult result = new SynchronizationResult();
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() {
|
||||
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
RealmModel realm = session.realms().getRealm(realmId);
|
||||
session.getContext().setRealm(realm);
|
||||
ScimDispatcher dispatcher = new ScimDispatcher(session);
|
||||
if (BooleanUtils.TRUE.equals(model.get("propagation-user"))) {
|
||||
dispatcher.dispatchUserModificationToOne(model, client -> client.sync(result));
|
||||
}
|
||||
if (BooleanUtils.TRUE.equals(model.get("propagation-group"))) {
|
||||
dispatcher.dispatchGroupModificationToOne(model, client -> client.sync(result));
|
||||
}
|
||||
KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> {
|
||||
RealmModel realm = session.realms().getRealm(realmId);
|
||||
session.getContext().setRealm(realm);
|
||||
ScimDispatcher dispatcher = new ScimDispatcher(session);
|
||||
if (BooleanUtils.TRUE.equals(model.get("propagation-user"))) {
|
||||
dispatcher.dispatchUserModificationToOne(model, client -> client.sync(result));
|
||||
}
|
||||
if (BooleanUtils.TRUE.equals(model.get("propagation-group"))) {
|
||||
dispatcher.dispatchGroupModificationToOne(model, client -> client.sync(result));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,24 +64,114 @@ public class ScimStorageProviderFactory
|
|||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
TimerProvider timer = factory.create().getProvider(TimerProvider.class);
|
||||
timer.scheduleTask(taskSession -> {
|
||||
for (RealmModel realm : taskSession.realms().getRealmsStream().toList()) {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, new KeycloakSessionTask() {
|
||||
@Override
|
||||
public void run(KeycloakSession session) {
|
||||
// TODO : find a better way to handle scim dirty (use a QUEUE for SCIM queries ?)
|
||||
try (KeycloakSession keycloakSession = factory.create()) {
|
||||
TimerProvider timer = keycloakSession.getProvider(TimerProvider.class);
|
||||
timer.scheduleTask(taskSession -> {
|
||||
for (RealmModel realm : taskSession.realms().getRealmsStream().toList()) {
|
||||
KeycloakModelUtils.runJobInTransaction(factory, session -> {
|
||||
session.getContext().setRealm(realm);
|
||||
ScimDispatcher dispatcher = new ScimDispatcher(session);
|
||||
for (GroupModel group : session.groups().getGroupsStream(realm)
|
||||
.filter(x -> BooleanUtils.TRUE.equals(x.getFirstAttribute("scim-dirty"))).toList()) {
|
||||
LOGGER.debug(group.getName() + " is dirty");
|
||||
logger.infof("[SCIM] Dirty group : %s", group.getName());
|
||||
dispatcher.dispatchGroupModificationToAll(client -> client.replace(group));
|
||||
group.removeAttribute("scim-dirty");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, Duration.ofSeconds(30).toMillis(), "scim-background");
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}, Duration.ofSeconds(30).toMillis(), "scim-background");
|
||||
@Override
|
||||
public List<ProviderConfigProperty> getConfigProperties() {
|
||||
// These Config Properties will be use to generate configuration page in Admin Console
|
||||
return ProviderConfigurationBuilder.create()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_ENDPOINT)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.required(true)
|
||||
.label("SCIM 2.0 endpoint")
|
||||
.helpText("External SCIM 2.0 base " +
|
||||
"URL (/ServiceProviderConfig /Schemas and /ResourcesTypes should be accessible)")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_CONTENT_TYPE)
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Endpoint content type")
|
||||
.helpText("Only used when endpoint doesn't support application/scim+json")
|
||||
.options(MediaType.APPLICATION_JSON, HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.defaultValue(HttpHeader.SCIM_CONTENT_TYPE)
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_AUTH_MODE)
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Auth mode")
|
||||
.helpText("Select the authorization mode")
|
||||
.options("NONE", "BASIC_AUTH", "BEARER")
|
||||
.defaultValue("NONE")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_AUTH_USER)
|
||||
.type(ProviderConfigProperty.STRING_TYPE)
|
||||
.label("Auth username")
|
||||
.helpText("Required for basic authentication.")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_AUTH_PASSWORD)
|
||||
.type(ProviderConfigProperty.PASSWORD)
|
||||
.label("Auth password/token")
|
||||
.helpText("Password or token required for basic or bearer authentication.")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_PROPAGATION_USER)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable user propagation")
|
||||
.helpText("Should operation on users be propagated to this provider?")
|
||||
.defaultValue(BooleanUtils.TRUE)
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_PROPAGATION_GROUP)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable group propagation")
|
||||
.helpText("Should operation on groups be propagated to this provider?")
|
||||
.defaultValue(BooleanUtils.TRUE)
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_SYNC_IMPORT)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable import during sync")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_SYNC_IMPORT_ACTION)
|
||||
.type(ProviderConfigProperty.LIST_TYPE)
|
||||
.label("Import action")
|
||||
.helpText("What to do when the user doesn't exists in Keycloak.")
|
||||
.options("NOTHING", "CREATE_LOCAL", "DELETE_REMOTE")
|
||||
.defaultValue("CREATE_LOCAL")
|
||||
.add()
|
||||
.property()
|
||||
.name(ScrimProviderConfiguration.CONF_KEY_SYNC_REFRESH)
|
||||
.type(ProviderConfigProperty.BOOLEAN_TYPE)
|
||||
.label("Enable refresh during sync")
|
||||
.add()
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ScimStorageProvider create(KeycloakSession session, ComponentModel model) {
|
||||
return new ScimStorageProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
* Empty implementation : we used this {@link ScimStorageProviderFactory} to generate Admin Console page.
|
||||
*/
|
||||
public static final class ScimStorageProvider implements UserStorageProvider {
|
||||
@Override
|
||||
public void close() {
|
||||
// Nothing to close here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue