diff --git a/app/plugins/CoreServer/src/Model/Table/Oauth2ServersTable.php b/app/plugins/CoreServer/src/Model/Table/Oauth2ServersTable.php index d21953283..ffc9113e3 100644 --- a/app/plugins/CoreServer/src/Model/Table/Oauth2ServersTable.php +++ b/app/plugins/CoreServer/src/Model/Table/Oauth2ServersTable.php @@ -252,7 +252,7 @@ public function validationDefault(Validator $validator): Validator { 'provider' => 'table' ] ]); - $validator->allowEmptyString('scope'); + $validator->notEmptyString('scope'); $validator->add('refresh_token', [ 'content' => [ diff --git a/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php b/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php index 029ab4519..7630cd94b 100644 --- a/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php +++ b/app/plugins/OrcidSource/src/Model/Table/OrcidSourcesTable.php @@ -30,12 +30,12 @@ namespace OrcidSource\Model\Table; use App\Model\Entity\ExternalIdentitySource; +use Cake\Http\Client; use Cake\ORM\Table; use Cake\ORM\TableRegistry; use Cake\Routing\Router; use Cake\Validation\Validator; use OrcidSource\Lib\Enum\{OrcidSourceApiEnum, OrcidSourceTierEnum}; -use \App\Lib\Enum\EduPersonAffiliationEnum; use \App\Lib\Enum\HttpStatusCodesEnum; use \OrcidSource\Model\Entity\OrcidSource; @@ -284,7 +284,7 @@ protected function resultToEntityData( $eidata['names'][] = $name; - foreach($result['emails']["email"] as $m) { + foreach($result['emails']['email'] as $m) { $eidata['email_addresses'][] = [ 'mail' => $m['email'], 'type' => $this->EmailAddressTypes->getTypeLabel($OrcidSource->email_address_type_id), @@ -342,6 +342,10 @@ public function retrieve( /** * Search the External Identity Source. + * The ORCID search will be triggered by the CO/Platform Admin. As a result, we want to use a privileged access key + * i.e. the one the CO Admin got in Oauth2Server setup page + * + * refrence: https://info.orcid.org/documentation/api-tutorials/api-tutorial-searching-the-orcid-registry/ * * @param ExternalIdentitySource $source EIS Entity with instantiated plugin configuration * @param array $searchAttrs Array of search attributes and values, as configured by searchAttributes() @@ -364,22 +368,19 @@ public function search( // We just let search exceptions pop up the stack - $this->orcidConnect($source); + $this->httpClient = $this->orcidConnect($source); $records = $this->orcidRequest('/v3.0/search/', $searchAttrs); - // We can control pagination with query params, but the OIS search capability - // doesn't currently understand pagination. - - if(isset($records->{'num-found'}) && $records->{'num-found'} > 0) { - foreach($records->result as $rec) { - if(!empty($rec->{'orcid-identifier'}->{'path'})) { - $orcid = $rec->{'orcid-identifier'}->{'path'}; + if(isset($records['num-found']) && $records['num-found'] > 0) { + foreach($records['result'] as $rec) { + if(!empty($rec['orcid-identifier']['path'])) { + $orcid = $rec['orcid-identifier']['path']; $orcidbio = $this->orcidRequest('/v3.0/' . $orcid . '/person'); if(!empty($orcidbio)) { -// $ret[ $orcid ] = $this->resultToOrgIdentity($orcid, $orcidbio); + $ret[ $orcid ] = $this->resultToEntityData($source->orcid_source, $orcidbio); } } } @@ -396,11 +397,7 @@ public function search( */ public function searchableAttributes(): array { - // By default, ORCID uses a free form search. It is possible to search on - // specific fields (eg: email), though for the initial implementation we - // won't support that. return [ - // XXX This really isn't the right language key, we want an fd.* 'q' => __d('operation', 'search') ]; } @@ -419,11 +416,17 @@ public function searchableAttributes(): array { */ public function orcidRequest(string $urlPath, array $data=[], string $action="get"): array { + // Get the user access_token. If none is provided, then throw an exception + $accessToken = match(true) { + $this->orcidToken?->access_token !== null => $this->orcidTokensTable->getUnencrypted($this->orcidToken->access_token), + $this->orcidSource?->server?->oauth2_server?->access_token !== null => $this->orcidSource->server->oauth2_server->access_token, + default => throw new \InvalidArgumentException(__d('orcid_source', 'error.token.none')) + }; + $options = [ 'headers' => [ 'Accept' => 'application/json', - 'Authorization' => 'Bearer ' . ($this->orcidTokensTable->getUnencrypted($this->orcidToken->access_token) - ?? $this->orcidSource->server->oauth2_server->access_token), + 'Authorization' => 'Bearer ' . $accessToken, 'Content-Type' => 'application/orcid+json' ] ]; @@ -484,15 +487,14 @@ public function orcidUrl(string $api=OrcidSourceApiEnum::PUBLIC, string $tier=Or /** * Establish connection to ORCID API by configuring the HTTP client with appropriate credentials. * - * @param int $exterrnalIdentitySourceId The external identity source ID to connect to - * @param string $orcidIdentifier The ORCID identifier to use for authentication - * @return \Cake\Http\Client Configured HTTP client for ORCID API requests - * @throws \InvalidArgumentException If OAuth2 server configuration or access token not found + * @param ExternalIdentitySource $exterrnalIdentitySource + * @param string|null $orcidIdentifier The ORCID identifier to use for authentication + * @return Client Configured HTTP client for ORCID API requests * @since COmanage Registry v5.2.0 */ protected function orcidConnect( \App\Model\Entity\ExternalIdentitySource $exterrnalIdentitySource, - string $orcidIdentifier + ?string $orcidIdentifier = null ): \Cake\Http\Client { $this->orcidSource = $this->find() ->contain([ @@ -521,16 +523,19 @@ protected function orcidConnect( throw new \InvalidArgumentException(__d('error', 'notfound', [__d('core_server', 'controller.Oauth2Servers')])); } - $this->orcidToken = $this->orcidTokensTable - ->find() - ->where([ - 'orcid_source_id' => $this->orcidSource->id, - 'orcid_identifier' => $orcidIdentifier, - ]) - ->first(); - - if (empty($this->orcidToken->access_token)) { - throw new \InvalidArgumentException(__d('orcid_source', 'error.token.none')); + // Since this is null, we will use the master access token stored in Oauth2Server Configuration + if ($orcidIdentifier !== null) { + $this->orcidToken = $this->orcidTokensTable + ->find() + ->where([ + 'orcid_source_id' => $this->orcidSource->id, + 'orcid_identifier' => $orcidIdentifier, + ]) + ->first(); + + if (empty($this->orcidToken->access_token)) { + throw new \InvalidArgumentException(__d('orcid_source', 'error.token.none')); + } } return $this->oauth2ServersTable->createHttpClient($this->orcidSource->server->oauth2_server->id);