settings = $settings; $this->client_wrapper = $client_wrapper; $this->logger = $logger; } public static function register(OpenID_Connect_Generic_Option_Settings $settings, OpenID_Connect_Generic_Client_Wrapper $client_wrapper, OpenID_Connect_Generic_Option_Logger $logger) { $webhook = new OpenID_Connect_Generic_Webhook($settings, $client_wrapper, $logger); add_action('rest_api_init', [$webhook, 'register_rest_api']); return $webhook; } public function register_rest_api() { register_rest_route('openid-connect/v1', '/webhook/keycloak', [ [ 'methods' => ['GET', 'POST'], 'callback' => [$this, 'webhook_keycloak'], ], 'schema' => [$this, 'webhook_keycloak_schema'], ]); } public function webhook_keycloak(WP_REST_Request $request) { if (!$this->settings->enable_webhook) { $this->logger->log('Webhook is disabled in setting.', 'webhook'); return rest_ensure_response([ 'status' => -1, 'code' => 'ERR::WEBHOOK_DISABLED', 'error' => 'Webhook is disabled in setting.' ]); } $query = $request->get_query_params(); if (!empty($this->settings->webhook_key) && $query['key'] != $this->settings->webhook_key) { $this->logger->log('Webhook key is incorrect.', 'webhook'); return rest_ensure_response([ 'status' => -1, 'code' => 'ERR::WEBHOOK_KEY_INCORRECT', 'error' => 'Webhook key is incorrect.' ]); } $event = $request->get_json_params(); if (is_null($event)) { $this->logger->log('Event body is empty.', 'webhook'); return rest_ensure_response([ 'status' => -1, 'code' => 'ERR::EMPTY_EVENT_BODY', 'error' => 'Event body is empty.', ]); } if ($event['realmId'] != $this->settings->realm) { $this->logger->log("Realm id mismatch: {$event['realmId']}", 'webhook'); return rest_ensure_response([ 'status' => 0, 'warning' => "Realm id mismatch: {$event['realmId']}", ]); } if (!in_array($event['type'], ['UPDATE_PROFILE', 'UPDATE_EMAIL'])) { return rest_ensure_response([ 'status' => 0, 'warning' => 'Event type ignored', ]); } $subject = $event['userId']; $user = $this->client_wrapper->get_user_by_identity($subject); if (!$user) { $this->logger->log("Cannot find user for subject: {$subject}", 'webhook'); return rest_ensure_response([ 'status' => 0, 'warning' => "Cannot find user for subject: {$subject}", ]); } if (isset($event['userInfo'])) { // Update user profile $this->client_wrapper->update_user_profile($user, $event['userInfo']); $this->logger->log("Updated user profile: {$user->user_login}", 'webhook'); } return rest_ensure_response([ 'status' => 1 ]); } public function webhook_keycloak_schema() { return [ '$schema' => 'http://json-schema.org/draft-04/schema#', // The title property marks the identity of the resource. 'title' => 'keycloak-webhook', 'type' => 'object', 'required' => ['type', 'realmId', 'userId', 'userInfo'], 'properties' => [ 'type' => [ 'description' => esc_html__('Event Type', 'daggerhart-openid-connect-generic'), 'type' => 'string', ], 'realmId' => [ 'description' => esc_html__('IDP Realm ID', 'daggerhart-openid-connect-generic'), 'type' => 'string', ], 'userId' => [ 'description' => esc_html__('IDP User ID', 'daggerhart-openid-connect-generic'), 'type' => 'string', ], 'userInfo' => [ 'description' => esc_html__('IDP User Info', 'daggerhart-openid-connect-generic'), 'type' => 'object', ], ], ]; } }