Skip to content
Snippets Groups Projects
Commit 21926775 authored by Marco Villegas's avatar Marco Villegas Committed by Neil Drumm
Browse files

Issue #3470408: Add route to react on KC user deletes

parent 67bf44ce
Branches
No related tags found
3 merge requests!312Issue # 3494493: Documentation: Document Maintainer Widget,!299Remove heroes from components field. Update CTA section and add variants.,!279#3470408 Add route to process keycloak user delete event relay
......@@ -400,6 +400,13 @@ function drupalorg_menu() {
'type' => MENU_CALLBACK,
'file' => 'drupalorg.pages.inc',
];
$items['drupalorg-keycloak-event/%/%'] = [
'access callback' => 'drupalorg_keycloak_relay_event_access',
'page callback' => 'drupalorg_keycloak_event',
'page arguments' => [1, 2],
'type' => MENU_CALLBACK,
'file' => 'drupalorg.pages.inc',
];
// Queue in-place-update “quasi diff” generation.
$items['in-place-updates/%project/%drupalorg_in_place_update'] = [
......@@ -665,6 +672,22 @@ function drupalorg_gitlab_webhook_access() {
return TRUE;
}
/**
* Menu access callback, validate Keycloak event payload.
*/
function drupalorg_keycloak_relay_event_access() {
if (empty(variable_get('drupalorg_keycloak_event_token'))) {
return FALSE;
}
if (empty($_SERVER['HTTP_X_KEYCLOAK_TOKEN'])) {
return FALSE;
}
if ($_SERVER['HTTP_X_KEYCLOAK_TOKEN'] !== variable_get('drupalorg_keycloak_event_token')) {
return FALSE;
}
return TRUE;
}
/**
* Menu title callback. Be more welcoming than core.
*/
......@@ -6471,6 +6494,10 @@ function drupalorg_cron_queue_info() {
'worker callback' => 'drupalorg_keycloak_process',
'skip on cron' => TRUE,
],
'drupalorg_keycloak_event' => [
'worker callback' => 'drupalorg_keycloak_event_process',
'skip on cron' => TRUE,
],
'drupalorg_commits_comments' => [
'worker callback' => 'drupalorg_commits_comments_process',
'skip on cron' => TRUE,
......@@ -11095,3 +11122,42 @@ function drupalorg_get_gitlab_project_id($namespace, $name) {
return $ids[$key];
}
/**
* Queue worker callback for KC delete account event.
*
* @param array $item
* The queue item. An map with the following keys.
* - 'uid': Drupal user id.
* - 'keycloak_uuid': Keycloak user UUID.
*/
function drupalorg_keycloak_event_process($item) {
if (empty($item['keycloak_uuid']) || empty($item['uid'] || empty($item['event']))) {
watchdog('drupalorg_keycloak_event', 'Incomplete item on the queue: @item', [
'@item' => print_r($item, 1),
], WATCHDOG_ERROR);
return;
}
$keycloak_user = (new KeycloakIntegration())->getUser($item['keycloak_uuid']);
switch ($item['event']) {
case 'delete':
if (!is_null($keycloak_user)) {
watchdog('drupalorg_keycloak_event', 'Keycloak user still exists, skip deleting it: @item', [
'@item' => print_r($item, 1),
], WATCHDOG_ERROR);
return;
}
watchdog('drupalorg_keycloak_events_user_delete_process', 'Deleting drupal user uid "@uid" following a Keycloak user uuid "@uuid" delete', [
'@uid' => $item['uid'],
'@uuid' => $item['keycloak_uuid'],
], WATCHDOG_INFO);
user_delete($item['uid']);
return;
default:
watchdog('drupalorg_keycloak_event', 'Unrecognised event on item: @item', [
'@item' => print_r($item, 1),
], WATCHDOG_ERROR);
}
}
......@@ -1662,3 +1662,98 @@ function drupalorg_registration_validation_text_form_submit($form, &$form_state)
drupal_set_message(t('Support request emailed to <a href="mailto:help@drupal.org">help@drupal.org</a>.'));
}
/**
* Menu callback, handle Keycloak relayed event.
*
* @param string $event_trigger
* Trigger that produced the event.
* Either 'user' or 'admin'.
* @param string $event_type
* Type of sent on the payload.
*
* Payload is JSON with the event data.
* E.g.
* @code
* {
* "event_trigger": "user",
* "event": {
* "id": "0c9061c8-8656-4b73-9302-9cf52e94922b",
* "time": "1725046809362",
* "type": "CODE_TO_TOKEN",
* "realm_id": "Main",
* "client_id": "account-console",
* "user_id": "80cefc10-1d71-4b5b-b079-39345ab5e65d",
* "session_id": "9ecd59b9-7c17-4fc2-adc6-422f4d2bdace",
* "ip_address": "192.168.1.101",
* "error": ""
* }
* }
* @endcode
*/
function drupalorg_keycloak_event($event_trigger, $event_type) {
if (empty($event_trigger) || empty($event_type)) {
// Missing arguments.
watchdog('drupalorg_keycloak_event', 'Keycloak sent an event with missing arguments, event trigger was "@trigger", and event type was "@type".', [
'@trigger' => $event_trigger,
'@type' => $event_type,
], WATCHDOG_ERROR);
drupal_add_http_header('Status', '400 Bad Request');
print '{"error": "Empty event trigger or event type in request path"}';
drupal_page_footer();
exit();
}
if ($event_trigger !== 'user') {
// Only support KC user events for now.
drupal_add_http_header('Status', '204 No Content');
drupal_page_footer();
exit();
}
$user_event = json_decode(file_get_contents('php://input'), true);
switch ($event_type) {
case 'DELETE_ACCOUNT':
_drupalorg_keycloak_event_handle_delete_account($user_event);
break;
default:
// Unsupported event type.
drupal_add_http_header('Status', '204 No Content');
}
drupal_add_http_header('Content-Type', 'text/plain');
echo '👋';
drupal_page_footer();
exit();
}
/**
* Handles KC user delete event.
*
* @param array $user_event
* Parsed JSON payload on POST. See payload example on
* drupalorg_keycloak_event().
*/
function _drupalorg_keycloak_event_handle_delete_account($user_event) {
if (empty($user_event['event']['user_id'])) {
// Cannot continue without a Keycloak UUID.
watchdog('drupalorg_keycloak_event', 'Keycloak sent a user event without event.user_id set: @payload', [
'@payload' => print_r($user_event, 1),
], WATCHDOG_ERROR);
return;
}
$event = $user_event['event'];
$uid = db_query("SELECT uid FROM {authmap} WHERE module = 'openid_connect_keycloak' AND authname = :authname", [
':authname' => $event['user_id'],
])->fetchField();
if (empty($uid)) {
// Cannot find a related user id.
watchdog('drupalorg_keycloak_event', 'Provided Keycloak UUID "@uuid" does not have a related entry on authmap: @payload', [
'@uuid' => print_r($event['user_id'], TRUE),
'@payload' => print_r($event, TRUE),
], WATCHDOG_ERROR);
return;
}
DrupalQueue::get('drupalorg_keycloak_event')->createItem([
'uid' => $uid,
'keycloak_uuid' => $event['user_id'],
'event' => 'delete',
]);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment