diff --git a/src/Form/TripleStoreIndexerConfigForm.php b/src/Form/TripleStoreIndexerConfigForm.php index be068a5..af4052c 100644 --- a/src/Form/TripleStoreIndexerConfigForm.php +++ b/src/Form/TripleStoreIndexerConfigForm.php @@ -2,10 +2,11 @@ namespace Drupal\triplestore_indexer\Form; +use Drupal\advancedqueue\Entity\Queue; use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Form\ConfigFormBase; use Drupal\Core\Form\FormStateInterface; -use Drupal\advancedqueue\Entity\Queue; +use Drupal\Core\Url; /** * Class TripleStoreIndexerConfigForm definition. @@ -66,6 +67,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#options' => [ '-1' => 'None', 'digest' => 'Basic Authentication', + 'jwt' => 'JWT Authentication', ], '#ajax' => [ 'wrapper' => 'questions-fieldset-wrapper', @@ -109,6 +111,24 @@ public function buildForm(array $form, FormStateInterface $form_state) { '#description' => $this->t('To reset the password, change Method of authentication to None first.'), ]; break; + + case 'jwt': + $jwt_key = \Drupal::config('jwt.config')->get('key_id'); + $key_repository = \Drupal::service('key.repository'); + $key = $key_repository->getKey($jwt_key); + if (\Drupal::hasService('jwt.authentication.jwt') && $key != NULL) { + $form['container']['triplestore-server-config']['auth-config']['jwt_available'] = [ + '#markup' => $this->t('JWT service is available.'), + ]; + } + else { + $url = Url::fromRoute('jwt.jwt_config_form')->toString(); + $form['container']['triplestore-server-config']['auth-config']['jwt_error'] = [ + '#markup' => $this->t('JWT service is not available. Please ensure you have enabled the JWT module and configured the key. Click here to configure JWT.', ['@url' => $url]), + ]; + } + break; + default: $form['container']['triplestore-server-config']['auth-config']['question'] = [ '#markup' => $this->t('None.'), @@ -218,6 +238,20 @@ public function validateForm(array &$form, FormStateInterface $form_state) { $form_state->setErrorByName("advancedqueue_id", new FormattableMarkup('This queue\'s machine name "' . $form_state->getValues()['advancedqueue_id'] . '" is not valid, please verify it by clicking here.', [])); } + + // Validate JWT availability. + if ($form_state->getValues()['select-auth-method'] === 'jwt') { + // Use key_id from jwt module configuration to check if it is available. + $jwt_key = \Drupal::config('jwt.config')->get('key_id'); + $key_repository = \Drupal::service('key.repository'); + // Check if the key is in key repository. + $key = $key_repository->getKey($jwt_key); + if (!\Drupal::hasService('jwt.authentication.jwt') || $key == NULL) { + $url = Url::fromRoute('jwt.jwt_config_form')->toString(); + $form_state->setErrorByName("select-auth-method", + new FormattableMarkup('JWT service is not available. Please ensure you have enabled the JWT module and configured the key. Click here to configure JWT.', ['@url' => $url])); + } + } } /** @@ -240,9 +274,18 @@ public function submitForm(array &$form, FormStateInterface $form_state) { $configFactory->set('admin_password', base64_encode($form_state->getValues()['admin_password'])); } break; + + case 'jwt': + if (\Drupal::hasService('jwt.authentication.jwt')) { + $jwt_token = \Drupal::service('jwt.authentication.jwt')->generateToken(); + $configFactory->set('jwt_token', $jwt_token); + } + break; + default: $configFactory->set('admin_username', NULL); $configFactory->set('admin_password', NULL); + $configFactory->set('jwt_token', NULL); break; } @@ -267,4 +310,5 @@ public function promptAuthCallback(array $form, FormStateInterface $form_state) public function promptOpCallback(array $form, FormStateInterface $form_state) { return $form['container']['triplestore-server-config']['op-config']; } + } diff --git a/src/IndexingService.php b/src/IndexingService.php index 0d06dfb..bd37f77 100644 --- a/src/IndexingService.php +++ b/src/IndexingService.php @@ -18,14 +18,24 @@ public function serialization(array $payload) { // Make GET request to any content with _format=jsonld. $config = \Drupal::config('triplestore_indexer.settings'); $uri = "$base_url/$type/$nid" . '?_format=jsonld'; - - if ($config->get("method_of_auth") == 'digest') { - $headers = [ - 'auth' => [$config->get('admin_username'),base64_decode($config->get('admin_password'))] - ]; - $request = \Drupal::httpClient()->get($uri, $headers); - } else { - $request = \Drupal::httpClient()->get($uri); + switch ($config->get("method_of_auth")) { + case 'digest': + $headers = [ + 'auth' => [$config->get('admin_username'), base64_decode($config->get('admin_password'))], + ]; + $request = \Drupal::httpClient()->get($uri, $headers); + break; + + case 'jwt': + $headers = [ + 'Authorization' => 'Bearer ' . $config->get('jwt_token'), + ]; + $request = \Drupal::httpClient()->get($uri, ['headers' => $headers]); + break; + + default: + $request = \Drupal::httpClient()->get($uri); + break; } $graph = $request->getBody(); return $graph; @@ -42,18 +52,30 @@ public function getOtherConmponentAssocNode(array $payload) { // Make GET request to any content with _format=jsonld. $uri = "$base_url/$type/$nid" . '?_format=jsonld'; - // add header if there is authentication is needed + // Add header if there is authentication is needed. $config = \Drupal::config('triplestore_indexer.settings'); - if ($config->get("method_of_auth") == 'digest') { - $headers = [ - 'auth' => [$config->get('admin_username'),base64_decode($config->get('admin_password'))] - ]; - $request = \Drupal::httpClient()->get($uri, $headers); - } else { - $request = \Drupal::httpClient()->get($uri); + + switch ($config->get("method_of_auth")) { + case 'digest': + $headers = [ + 'auth' => [$config->get('admin_username'), base64_decode($config->get('admin_password'))], + ]; + $request = \Drupal::httpClient()->get($uri, $headers); + break; + + case 'jwt': + $headers = [ + 'Authorization' => 'Bearer ' . $config->get('jwt_token'), + ]; + $request = \Drupal::httpClient()->get($uri, ['headers' => $headers]); + break; + + default: + $request = \Drupal::httpClient()->get($uri); + break; } - // get response body + // Get response body. $graph = ((array) json_decode($request->getBody()))['@graph']; $others = []; for ($i = 1; $i < count($graph); $i++) { diff --git a/triplestore_indexer.module b/triplestore_indexer.module index c40b735..6620659 100644 --- a/triplestore_indexer.module +++ b/triplestore_indexer.module @@ -5,12 +5,15 @@ * Contains triplestore_indexer.module. */ -use Drupal\taxonomy\Entity\Term; -use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Routing\RouteMatchInterface; use Drupal\advancedqueue\Entity\Queue; use Drupal\advancedqueue\Job; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Logger\RfcLogLevel; +use Drupal\Core\Routing\RouteMatchInterface; +use Drupal\Core\Url; +use Drupal\taxonomy\Entity\Term; +use GuzzleHttp\Exception\ClientException; /** * Implements hook_help(). @@ -46,6 +49,70 @@ function triplestore_indexer_entity_insert(EntityInterface $entity) { drupal_register_shutdown_function('execute_indexing_action', 'index_node_to_triplestore_advancedqueue', $entity); } +/** + * Implements hook_form_alter(). + */ +function triplestore_indexer_form_alter(&$form, FormStateInterface $form_state, $form_id) { + // Matching the form id to node_[content_type]_delete_form. + // Adding validator only to the content type that is indexed. + if (preg_match('/^node_(.*)_delete_form$/', $form_id, $matches)) { + $originalId = $matches[1]; + $config = \Drupal::config('triplestore_indexer.settings'); + $indexed_content = $config->get('content_type_to_index'); + if (is_array($config->get('content_type_to_index')) && in_array($originalId, $indexed_content)) { + $form['#validate'][] = 'triplestore_indexer_node_delete_form_validate'; + } + } +} + +/** + * Validation handler for node delete forms. + */ +function triplestore_indexer_node_delete_form_validate($form, FormStateInterface $form_state) { + global $base_url; + $node = $form_state->getFormObject()->getEntity(); + $url = $node->toUrl()->toString(); + $uri = $base_url . $url . '?_format=jsonld'; + $config = \Drupal::config('triplestore_indexer.settings'); + try { + switch ($config->get("method_of_auth")) { + case 'digest': + $headers = [ + 'auth' => [$config->get('admin_username'), base64_decode($config->get('admin_password'))], + ]; + \Drupal::httpClient()->get($uri, $headers); + break; + + case 'jwt': + $headers = [ + 'Authorization' => 'Bearer ' . $config->get('jwt_token'), + ]; + \Drupal::httpClient()->get($uri, ['headers' => $headers]); + break; + + default: + \Drupal::httpClient()->get($uri); + break; + } + } + catch (Exception $e) { + if ($e instanceof ClientException && $e->getCode() > 400 && $e->getCode() < 500) { + // Handling 4XX errors. + $triplestore_url = Url::fromRoute('triplestore_indexer.triplestore_indexer_config_form')->toString(); + $form_state->setErrorByName('delete', t('Access Control is in place for this item. Click here to add authentication to Triplestore Indexer in order to proceed deletion.', ['@url' => $triplestore_url])); + } + elseif (str_contains($e->getResponse()->getBody()->getContents(), 'getKey()')) { + // Handling JWT key missing. + $jwt_url = Url::fromRoute('jwt.jwt_config_form')->toString(); + $form_state->setErrorByName('delete', t('An error occurred: Your JWT Authentication Configurations are invalid! Click Here to configure them.', ['@url' => $jwt_url])); + } + else { + // General error. + $form_state->setErrorByName('delete', t('An error occurred: @error', ['@error' => $e->getMessage()])); + } + } +} + /** * Implements hook_entity_update(). */ @@ -57,11 +124,11 @@ function triplestore_indexer_entity_update(EntityInterface $entity) { * Implements hook_entity_delete(). */ function triplestore_indexer_entity_predelete(EntityInterface $entity) { - // both failed + // Both failed. execute_indexing_action('delete_node_in_triplestore_advancedqueue', $entity); - // delete content work, but delete indexed content in blazegraph works - //drupal_register_shutdown_function('execute_indexing_action', 'delete_node_in_triplestore_advancedqueue', $entity); + // Delete content work, but delete indexed content in blazegraph works. + // drupal_register_shutdown_function('execute_indexing_action', 'delete_node_in_triplestore_advancedqueue', $entity); } /**