From 2548f396efe772400ad2def70a5c994845ec487b Mon Sep 17 00:00:00 2001 From: Yuri Kuznetsov Date: Fri, 28 Jun 2024 11:09:53 +0300 Subject: [PATCH] ref --- .../Core/ExternalAccount/ClientManager.php | 132 ++++++++---------- .../Clients/OAuth2Abstract.php | 2 +- application/Espo/Entities/ExternalAccount.php | 46 ++++++ application/Espo/Entities/Integration.php | 5 + 4 files changed, 111 insertions(+), 74 deletions(-) diff --git a/application/Espo/Core/ExternalAccount/ClientManager.php b/application/Espo/Core/ExternalAccount/ClientManager.php index aaa9720514..75a56e9794 100644 --- a/application/Espo/Core/ExternalAccount/ClientManager.php +++ b/application/Espo/Core/ExternalAccount/ClientManager.php @@ -39,12 +39,11 @@ use Espo\Core\InjectableFactory; use Espo\Core\ORM\Repository\Option\SaveOption; use Espo\Core\Utils\Config; use Espo\Core\Utils\Metadata; -use Espo\ORM\Entity; use RuntimeException; class ClientManager { - /** @var array> */ + /** @var array & array{externalAccountEntity: ExternalAccount})> */ protected $clientMap = []; public function __construct( @@ -63,44 +62,41 @@ class ClientManager * } $data * @throws Error */ - public function storeAccessToken(string $hash, array $data): void + public function storeAccessToken(object $client, array $data): void { - if ( - empty($this->clientMap[$hash]) || - empty($this->clientMap[$hash]['externalAccountEntity']) - ) { + try { + $account = $this->getClientRecord($client); + } + catch (Error) { + // @todo Revise. return; } - /** @var ExternalAccount $account */ - $account = $this->clientMap[$hash]['externalAccountEntity']; - - $account->set('accessToken', $data['accessToken']); - $account->set('tokenType', $data['tokenType']); - $account->set('expiresAt', $data['expiresAt'] ?? null); + $account->setAccessToken($data['accessToken']); + $account->setTokenType($data['tokenType']); + $account->setExpiresAt($data['expiresAt'] ?? null); if ($data['refreshToken'] ?? null) { - $account->set('refreshToken', $data['refreshToken']); + $account->setRefreshToken($data['refreshToken']); } - // @todo Revise. Use refreshEntity? - + /** @var ?ExternalAccount $account */ $account = $this->entityManager->getEntityById(ExternalAccount::ENTITY_TYPE, $account->getId()); if (!$account) { throw new Error("External Account: Account removed."); } - if (!$account->get('enabled')) { + if (!$account->isEnabled()) { throw new Error("External Account: Account disabled."); } - $account->set('accessToken', $data['accessToken']); - $account->set('tokenType', $data['tokenType']); - $account->set('expiresAt', $data['expiresAt'] ?? null); + $account->setAccessToken($data['accessToken']); + $account->setTokenType($data['tokenType']); + $account->setExpiresAt($data['expiresAt'] ?? null); if ($data['refreshToken'] ?? null) { - $account->set('refreshToken', $data['refreshToken'] ?? null); + $account->setRefreshToken($data['refreshToken'] ?? null); } $this->entityManager->saveEntity($account, [ @@ -133,19 +129,14 @@ class ClientManager /** @var ?Integration $integrationEntity */ $integrationEntity = $this->entityManager->getEntityById(Integration::ENTITY_TYPE, $integration); - /** @var ?ExternalAccount $externalAccountEntity */ - $externalAccountEntity = $this->entityManager - ->getEntityById(ExternalAccount::ENTITY_TYPE, "{$integration}__$userId"); + /** @var ?ExternalAccount $account */ + $account = $this->entityManager->getEntityById(ExternalAccount::ENTITY_TYPE, "{$integration}__$userId"); - if (!$externalAccountEntity) { + if (!$account) { throw new Error("External Account $integration not found for $userId."); } - if ( - !$integrationEntity || - !$integrationEntity->get('enabled') || - !$externalAccountEntity->get('enabled') - ) { + if (!$integrationEntity || !$integrationEntity->isEnabled() || !$account->isEnabled()) { return null; } @@ -161,11 +152,11 @@ class ClientManager $client->setup( $userId, $integrationEntity, - $externalAccountEntity, + $account, $this ); - $this->addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId); + $this->addToClientMap($client, $integrationEntity, $account, $userId); return $client; } @@ -175,32 +166,29 @@ class ClientManager */ protected function createOAuth2(string $integration, string $userId): ?object { - /** @var Integration|null $integrationEntity */ - $integrationEntity = $this->entityManager->getEntity(Integration::ENTITY_TYPE, $integration); + /** @var ?Integration $integrationEntity */ + $integrationEntity = $this->entityManager->getEntityById(Integration::ENTITY_TYPE, $integration); - /** @var ExternalAccount|null $externalAccountEntity */ - $externalAccountEntity = $this->entityManager - ->getEntity(ExternalAccount::ENTITY_TYPE, $integration . '__' . $userId); + /** @var ?ExternalAccount $account */ + $account = $this->entityManager->getEntityById(ExternalAccount::ENTITY_TYPE, "{$integration}__$userId"); /** @var class-string $className */ $className = $this->metadata->get("integrations.$integration.clientClassName"); - $redirectUri = $this->config->get('siteUrl') . '?entryPoint=oauthCallback'; - $redirectUriPath = $this->metadata->get(['integrations', $integration, 'params', 'redirectUriPath']); if ($redirectUriPath) { $redirectUri = rtrim($this->config->get('siteUrl'), '/') . '/' . $redirectUriPath; } - if (!$externalAccountEntity) { + if (!$account) { throw new Error("External Account $integration not found for '$userId'."); } if ( !$integrationEntity || - !$integrationEntity->get('enabled') || - !$externalAccountEntity->get('enabled') + !$integrationEntity->isEnabled() || + !$account->isEnabled() ) { return null; } @@ -213,10 +201,10 @@ class ClientManager 'clientId' => $integrationEntity->get('clientId'), 'clientSecret' => $integrationEntity->get('clientSecret'), 'redirectUri' => $redirectUri, - 'accessToken' => $externalAccountEntity->get('accessToken'), - 'refreshToken' => $externalAccountEntity->get('refreshToken'), - 'tokenType' => $externalAccountEntity->get('tokenType'), - 'expiresAt' => $externalAccountEntity->get('expiresAt'), + 'accessToken' => $account->get('accessToken'), + 'refreshToken' => $account->get('refreshToken'), + 'tokenType' => $account->get('tokenType'), + 'expiresAt' => $account->get('expiresAt'), ]; foreach (get_object_vars($integrationEntity->getValueMap()) as $k => $v) { @@ -244,7 +232,7 @@ class ClientManager $client = new $className($oauth2Client, $params, $this); } - $this->addToClientMap($client, $integrationEntity, $externalAccountEntity, $userId); + $this->addToClientMap($client, $integrationEntity, $account, $userId); return $client; } @@ -255,16 +243,16 @@ class ClientManager */ protected function addToClientMap( $client, - Integration $integrationEntity, - ExternalAccount $externalAccountEntity, + Integration $integration, + ExternalAccount $account, string $userId ) { $this->clientMap[spl_object_hash($client)] = [ 'client' => $client, 'userId' => $userId, - 'integration' => $integrationEntity->getId(), - 'integrationEntity' => $integrationEntity, - 'externalAccountEntity' => $externalAccountEntity, + 'integration' => $integration->getId(), + 'integrationEntity' => $integration, + 'externalAccountEntity' => $account, ]; } @@ -272,14 +260,18 @@ class ClientManager * @param object $client * @throws Error */ - protected function getClientRecord($client): Entity + protected function getClientRecord($client): ExternalAccount { - $data = $this->clientMap[spl_object_hash($client)]; + $data = $this->clientMap[spl_object_hash($client)] ?? null; if (!$data) { throw new Error("External Account: Client not found in hash."); } + if (!isset($data['externalAccountEntity'])) { + throw new Error("External Account: Account not found in hash."); + } + return $data['externalAccountEntity']; } @@ -289,13 +281,11 @@ class ClientManager */ public function isClientLocked($client): bool { - $externalAccountEntity = $this->getClientRecord($client); + $accountSet = $this->getClientRecord($client); - $id = $externalAccountEntity->getId(); + $account = $this->fetchAccountOnlyWithIsLocked($accountSet->getId()); - $account = $this->fetchAccountOnlyWithIsLocked($id); - - return $account->get('isLocked'); + return $account->isLocked(); } /** @@ -303,12 +293,10 @@ class ClientManager */ public function lockClient(object $client): void { - $externalAccountEntity = $this->getClientRecord($client); + $accountSet = $this->getClientRecord($client); - $id = $externalAccountEntity->getId(); - - $account = $this->fetchAccountOnlyWithIsLocked($id); - $account->set('isLocked', true); + $account = $this->fetchAccountOnlyWithIsLocked($accountSet->getId()); + $account->setIsLocked(true); $this->entityManager->saveEntity($account, [ SaveOption::SKIP_HOOKS => true, @@ -321,14 +309,12 @@ class ClientManager */ public function unlockClient(object $client): void { - $externalAccountEntity = $this->getClientRecord($client); + $accountSet = $this->getClientRecord($client); - $id = $externalAccountEntity->getId(); + $accountSet = $this->fetchAccountOnlyWithIsLocked($accountSet->getId()); + $accountSet->setIsLocked(false); - $account = $this->fetchAccountOnlyWithIsLocked($id); - $account->set('isLocked', false); - - $this->entityManager->saveEntity($account, [ + $this->entityManager->saveEntity($accountSet, [ SaveOption::SKIP_HOOKS => true, SaveOption::SILENT => true, ]); @@ -340,9 +326,9 @@ class ClientManager */ public function reFetchClient($client): void { - $externalAccountEntity = $this->getClientRecord($client); + $accountSet = $this->getClientRecord($client); - $id = $externalAccountEntity->getId(); + $id = $accountSet->getId(); $account = $this->entityManager->getEntityById(ExternalAccount::ENTITY_TYPE, $id); @@ -352,7 +338,7 @@ class ClientManager $data = $account->getValueMap(); - $externalAccountEntity->set($data); + $accountSet->set($data); $client->setParams(get_object_vars($data)); } diff --git a/application/Espo/Core/ExternalAccount/Clients/OAuth2Abstract.php b/application/Espo/Core/ExternalAccount/Clients/OAuth2Abstract.php index 1008503ad0..3b13e8d041 100644 --- a/application/Espo/Core/ExternalAccount/Clients/OAuth2Abstract.php +++ b/application/Espo/Core/ExternalAccount/Clients/OAuth2Abstract.php @@ -165,7 +165,7 @@ abstract class OAuth2Abstract implements IClient */ protected function afterTokenRefreshed(array $data): void { - $this->manager?->storeAccessToken(spl_object_hash($this), $data); + $this->manager?->storeAccessToken($this, $data); } /** diff --git a/application/Espo/Entities/ExternalAccount.php b/application/Espo/Entities/ExternalAccount.php index 74ee82ad3d..86f876129e 100644 --- a/application/Espo/Entities/ExternalAccount.php +++ b/application/Espo/Entities/ExternalAccount.php @@ -32,4 +32,50 @@ namespace Espo\Entities; class ExternalAccount extends Integration { public const ENTITY_TYPE = 'ExternalAccount'; + + public function isEnabled(): bool + { + return (bool) $this->get('enabled'); + } + + public function setIsLocked(bool $isLocked): self + { + $this->set('isLocked', $isLocked); + + return $this; + } + + public function isLocked(): bool + { + return (bool) $this->get('isLocked'); + } + + public function setAccessToken(?string $accessToken): self + { + $this->set('accessToken', $accessToken); + + return $this; + } + + public function setTokenType(?string $tokenType): self + { + $this->set('tokenType', $tokenType); + + return $this; + } + + public function setRefreshToken(?string $refreshToken): self + { + $this->set('refreshToken', $refreshToken); + + return $this; + } + + + public function setExpiresAt(?string $expiresAt): self + { + $this->set('expiresAt', $expiresAt); + + return $this; + } } diff --git a/application/Espo/Entities/Integration.php b/application/Espo/Entities/Integration.php index 9f86b67eda..b5def12719 100644 --- a/application/Espo/Entities/Integration.php +++ b/application/Espo/Entities/Integration.php @@ -183,4 +183,9 @@ class Integration extends Entity return (object) $arr; } + + public function isEnabled(): bool + { + return (bool) $this->get('enabled'); + } }