From 362bf560c846bcb0e195ea0e39ce86adfdd966ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20B=C3=B6hmer?= Date: Tue, 28 Jul 2026 21:07:17 +0200 Subject: [PATCH] test --- .../OAuth/OAuthClientAdminManager.php | 133 ------------------ 1 file changed, 133 deletions(-) delete mode 100644 src/Services/OAuth/OAuthClientAdminManager.php diff --git a/src/Services/OAuth/OAuthClientAdminManager.php b/src/Services/OAuth/OAuthClientAdminManager.php deleted file mode 100644 index 5772439f..00000000 --- a/src/Services/OAuth/OAuthClientAdminManager.php +++ /dev/null @@ -1,133 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace App\Services\OAuth; - -use Doctrine\ORM\EntityManagerInterface; -use League\Bundle\OAuth2ServerBundle\Manager\ClientManagerInterface; -use League\Bundle\OAuth2ServerBundle\Model\AbstractClient; -use League\Bundle\OAuth2ServerBundle\Model\AccessToken; -use League\Bundle\OAuth2ServerBundle\Model\AuthorizationCode; -use League\Bundle\OAuth2ServerBundle\Model\ClientInterface; -use League\Bundle\OAuth2ServerBundle\Model\RefreshToken; - -/** - * Backs the admin OAuth2 client overview (App\Controller\OAuthClientAdminController) - listing every - * registered client (almost always RFC 7591 self-registered, see that controller's docblock) with how - * many currently-live access tokens each one holds, and permanently removing a client plus everything it - * was ever granted. - * - * Deliberately does NOT use League\Bundle\OAuth2ServerBundle\Service\CredentialsRevokerInterface's - * DoctrineCredentialsRevoker: it unconditionally also issues an UPDATE against the DeviceCode model/table, - * which does not exist in our schema (config/packages/league_oauth2_server.yaml disables the device code - * grant, so migrations/Version20260726180454.php never created oauth2_device_code) - that call would - * throw "no such table" here. The access/refresh tokens and authorization codes (the 3 credential types - * we actually persist) are bulk-deleted directly instead, before removing the client itself - the - * "client" FK columns are mapped ON DELETE CASCADE (see League's Persistence\Mapping\Driver), but SQLite - * only enforces that when "PRAGMA foreign_keys = ON" is set on the connection, which is not guaranteed - * here, so this does not rely on it. - */ -class OAuthClientAdminManager -{ - public function __construct( - private readonly EntityManagerInterface $entityManager, - private readonly ClientManagerInterface $clientManager, - ) { - } - - /** - * @return list - */ - public function listClientsWithLiveTokenCounts(): array - { - $clients = $this->clientManager->list(null); - - $rows = $this->entityManager->createQueryBuilder() - ->select('IDENTITY(at.client) as clientId', 'COUNT(at.identifier) as tokenCount') - ->from(AccessToken::class, 'at') - ->where('at.revoked = false') - ->andWhere('at.expiry > :now') - ->groupBy('at.client') - ->setParameter('now', new \DateTimeImmutable()) - ->getQuery() - ->getResult(); - - /** @var array $countByClientId */ - $countByClientId = []; - foreach ($rows as $row) { - $countByClientId[$row['clientId']] = (int) $row['tokenCount']; - } - - return array_map(static fn (ClientInterface $client): array => [ - 'client' => $client, - 'liveTokenCount' => $countByClientId[$client->getIdentifier()] ?? 0, - ], $clients); - } - - /** - * @return bool false if no client with that identifier exists - */ - public function deleteClient(string $identifier): bool - { - $client = $this->clientManager->find($identifier); - if (!$client instanceof AbstractClient) { - return false; - } - - $this->deleteAllCredentialsForClient($identifier); - $this->clientManager->remove($client); - - return true; - } - - private function deleteAllCredentialsForClient(string $clientIdentifier): void - { - // Refresh tokens first - they reference access tokens (not the client directly), so this has to - // run before the access tokens themselves are deleted. - $accessTokenIdsSubQuery = $this->entityManager->createQueryBuilder() - ->select('at2.identifier') - ->from(AccessToken::class, 'at2') - ->where('at2.client = :client') - ->getDQL(); - - $refreshTokenDelete = $this->entityManager->createQueryBuilder(); - $refreshTokenDelete->delete(RefreshToken::class, 'rt') - ->where($refreshTokenDelete->expr()->in('rt.accessToken', $accessTokenIdsSubQuery)) - ->setParameter('client', $clientIdentifier) - ->getQuery() - ->execute(); - - $this->entityManager->createQueryBuilder() - ->delete(AccessToken::class, 'at') - ->where('at.client = :client') - ->setParameter('client', $clientIdentifier) - ->getQuery() - ->execute(); - - $this->entityManager->createQueryBuilder() - ->delete(AuthorizationCode::class, 'ac') - ->where('ac.client = :client') - ->setParameter('client', $clientIdentifier) - ->getQuery() - ->execute(); - } -}