Refactor Scim Event listener

This commit is contained in:
Alex Morel 2024-06-20 17:36:46 +02:00
parent f81001503d
commit 2e992dad44
3 changed files with 35 additions and 26 deletions

View file

@ -89,14 +89,6 @@ public abstract class AbstractScimService<RMM extends RoleMapperModel, S extends
protected abstract ResourceNode toScimForReplace(RMM roleMapperModel, EntityOnRemoteScimId externalId); protected abstract ResourceNode toScimForReplace(RMM roleMapperModel, EntityOnRemoteScimId externalId);
/**
* @deprecated use {@link #delete(KeycloakId)}
*/
@Deprecated
public void delete(String id) {
delete(new KeycloakId(id));
}
public void delete(KeycloakId id) { public void delete(KeycloakId id) {
try { try {
ScimResource resource = findById(id).get(); ScimResource resource = findById(id).get();

View file

@ -40,6 +40,11 @@ public class KeycloakDao {
return getKeycloakSession().users().getUserById(getRealm(), userId.asString()); return getKeycloakSession().users().getUserById(getRealm(), userId.asString());
} }
public GroupModel getGroupById(KeycloakId groupId) {
return getKeycloakSession().groups().getGroupById(getRealm(), groupId.asString());
}
public Stream<GroupModel> getGroupsStream() { public Stream<GroupModel> getGroupsStream() {
return getKeycloakSession().groups().getGroupsStream(getRealm()); return getKeycloakSession().groups().getGroupsStream(getRealm());
} }
@ -71,4 +76,6 @@ public class KeycloakDao {
public UserModel addUser(String username) { public UserModel addUser(String username) {
return getKeycloakSession().users().addUser(getRealm(), username); return getKeycloakSession().users().addUser(getRealm(), username);
} }
} }

View file

@ -11,7 +11,10 @@ import org.keycloak.events.admin.ResourceType;
import org.keycloak.models.GroupModel; import org.keycloak.models.GroupModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import sh.libre.scim.core.KeycloakDao;
import sh.libre.scim.core.KeycloakId;
import sh.libre.scim.core.ScimDispatcher; import sh.libre.scim.core.ScimDispatcher;
import sh.libre.scim.core.ScimResourceType;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
@ -30,6 +33,8 @@ public class ScimEventListenerProvider implements EventListenerProvider {
private final KeycloakSession session; private final KeycloakSession session;
private final KeycloakDao keycloackDao;
private final Map<ResourceType, Pattern> patterns = Map.of( private final Map<ResourceType, Pattern> patterns = Map.of(
ResourceType.USER, Pattern.compile("users/(.+)"), ResourceType.USER, Pattern.compile("users/(.+)"),
ResourceType.GROUP, Pattern.compile("groups/([\\w-]+)(/children)?"), ResourceType.GROUP, Pattern.compile("groups/([\\w-]+)(/children)?"),
@ -40,15 +45,15 @@ public class ScimEventListenerProvider implements EventListenerProvider {
public ScimEventListenerProvider(KeycloakSession session) { public ScimEventListenerProvider(KeycloakSession session) {
this.session = session; this.session = session;
this.keycloackDao = new KeycloakDao(session);
this.dispatcher = ScimDispatcher.createForSession(session); this.dispatcher = ScimDispatcher.createForSession(session);
} }
@Override @Override
public void onEvent(Event event) { public void onEvent(Event event) {
// React to User-related event : creation, deletion, update // React to User-related event : creation, deletion, update
EventType eventType = event.getType(); EventType eventType = event.getType();
String eventUserId = event.getUserId(); KeycloakId eventUserId = new KeycloakId(event.getUserId());
switch (eventType) { switch (eventType) {
case REGISTER -> { case REGISTER -> {
LOGGER.infof("[SCIM] Propagate User Registration - %s", eventUserId); LOGGER.infof("[SCIM] Propagate User Registration - %s", eventUserId);
@ -85,21 +90,26 @@ public class ScimEventListenerProvider implements EventListenerProvider {
// Step 2: propagate event (if needed) according to its resource type // Step 2: propagate event (if needed) according to its resource type
switch (event.getResourceType()) { switch (event.getResourceType()) {
case USER -> { case USER -> {
String userId = matcher.group(1); KeycloakId userId = new KeycloakId(matcher.group(1));
handleUserEvent(event, userId); handleUserEvent(event, userId);
} }
case GROUP -> { case GROUP -> {
String groupId = matcher.group(1); KeycloakId groupId = new KeycloakId(matcher.group(1));
handleGroupEvent(event, groupId); handleGroupEvent(event, groupId);
} }
case GROUP_MEMBERSHIP -> { case GROUP_MEMBERSHIP -> {
String userId = matcher.group(1); KeycloakId userId = new KeycloakId(matcher.group(1));
String groupId = matcher.group(2); KeycloakId groupId = new KeycloakId(matcher.group(2));
handleGroupMemberShipEvent(event, userId, groupId); handleGroupMemberShipEvent(event, userId, groupId);
} }
case REALM_ROLE_MAPPING -> { case REALM_ROLE_MAPPING -> {
String type = matcher.group(1); String rawResourceType = matcher.group(1);
String id = matcher.group(2); ScimResourceType type = switch (rawResourceType) {
case "users" -> ScimResourceType.USER;
case "groups" -> ScimResourceType.GROUP;
default -> throw new IllegalArgumentException("Unsuported resource type : " + rawResourceType);
};
KeycloakId id = new KeycloakId(matcher.group(2));
handleRoleMappingEvent(event, type, id); handleRoleMappingEvent(event, type, id);
} }
case COMPONENT -> { case COMPONENT -> {
@ -114,7 +124,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
} }
private void handleUserEvent(AdminEvent userEvent, String userId) { private void handleUserEvent(AdminEvent userEvent, KeycloakId userId) {
LOGGER.infof("[SCIM] Propagate User %s - %s", userEvent.getOperationType(), userId); LOGGER.infof("[SCIM] Propagate User %s - %s", userEvent.getOperationType(), userId);
switch (userEvent.getOperationType()) { switch (userEvent.getOperationType()) {
case CREATE -> { case CREATE -> {
@ -141,7 +151,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
* @param event the event to propagate * @param event the event to propagate
* @param groupId event target's id * @param groupId event target's id
*/ */
private void handleGroupEvent(AdminEvent event, String groupId) { private void handleGroupEvent(AdminEvent event, KeycloakId groupId) {
LOGGER.infof("[SCIM] Propagate Group %s - %s", event.getOperationType(), groupId); LOGGER.infof("[SCIM] Propagate Group %s - %s", event.getOperationType(), groupId);
switch (event.getOperationType()) { switch (event.getOperationType()) {
case CREATE -> { case CREATE -> {
@ -159,7 +169,7 @@ public class ScimEventListenerProvider implements EventListenerProvider {
} }
} }
private void handleGroupMemberShipEvent(AdminEvent groupMemberShipEvent, String userId, String groupId) { private void handleGroupMemberShipEvent(AdminEvent groupMemberShipEvent, KeycloakId userId, KeycloakId groupId) {
LOGGER.infof("[SCIM] Propagate GroupMemberShip %s - User %s Group %s", groupMemberShipEvent.getOperationType(), userId, groupId); LOGGER.infof("[SCIM] Propagate GroupMemberShip %s - User %s Group %s", groupMemberShipEvent.getOperationType(), userId, groupId);
GroupModel group = getGroup(groupId); GroupModel group = getGroup(groupId);
group.setSingleAttribute("scim-dirty", BooleanUtils.TRUE); group.setSingleAttribute("scim-dirty", BooleanUtils.TRUE);
@ -167,14 +177,14 @@ public class ScimEventListenerProvider implements EventListenerProvider {
dispatcher.dispatchUserModificationToAll(client -> client.replace(user)); dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
} }
private void handleRoleMappingEvent(AdminEvent roleMappingEvent, String type, String id) { private void handleRoleMappingEvent(AdminEvent roleMappingEvent, ScimResourceType type, KeycloakId id) {
LOGGER.infof("[SCIM] Propagate RoleMapping %s - %s %s", roleMappingEvent.getOperationType(), type, id); LOGGER.infof("[SCIM] Propagate RoleMapping %s - %s %s", roleMappingEvent.getOperationType(), type, id);
switch (type) { switch (type) {
case "users" -> { case USER -> {
UserModel user = getUser(id); UserModel user = getUser(id);
dispatcher.dispatchUserModificationToAll(client -> client.replace(user)); dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
} }
case "groups" -> { case GROUP -> {
GroupModel group = getGroup(id); GroupModel group = getGroup(id);
session.users() session.users()
.getGroupMembersStream(session.getContext().getRealm(), group) .getGroupMembersStream(session.getContext().getRealm(), group)
@ -208,12 +218,12 @@ public class ScimEventListenerProvider implements EventListenerProvider {
} }
private UserModel getUser(String id) { private UserModel getUser(KeycloakId id) {
return session.users().getUserById(session.getContext().getRealm(), id); return keycloackDao.getUserById(id);
} }
private GroupModel getGroup(String id) { private GroupModel getGroup(KeycloakId id) {
return session.groups().getGroupById(session.getContext().getRealm(), id); return keycloackDao.getGroupById(id);
} }
@Override @Override