From 1eb425c4ffef571180e6d03dd6f3d4c3bdef30f8 Mon Sep 17 00:00:00 2001 From: Danny Lamb Date: Wed, 3 Jun 2026 09:26:54 -0300 Subject: [PATCH] Skip OAI list records the requester cannot view ListRecords and ListIdentifiers iterate cached records and call getHeaderById()/getRecordById() without checking that the entity loaded. When a cached record references an entity the current (anonymous) user cannot view -- e.g. published content protected by node access (Group, embargo, IP restrictions) -- or one that has since been deleted, loadEntity() sets $this->entity to FALSE and the subsequent $this->entity->hasField('changed') call throws "Call to a member function hasField() on false", white-screening the entire harvest. Only the list verbs are affected; GetRecord already handles a FALSE entity gracefully (returns idDoesNotExist). Guard both loops with `if (empty($this->entity)) { continue; }` so inaccessible or missing records are skipped rather than fataling, mirroring the FALSE handling getRecord() already performs. --- src/Plugin/rest/resource/OaiPmh.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/Plugin/rest/resource/OaiPmh.php b/src/Plugin/rest/resource/OaiPmh.php index 8e13b81..5e570bc 100644 --- a/src/Plugin/rest/resource/OaiPmh.php +++ b/src/Plugin/rest/resource/OaiPmh.php @@ -469,6 +469,14 @@ protected function listIdentifiers() { $this->oaiEntity = $entity; $identifier = $this->buildIdentifier($entity); $this->loadEntity($identifier, TRUE); + // The cache table can reference an entity the current (anonymous) user + // cannot view, or one that has since been deleted. In both cases + // loadEntity() sets $this->entity to FALSE. Skip the record instead of + // calling getHeaderById() on FALSE, which fatals. This mirrors the FALSE + // handling getRecord() already performs for the GetRecord verb. + if (empty($this->entity)) { + continue; + } $this->response[$this->verb]['header'][] = $this->getHeaderById($identifier); } if (empty($this->response[$this->verb]['header'])) { @@ -486,6 +494,14 @@ protected function listRecords() { $this->oaiEntity = $entity; $identifier = $this->buildIdentifier($entity); $this->loadEntity($identifier, TRUE); + // The cache table can reference an entity the current (anonymous) user + // cannot view, or one that has since been deleted. In both cases + // loadEntity() sets $this->entity to FALSE. Skip the record instead of + // calling getRecordById() on FALSE, which fatals. This mirrors the FALSE + // handling getRecord() already performs for the GetRecord verb. + if (empty($this->entity)) { + continue; + } $this->response[$this->verb]['record'][] = $this->getRecordById($identifier); } if (empty($this->response[$this->verb]['record'])) {