add propagation options + simplify auth config

This commit is contained in:
Hugo Renard 2022-05-03 16:20:38 +02:00
parent 4a2cdb5e86
commit 55052475c6
Signed by: hougo
GPG key ID: 3A285FD470209C59
5 changed files with 47 additions and 33 deletions

View file

@ -10,7 +10,6 @@ import org.jboss.logging.Logger;
import org.keycloak.connections.jpa.JpaConnectionProvider; import org.keycloak.connections.jpa.JpaConnectionProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RoleMapperModel; import org.keycloak.models.RoleMapperModel;
import sh.libre.scim.jpa.ScimResource; import sh.libre.scim.jpa.ScimResource;
public abstract class Adapter<M extends RoleMapperModel, S extends com.unboundid.scim2.common.ScimResource> { public abstract class Adapter<M extends RoleMapperModel, S extends com.unboundid.scim2.common.ScimResource> {
@ -124,7 +123,7 @@ public abstract class Adapter<M extends RoleMapperModel, S extends com.unboundid
public abstract Boolean tryToMap(); public abstract Boolean tryToMap();
public abstract void createEntity(); public abstract void createEntity();
public abstract Stream<M> getResourceStream(); public abstract Stream<M> getResourceStream();
public abstract Boolean skipRefresh(); public abstract Boolean skipRefresh();

View file

@ -38,12 +38,12 @@ public class ScimClient {
var target = client.target(model.get("endpoint")); var target = client.target(model.get("endpoint"));
switch (model.get("auth-mode")) { switch (model.get("auth-mode")) {
case "BEARER": case "BEARER":
target = target.register(new BearerAuthentication(model.get("auth-bearer-token"))); target = target.register(new BearerAuthentication(model.get("auth-pass")));
break; break;
case "BASIC_AUTH": case "BASIC_AUTH":
target = target.register(new BasicAuthentication( target = target.register(new BasicAuthentication(
model.get("auth-basic-auth-user"), model.get("auth-user"),
model.get("auth-basic-auth-pass"))); model.get("auth-pass")));
} }
scimService = new ScimService(target); scimService = new ScimService(target);

View file

@ -7,6 +7,9 @@ import org.keycloak.models.KeycloakSession;
import sh.libre.scim.storage.ScimStorageProviderFactory; import sh.libre.scim.storage.ScimStorageProviderFactory;
public class ScimDispatcher { public class ScimDispatcher {
public static final String SCOPE_USER = "user";
public static final String SCOPE_GROUP = "group";
final private KeycloakSession session; final private KeycloakSession session;
final private Logger LOGGER = Logger.getLogger(ScimDispatcher.class); final private Logger LOGGER = Logger.getLogger(ScimDispatcher.class);
@ -14,10 +17,10 @@ public class ScimDispatcher {
this.session = session; this.session = session;
} }
public void run(Consumer<ScimClient> f) { public void run(String scope, Consumer<ScimClient> f) {
session.getContext().getRealm().getComponentsStream() session.getContext().getRealm().getComponentsStream()
.filter((m) -> { .filter((m) -> {
return ScimStorageProviderFactory.ID.equals(m.getProviderId()) && m.get("enabled").equals("true"); return ScimStorageProviderFactory.ID.equals(m.getProviderId()) && m.get("enabled").equals("true") && m.get("propagation-"+scope).equals("true");
}) })
.forEach(m -> { .forEach(m -> {
LOGGER.infof("%s %s %s %s", m.getId(), m.getName(), m.getProviderId(), m.getProviderType()); LOGGER.infof("%s %s %s %s", m.getId(), m.getName(), m.getProviderId(), m.getProviderType());

View file

@ -35,14 +35,14 @@ public class ScimEventListenerProvider implements EventListenerProvider {
public void onEvent(Event event) { public void onEvent(Event event) {
if (event.getType() == EventType.REGISTER) { if (event.getType() == EventType.REGISTER) {
var user = getUser(event.getUserId()); var user = getUser(event.getUserId());
dispatcher.run((client) -> client.create(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.create(UserAdapter.class, user));
} }
if (event.getType() == EventType.UPDATE_EMAIL || event.getType() == EventType.UPDATE_PROFILE) { if (event.getType() == EventType.UPDATE_EMAIL || event.getType() == EventType.UPDATE_PROFILE) {
var user = getUser(event.getUserId()); var user = getUser(event.getUserId());
dispatcher.run((client) -> client.replace(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.replace(UserAdapter.class, user));
} }
if (event.getType() == EventType.DELETE_ACCOUNT) { if (event.getType() == EventType.DELETE_ACCOUNT) {
dispatcher.run((client) -> client.delete(UserAdapter.class, event.getUserId())); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.delete(UserAdapter.class, event.getUserId()));
} }
} }
@ -53,14 +53,14 @@ public class ScimEventListenerProvider implements EventListenerProvider {
LOGGER.infof("%s %s", userId, event.getOperationType()); LOGGER.infof("%s %s", userId, event.getOperationType());
if (event.getOperationType() == OperationType.CREATE) { if (event.getOperationType() == OperationType.CREATE) {
var user = getUser(userId); var user = getUser(userId);
dispatcher.run((client) -> client.create(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.create(UserAdapter.class, user));
} }
if (event.getOperationType() == OperationType.UPDATE) { if (event.getOperationType() == OperationType.UPDATE) {
var user = getUser(userId); var user = getUser(userId);
dispatcher.run((client) -> client.replace(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.replace(UserAdapter.class, user));
} }
if (event.getOperationType() == OperationType.DELETE) { if (event.getOperationType() == OperationType.DELETE) {
dispatcher.run((client) -> client.delete(UserAdapter.class, userId)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.delete(UserAdapter.class, userId));
} }
} }
if (event.getResourceType() == ResourceType.GROUP) { if (event.getResourceType() == ResourceType.GROUP) {
@ -68,14 +68,14 @@ public class ScimEventListenerProvider implements EventListenerProvider {
LOGGER.infof("%s %s", event.getResourcePath(), event.getOperationType()); LOGGER.infof("%s %s", event.getResourcePath(), event.getOperationType());
if (event.getOperationType() == OperationType.CREATE) { if (event.getOperationType() == OperationType.CREATE) {
var group = getGroup(groupId); var group = getGroup(groupId);
dispatcher.run((client) -> client.create(GroupAdapter.class, group)); dispatcher.run(ScimDispatcher.SCOPE_GROUP, (client) -> client.create(GroupAdapter.class, group));
} }
if (event.getOperationType() == OperationType.UPDATE) { if (event.getOperationType() == OperationType.UPDATE) {
var group = getGroup(groupId); var group = getGroup(groupId);
dispatcher.run((client) -> client.replace(GroupAdapter.class, group)); dispatcher.run(ScimDispatcher.SCOPE_GROUP, (client) -> client.replace(GroupAdapter.class, group));
} }
if (event.getOperationType() == OperationType.DELETE) { if (event.getOperationType() == OperationType.DELETE) {
dispatcher.run((client) -> client.delete(GroupAdapter.class, groupId)); dispatcher.run(ScimDispatcher.SCOPE_GROUP, (client) -> client.delete(GroupAdapter.class, groupId));
} }
} }
if (event.getResourceType() == ResourceType.GROUP_MEMBERSHIP) { if (event.getResourceType() == ResourceType.GROUP_MEMBERSHIP) {
@ -86,9 +86,9 @@ public class ScimEventListenerProvider implements EventListenerProvider {
var groupId = matcher.group(2); var groupId = matcher.group(2);
LOGGER.infof("%s %s from %s", event.getOperationType(), userId, groupId); LOGGER.infof("%s %s from %s", event.getOperationType(), userId, groupId);
var group = getGroup(groupId); var group = getGroup(groupId);
dispatcher.run((client) -> client.replace(GroupAdapter.class, group)); dispatcher.run(ScimDispatcher.SCOPE_GROUP, (client) -> client.replace(GroupAdapter.class, group));
var user = getUser(userId); var user = getUser(userId);
dispatcher.run((client) -> client.replace(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.replace(UserAdapter.class, user));
} }
} }
if (event.getResourceType() == ResourceType.REALM_ROLE_MAPPING) { if (event.getResourceType() == ResourceType.REALM_ROLE_MAPPING) {
@ -100,11 +100,11 @@ public class ScimEventListenerProvider implements EventListenerProvider {
LOGGER.infof("%s %s %s roles", event.getOperationType(), type, id); LOGGER.infof("%s %s %s roles", event.getOperationType(), type, id);
if (type.equals("users")) { if (type.equals("users")) {
var user = getUser(id); var user = getUser(id);
dispatcher.run((client) -> client.replace(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.replace(UserAdapter.class, user));
} else if (type.equals("groups")) { } else if (type.equals("groups")) {
var group = getGroup(id); var group = getGroup(id);
session.users().getGroupMembersStream(session.getContext().getRealm(), group).forEach(user -> { session.users().getGroupMembersStream(session.getContext().getRealm(), group).forEach(user -> {
dispatcher.run((client) -> client.replace(UserAdapter.class, user)); dispatcher.run(ScimDispatcher.SCOPE_USER, (client) -> client.replace(UserAdapter.class, user));
}); });
} }
} }

View file

@ -52,24 +52,32 @@ public class ScimStorageProviderFactory
.type(ProviderConfigProperty.LIST_TYPE) .type(ProviderConfigProperty.LIST_TYPE)
.label("Auth mode") .label("Auth mode")
.helpText("Select the authorization mode") .helpText("Select the authorization mode")
.options("NONE", "BEARER", "BASIC_AUTH") .options("NONE", "BASIC_AUTH", "BEARER")
.defaultValue("NONE") .defaultValue("NONE")
.add() .add()
.property() .property()
.name("auth-bearer-token") .name("auth-user")
.type(ProviderConfigProperty.PASSWORD)
.label("Bearer token")
.helpText("Add a bearer token in the authorization header")
.add()
.property()
.name("auth-basic-auth-user")
.type(ProviderConfigProperty.STRING_TYPE) .type(ProviderConfigProperty.STRING_TYPE)
.label("BasicAuth user") .label("Auth username")
.helpText("Required for basic authentification.")
.add() .add()
.property() .property()
.name("auth-basic-auth-pass") .name("auth-pass")
.type(ProviderConfigProperty.PASSWORD) .type(ProviderConfigProperty.PASSWORD)
.label("BasicAuth password") .label("Auth password/token")
.helpText("Password or token required for basic or bearer authentification.")
.add()
.property()
.name("propagation-user")
.type(ProviderConfigProperty.BOOLEAN_TYPE)
.label("Enable user propagation")
.defaultValue("true")
.add()
.property()
.name("propagation-group")
.type(ProviderConfigProperty.BOOLEAN_TYPE)
.label("Enable group propagation")
.defaultValue("true")
.add() .add()
.property() .property()
.name("sync-import") .name("sync-import")
@ -120,8 +128,12 @@ public class ScimStorageProviderFactory
RealmModel realm = session.realms().getRealm(realmId); RealmModel realm = session.realms().getRealm(realmId);
session.getContext().setRealm(realm); session.getContext().setRealm(realm);
var client = new ScimClient(model, session); var client = new ScimClient(model, session);
client.sync(UserAdapter.class, result); if (model.get("propagation-user").equals("true")) {
client.sync(GroupAdapter.class, result); client.sync(UserAdapter.class, result);
}
if (model.get("propagation-group").equals("true")) {
client.sync(GroupAdapter.class, result);
}
client.close(); client.close();
} }