From 4ee3a38952a948bf08954b4d9870f8dbc53f93ac Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 10 Apr 2023 22:17:40 -0700 Subject: [PATCH] AuthxCredential.validate - Multiple changes * Fix crash for undefined reference to `AuthenticatorTarget` * This is a private class. * Expect a credential instead of bare token * Fix types on setter/getter * Accept optional `$flow`. Make default visible. --- .../Api4/Action/AuthxCredential/Validate.php | 44 ++++++++++--------- ext/authx/Civi/Authx/Authenticator.php | 29 ++++++++++++ 2 files changed, 52 insertions(+), 21 deletions(-) diff --git a/ext/authx/Civi/Api4/Action/AuthxCredential/Validate.php b/ext/authx/Civi/Api4/Action/AuthxCredential/Validate.php index 528baca69f..55d50245b7 100644 --- a/ext/authx/Civi/Api4/Action/AuthxCredential/Validate.php +++ b/ext/authx/Civi/Api4/Action/AuthxCredential/Validate.php @@ -13,43 +13,45 @@ namespace Civi\Api4\Action\AuthxCredential; use Civi\Api4\Generic\Result; -use Civi\Authx\AuthxException; -use Civi\Authx\AuthenticatorTarget; -use Civi\Authx\CheckCredentialEvent; /** - * Validate that a JWT is still valid and can be used in CiviCRM. + * Validate that a credential is still valid and can be used in CiviCRM. * - * @method int getToken() Get Token to validate (required) - * @method setToken(string $token) Get contact ID param (required) + * @method string getCred() Get Token to validate (required) + * @method Validate setCred(string $token) Get contact ID param (required) */ class Validate extends \Civi\Api4\Generic\AbstractAction { + + /** + * Identify the login-flow. Used for policy enforcement. + * + * @var string + */ + protected $flow = 'script'; + /** - * Token to validate + * Credential to validate * * @var string + * Ex: 'Bearer ABCD1234' * @required */ - protected $token; + protected $cred; /** * @param \Civi\Api4\Generic\Result $result + * @throws \Civi\Authx\AuthxException */ public function _run(Result $result) { - $tgt = AuthenticatorTarget::create([ - 'flow' => 'script', - 'cred' => 'Bearer ' . $this->token, - 'siteKey' => NULL, + $details = [ + 'flow' => $this->flow, + 'cred' => $this->cred, + 'siteKey' => NULL, /* Old school. Hopefully, we don't need to expose this. */ 'useSession' => FALSE, - ]); - $checkEvent = new CheckCredentialEvent($tgt->cred); - \Civi::dispatcher()->dispatch('civi.authx.checkCredential', $checkEvent); - - if ($checkEvent->getRejection()) { - throw new AuthxException($checkEvent->getRejection()); - } - - $result[] = $checkEvent->getPrincipal(); + ]; + $auth = new \Civi\Authx\Authenticator(); + $auth->setRejectMode('exception'); + $result[] = $auth->validate($details); } } diff --git a/ext/authx/Civi/Authx/Authenticator.php b/ext/authx/Civi/Authx/Authenticator.php index d38bc985dd..b30fbf2ab3 100644 --- a/ext/authx/Civi/Authx/Authenticator.php +++ b/ext/authx/Civi/Authx/Authenticator.php @@ -142,6 +142,35 @@ class Authenticator extends AutoService implements HookInterface { return TRUE; } + /** + * Determine whether credentials are valid. This is similar to `auth()` + * but stops short of performing an actual login. + * + * @param array $details + * @return array{flow: string, credType: string, jwt: ?array, useSession: bool, userId: ?int, contactId: ?int} + * Description of the validated principal (redacted). + * @throws \Civi\Authx\AuthxException + */ + public function validate(array $details): array { + if (!isset($details['flow'])) { + $this->reject('Authentication logic error: Must specify "flow".'); + } + + $tgt = AuthenticatorTarget::create([ + 'flow' => $details['flow'], + 'cred' => $details['cred'] ?? NULL, + 'siteKey' => $details['siteKey'] ?? NULL, + 'useSession' => $details['useSession'] ?? FALSE, + ]); + + if ($principal = $this->checkCredential($tgt)) { + $tgt->setPrincipal($principal); + } + + $this->checkPolicy($tgt); + return $tgt->createRedacted(); + } + /** * Assess the credential ($tgt->cred) and determine the matching principal. * -- 2.25.1