From 9eaf39c6dcdcf226fdc52c3bf8460657ef19d8b1 Mon Sep 17 00:00:00 2001 From: Paul Pound Date: Fri, 27 Mar 2026 15:23:56 -0300 Subject: [PATCH] update the evergreen user lookup function --- config/schema/upei_roblib_ill.schema.yml | 6 + includes/utilities.inc | 137 ++++++++++++++++++----- src/Form/RoblibIllSettingsForm.php | 21 +++- 3 files changed, 133 insertions(+), 31 deletions(-) diff --git a/config/schema/upei_roblib_ill.schema.yml b/config/schema/upei_roblib_ill.schema.yml index 98affcc..ab0fc4c 100644 --- a/config/schema/upei_roblib_ill.schema.yml +++ b/config/schema/upei_roblib_ill.schema.yml @@ -29,3 +29,9 @@ upei_roblib_ill.settings: evergreen_api_url: type: string label: 'Evergreen API URL' + evergreen_username: + type: string + label: 'Evergreen Staff Username' + evergreen_password: + type: string + label: 'Evergreen Staff Password' diff --git a/includes/utilities.inc b/includes/utilities.inc index 50c2216..63715b1 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -94,68 +94,145 @@ call @phone. Note: if you are a student taking online courses only, please emai } /** - * Lookup the user in Evergreen and verify the surname matches. + * Authenticate against Evergreen and return an auth token. * - * @param string $campus_id - * The user's campus ID / barcode. + * @param string $base_url + * The base Evergreen URL. + * @param string $username + * The Evergreen staff username. + * @param string $password + * The Evergreen staff password. + * + * @return string|null + * The auth token, or NULL on failure. + */ +function upei_roblib_ill_evergreen_login($base_url, $username, $password) { + $gateway_url = rtrim($base_url, '/') . '/osrf-gateway-v1'; + $client = \Drupal::httpClient(); + + // Step 1: Get the seed/nonce for the username. + $seed_response = $client->get($gateway_url, [ + 'timeout' => 10, + 'query' => [ + 'service' => 'open-ils.auth', + 'method' => 'open-ils.auth.authenticate.init', + 'param' => '"' . $username . '"', + ], + ]); + $seed_data = json_decode((string) $seed_response->getBody(), TRUE); + $seed = $seed_data['payload'][0] ?? NULL; + if (empty($seed)) { + \Drupal::logger('upei_roblib_ill')->error('Evergreen auth init failed: no seed returned.'); + return NULL; + } + + // Step 2: Authenticate with the hashed password. + $hashed_password = md5($seed . md5($password)); + $auth_response = $client->get($gateway_url, [ + 'timeout' => 10, + 'query' => [ + 'service' => 'open-ils.auth', + 'method' => 'open-ils.auth.authenticate.complete', + 'param' => json_encode([ + 'username' => $username, + 'password' => $hashed_password, + 'type' => 'staff', + ]), + ], + ]); + $auth_data = json_decode((string) $auth_response->getBody(), TRUE); + $auth_result = $auth_data['payload'][0] ?? NULL; + + if (empty($auth_result) || ($auth_result['ilsevent'] ?? -1) != 0) { + $text_code = $auth_result['textcode'] ?? 'unknown'; + \Drupal::logger('upei_roblib_ill')->error('Evergreen auth failed: @code', [ + '@code' => $text_code, + ]); + return NULL; + } + + return $auth_result['payload']['authtoken'] ?? NULL; +} + +/** + * Lookup the user in Evergreen via the REST API and verify the surname matches. + * + * Authenticates with Evergreen using staff credentials, then retrieves the + * patron by user ID and compares the family_name field against the provided + * surname. + * + * @param string $user_id + * The Evergreen user ID. * @param string $surname * The surname to check against. * * @return bool * TRUE if the surname matches, FALSE otherwise. */ -function upei_roblib_ill_authenticate($campus_id, $surname): bool +function upei_roblib_ill_authenticate($user_id, $surname): bool { - if (empty($campus_id) || empty($surname)) { + if (empty($user_id) || empty($surname)) { return FALSE; } $config = \Drupal::config('upei_roblib_ill.settings'); $evergreen_api_url = $config->get('evergreen_api_url'); - if (empty($evergreen_api_url)) { - \Drupal::logger('upei_roblib_ill')->error('Evergreen API URL is not configured.'); + $eg_username = $config->get('evergreen_username'); + $eg_password = $config->get('evergreen_password'); + + if (empty($evergreen_api_url) || empty($eg_username) || empty($eg_password)) { + \Drupal::logger('upei_roblib_ill')->error('Evergreen API URL or credentials are not configured.'); return FALSE; } - // Construct the PatronAPI dump URL. - // Format: {evergreen_api_url}/api/patronapi/{campus_id}/dump - $url = rtrim($evergreen_api_url, '/') . '/api/patronapi/' . urlencode($campus_id) . '/dump'; - try { - $response = \Drupal::httpClient()->get($url, [ + // Authenticate to get an auth token. + $auth_token = upei_roblib_ill_evergreen_login($evergreen_api_url, $eg_username, $eg_password); + if (empty($auth_token)) { + return FALSE; + } + + // Retrieve the patron by user ID using the auth token. + $gateway_url = rtrim($evergreen_api_url, '/') . '/osrf-gateway-v1'; + $response = \Drupal::httpClient()->get($gateway_url, [ 'timeout' => 10, + 'query' => [ + 'service' => 'open-ils.actor', + 'method' => 'open-ils.actor.user.retrieve', + 'param' => ['"' . $auth_token . '"', '"' . $user_id . '"'], + ], ]); - $data = (string)$response->getBody(); + $data = json_decode((string) $response->getBody(), TRUE); - // The PatronAPI dump endpoint returns data in a plain text/HTML format - // looking like `PATRN NAME[p]=Smith, John`. - if (preg_match('/PATRN NAME\[p\]=([^\[<]+)/i', $data, $matches)) { - $patron_name = trim($matches[1]); + $patron = $data['payload'][0] ?? NULL; + if (empty($patron) || isset($patron['ilsevent'])) { + $evt = $patron['textcode'] ?? 'no data'; + \Drupal::logger('upei_roblib_ill')->warning('Evergreen REST API returned no patron for user ID @user_id: @evt', [ + '@user_id' => $user_id, + '@evt' => $evt, + ]); + return FALSE; + } - // Usually "Lastname, Firstname" - $name_parts = explode(',', $patron_name); - if (count($name_parts) > 0) { - $evergreen_surname = trim($name_parts[0]); + $evergreen_surname = $patron['family_name'] ?? ''; - // Case-insensitive comparison of the extracted surname and provided surname. - if (strcasecmp($evergreen_surname, trim($surname)) === 0) { - return TRUE; - } - } + // Case-insensitive comparison of the Evergreen surname and provided surname. + if (strcasecmp(trim($evergreen_surname), trim($surname)) === 0) { + return TRUE; } - // Log failure for debugging. - \Drupal::logger('upei_roblib_ill')->warning('Evergreen authentication failed for @campus_id. Expected surname: @surname', [ - '@campus_id' => $campus_id, + \Drupal::logger('upei_roblib_ill')->warning('Evergreen authentication failed for user ID @user_id. Expected surname: @surname, got: @evergreen_surname', [ + '@user_id' => $user_id, '@surname' => $surname, + '@evergreen_surname' => $evergreen_surname, ]); return FALSE; } catch (\Exception $e) { - \Drupal::logger('upei_roblib_ill')->error('Failed to connect to Evergreen API: @message', [ + \Drupal::logger('upei_roblib_ill')->error('Failed to connect to Evergreen REST API: @message', [ '@message' => $e->getMessage(), ]); return FALSE; diff --git a/src/Form/RoblibIllSettingsForm.php b/src/Form/RoblibIllSettingsForm.php index 0399742..a3d8524 100644 --- a/src/Form/RoblibIllSettingsForm.php +++ b/src/Form/RoblibIllSettingsForm.php @@ -73,11 +73,25 @@ class RoblibIllSettingsForm extends FormBase $form['evergreen_fieldset']['evergreen_api_url'] = [ '#type' => 'url', '#title' => t('Evergreen API URL'), - '#description' => t('The base URL of your Evergreen server (e.g. https://catalogue.example.com). Used for PatronAPI lookups.'), + '#description' => t('The base URL of your Evergreen server (e.g. https://catalogue.example.com). Used for REST API patron lookups.'), '#default_value' => $config->get('evergreen_api_url'), '#size' => 200, '#required' => TRUE, ]; + $form['evergreen_fieldset']['evergreen_username'] = [ + '#type' => 'textfield', + '#title' => t('Evergreen Staff Username'), + '#description' => t('A staff username for authenticating with the Evergreen REST API.'), + '#default_value' => $config->get('evergreen_username'), + '#size' => 200, + '#required' => TRUE, + ]; + $form['evergreen_fieldset']['evergreen_password'] = [ + '#type' => 'password', + '#title' => t('Evergreen Staff Password'), + '#description' => t('The staff password for the Evergreen REST API. Leave blank to keep the existing value.'), + '#size' => 200, + ]; // RapidILL settings. $form['rapid_ill_fieldset'] = [ @@ -143,6 +157,11 @@ class RoblibIllSettingsForm extends FormBase // Evergreen settings. $config->set('evergreen_api_url', rtrim($form_state->getValue('evergreen_api_url'), '/'))->save(); + $config->set('evergreen_username', $form_state->getValue('evergreen_username'))->save(); + $eg_password = $form_state->getValue('evergreen_password'); + if (!empty($eg_password)) { + $config->set('evergreen_password', $eg_password)->save(); + } // RapidILL settings. $config->set('rapid_ill_username', $form_state->getValue('rapid_ill_username'))->save();