Simply ScimEventListener code
This commit is contained in:
parent
5976031ac6
commit
764767185e
2 changed files with 109 additions and 53 deletions
|
@ -16,6 +16,11 @@ import java.util.Map;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
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.
|
||||||
|
*/
|
||||||
public class ScimEventListenerProvider implements EventListenerProvider {
|
public class ScimEventListenerProvider implements EventListenerProvider {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(ScimEventListenerProvider.class);
|
private static final Logger LOGGER = Logger.getLogger(ScimEventListenerProvider.class);
|
||||||
|
@ -36,14 +41,13 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
||||||
dispatcher = new ScimDispatcher(session);
|
dispatcher = new ScimDispatcher(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) {
|
public void onEvent(Event event) {
|
||||||
|
// React to User-related event : creation, deletion, update
|
||||||
EventType eventType = event.getType();
|
EventType eventType = event.getType();
|
||||||
String eventUserId = event.getUserId();
|
String eventUserId = event.getUserId();
|
||||||
|
LOGGER.infof("[SCIM] Propagate User Event %s - %s", eventType, eventUserId);
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
case REGISTER -> {
|
case REGISTER -> {
|
||||||
UserModel user = getUser(eventUserId);
|
UserModel user = getUser(eventUserId);
|
||||||
|
@ -54,81 +58,123 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
||||||
}
|
}
|
||||||
case DELETE_ACCOUNT -> dispatcher.dispatchUserModificationToAll(client -> client.delete(eventUserId));
|
case DELETE_ACCOUNT -> dispatcher.dispatchUserModificationToAll(client -> client.delete(eventUserId));
|
||||||
default -> LOGGER.trace("ignore event " + eventType);
|
default -> {
|
||||||
|
// No other event has to be propagated to SCIM Servers
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
public void onEvent(AdminEvent event, boolean includeRepresentation) {
|
||||||
|
// Step 1: check if event is relevant for propagation through SCIM
|
||||||
Pattern pattern = patterns.get(event.getResourceType());
|
Pattern pattern = patterns.get(event.getResourceType());
|
||||||
if (pattern == null)
|
if (pattern == null)
|
||||||
return;
|
return;
|
||||||
Matcher matcher = pattern.matcher(event.getResourcePath());
|
Matcher matcher = pattern.matcher(event.getResourcePath());
|
||||||
if (!matcher.find())
|
if (!matcher.find())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// 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);
|
String userId = matcher.group(1);
|
||||||
LOGGER.infof("%s %s", userId, event.getOperationType());
|
handleUserEvent(event, userId);
|
||||||
switch (event.getOperationType()) {
|
|
||||||
case CREATE -> {
|
|
||||||
UserModel user = getUser(userId);
|
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.create(user));
|
|
||||||
user.getGroupsStream().forEach(group -> {
|
|
||||||
dispatcher.dispatchGroupModificationToAll(client -> client.replace(group));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
case UPDATE -> {
|
|
||||||
UserModel user = getUser(userId);
|
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
|
||||||
}
|
|
||||||
case DELETE -> dispatcher.dispatchUserModificationToAll(client -> client.delete(userId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case GROUP -> {
|
case GROUP -> {
|
||||||
String groupId = matcher.group(1);
|
String groupId = matcher.group(1);
|
||||||
LOGGER.infof("group %s %s", groupId, event.getOperationType());
|
handleGroupEvent(event, groupId);
|
||||||
switch (event.getOperationType()) {
|
|
||||||
case CREATE -> {
|
|
||||||
GroupModel group = getGroup(groupId);
|
|
||||||
dispatcher.dispatchGroupModificationToAll(client -> client.create(group));
|
|
||||||
}
|
|
||||||
case UPDATE -> {
|
|
||||||
GroupModel group = getGroup(groupId);
|
|
||||||
dispatcher.dispatchGroupModificationToAll(client -> client.replace(group));
|
|
||||||
}
|
|
||||||
case DELETE -> dispatcher.dispatchGroupModificationToAll(client -> client.delete(groupId));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
case GROUP_MEMBERSHIP -> {
|
case GROUP_MEMBERSHIP -> {
|
||||||
String userId = matcher.group(1);
|
String userId = matcher.group(1);
|
||||||
String groupId = matcher.group(2);
|
String groupId = matcher.group(2);
|
||||||
LOGGER.infof("%s %s from %s", event.getOperationType(), userId, groupId);
|
handleGroupMemberShipEvent(event, userId, groupId);
|
||||||
GroupModel group = getGroup(groupId);
|
|
||||||
group.setSingleAttribute("scim-dirty", BooleanUtils.TRUE);
|
|
||||||
UserModel user = getUser(userId);
|
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
|
||||||
}
|
}
|
||||||
case REALM_ROLE_MAPPING -> {
|
case REALM_ROLE_MAPPING -> {
|
||||||
String type = matcher.group(1);
|
String type = matcher.group(1);
|
||||||
String id = matcher.group(2);
|
String id = matcher.group(2);
|
||||||
LOGGER.infof("%s %s %s roles", event.getOperationType(), type, id);
|
handleRoleMappingEvent(event, type, id);
|
||||||
switch (type) {
|
}
|
||||||
case "users" -> {
|
default -> {
|
||||||
UserModel user = getUser(id);
|
// No other resource modification has to be propagated to SCIM Servers
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
|
||||||
}
|
|
||||||
case "groups" -> {
|
|
||||||
GroupModel group = getGroup(id);
|
|
||||||
session.users().getGroupMembersStream(session.getContext().getRealm(), group).forEach(user -> {
|
|
||||||
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleUserEvent(AdminEvent userEvent, String userId) {
|
||||||
|
LOGGER.infof("[SCIM] Propagate User %s - %s", userEvent.getOperationType(), userId);
|
||||||
|
switch (userEvent.getOperationType()) {
|
||||||
|
case CREATE -> {
|
||||||
|
UserModel user = getUser(userId);
|
||||||
|
dispatcher.dispatchUserModificationToAll(client -> client.create(user));
|
||||||
|
user.getGroupsStream().forEach(group ->
|
||||||
|
dispatcher.dispatchGroupModificationToAll(client -> client.replace(group)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
case UPDATE -> {
|
||||||
|
UserModel user = getUser(userId);
|
||||||
|
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
||||||
|
}
|
||||||
|
case DELETE -> dispatcher.dispatchUserModificationToAll(client -> client.delete(userId));
|
||||||
|
default -> {
|
||||||
|
// ACTION userEvent are not relevant, nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Propagating the given group-related event to SCIM servers.
|
||||||
|
*
|
||||||
|
* @param event the event to propagate
|
||||||
|
* @param groupId event target's id
|
||||||
|
*/
|
||||||
|
private void handleGroupEvent(AdminEvent event, String groupId) {
|
||||||
|
LOGGER.infof("[SCIM] Propagate Group %s - %s", event.getOperationType(), groupId);
|
||||||
|
switch (event.getOperationType()) {
|
||||||
|
case CREATE -> {
|
||||||
|
GroupModel group = getGroup(groupId);
|
||||||
|
dispatcher.dispatchGroupModificationToAll(client -> client.create(group));
|
||||||
|
}
|
||||||
|
case UPDATE -> {
|
||||||
|
GroupModel group = getGroup(groupId);
|
||||||
|
dispatcher.dispatchGroupModificationToAll(client -> client.replace(group));
|
||||||
|
}
|
||||||
|
case DELETE -> dispatcher.dispatchGroupModificationToAll(client -> client.delete(groupId));
|
||||||
|
default -> {
|
||||||
|
// ACTION event are not relevant, nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleGroupMemberShipEvent(AdminEvent groupMemberShipEvent, String userId, String groupId) {
|
||||||
|
LOGGER.infof("[SCIM] Propagate GroupMemberShip %s - User %s Group %s", groupMemberShipEvent.getOperationType(), userId, groupId);
|
||||||
|
GroupModel group = getGroup(groupId);
|
||||||
|
group.setSingleAttribute("scim-dirty", BooleanUtils.TRUE);
|
||||||
|
UserModel user = getUser(userId);
|
||||||
|
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRoleMappingEvent(AdminEvent roleMappingEvent, String type, String id) {
|
||||||
|
LOGGER.infof("[SCIM] Propagate RoleMapping %s - %s %s", roleMappingEvent.getOperationType(), type, id);
|
||||||
|
switch (type) {
|
||||||
|
case "users" -> {
|
||||||
|
UserModel user = getUser(id);
|
||||||
|
dispatcher.dispatchUserModificationToAll(client -> client.replace(user));
|
||||||
|
}
|
||||||
|
case "groups" -> {
|
||||||
|
GroupModel group = getGroup(id);
|
||||||
|
session.users()
|
||||||
|
.getGroupMembersStream(session.getContext().getRealm(), group)
|
||||||
|
.forEach(user ->
|
||||||
|
dispatcher.dispatchUserModificationToAll(client -> client.replace(user)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
// No other type is relevant for propagation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private UserModel getUser(String id) {
|
private UserModel getUser(String id) {
|
||||||
return session.users().getUserById(session.getContext().getRealm(), id);
|
return session.users().getUserById(session.getContext().getRealm(), id);
|
||||||
}
|
}
|
||||||
|
@ -136,4 +182,10 @@ public class ScimEventListenerProvider implements EventListenerProvider {
|
||||||
private GroupModel getGroup(String id) {
|
private GroupModel getGroup(String id) {
|
||||||
return session.groups().getGroupById(session.getContext().getRealm(), id);
|
return session.groups().getGroupById(session.getContext().getRealm(), id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// Nothing to close here
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,24 @@ public class ScimEventListenerProviderFactory implements EventListenerProviderFa
|
||||||
return new ScimEventListenerProvider(session);
|
return new ScimEventListenerProvider(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return "scim";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Scope config) {
|
public void init(Scope config) {
|
||||||
|
// Nothing to initialize
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
|
// Nothing to initialize
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
|
// Nothing to close
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return "scim";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue