. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License version 3. * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "EspoCRM" word. ************************************************************************/ namespace Espo\Tools\OAuthServer\League\Repositories; use Espo\Core\Field\DateTime; use Espo\Core\Field\Link; use Espo\ORM\EntityManager; use Espo\Tools\OAuthServer\Entities\AuthorizationCode; use Espo\Tools\OAuthServer\League\Entities\AuthCodeEntity; use Espo\Tools\OAuthServer\Repository\AuthorizationCodeRepository; use Espo\Tools\OAuthServer\Repository\ClientRepository as ClientRepository; use Espo\Tools\OAuthServer\Utils\IdentifierHasher; use InvalidArgumentException; use League\OAuth2\Server\Entities\AuthCodeEntityInterface; use League\OAuth2\Server\Repositories\AuthCodeRepositoryInterface; use RuntimeException; use SensitiveParameter; class AuthCodeRepository implements AuthCodeRepositoryInterface { public function __construct( private EntityManager $entityManager, private AuthorizationCodeRepository $repository, private ClientRepository $clientRepository, private IdentifierHasher $hasher, ) {} public function getNewAuthCode(): AuthCodeEntity { return new AuthCodeEntity(); } public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): void { $entity = $this->entityManager->getRDBRepositoryByClass(AuthorizationCode::class)->getNew(); $userId = $authCodeEntity->getUserIdentifier(); if (!is_string($userId)) { throw new InvalidArgumentException("User ID must be string."); } $scopes = array_map(fn ($scope) => $scope->getIdentifier(), $authCodeEntity->getScopes()); $redirectUri = $authCodeEntity->getRedirectUri() ?? throw new InvalidArgumentException("No redirect URI"); $clientId = $authCodeEntity->getClient()->getIdentifier(); $client = $this->clientRepository->getActiveByIdentifier($clientId); if (!$client) { throw new RuntimeException("Client not found."); } $hash = $this->hasher->hash($authCodeEntity->getIdentifier()); $entity ->setClient($client) ->setHash($hash) ->setUser(Link::create($userId)) ->setScopes($scopes) ->setRedirectUri($redirectUri) ->setExpiresAt(DateTime::fromDateTime($authCodeEntity->getExpiryDateTime())); $this->entityManager->saveEntity($entity); } public function revokeAuthCode(#[SensitiveParameter] string $codeId): void { $code = $this->repository->getActiveByIdentifier($codeId); if (!$code || $code->isRevoked()) { return; } $code->setRevoked(); $this->entityManager->saveEntity($code); } public function isAuthCodeRevoked(#[SensitiveParameter] string $codeId): bool { $entity = $this->repository->getActiveByIdentifier($codeId); if (!$entity) { return true; } // Intentionally. return !$entity->isActive(); } }