From 1e58ce55b18c16e2a6be3043326a920a1eb441d8 Mon Sep 17 00:00:00 2001 From: Nattfarinn Date: Tue, 7 Mar 2023 01:57:16 +0100 Subject: [PATCH 01/46] wip: TokenService --- .../IbexaCoreExtension.php | 3 + .../config/storage/legacy/schema.yaml | 30 +++ src/bundle/Core/Resources/config/token.yml | 5 + .../Persistence/Token/CreateStruct.php | 22 ++ src/contracts/Persistence/Token/Handler.php | 33 +++ src/contracts/Persistence/Token/Token.php | 26 ++ src/contracts/Persistence/Token/TokenType.php | 18 ++ .../Decorator/TokenServiceDecorator.php | 69 ++++++ .../Events/Token/BeforeCheckTokenEvent.php | 67 ++++++ .../Events/Token/BeforeDeleteTokenEvent.php | 27 +++ .../Events/Token/BeforeGenerateTokenEvent.php | 87 +++++++ .../Events/Token/BeforeGetTokenEvent.php | 68 ++++++ .../Events/Token/CheckTokenEvent.php | 54 +++++ .../Events/Token/DeleteTokenEvent.php | 27 +++ .../Events/Token/GenerateTokenEvent.php | 74 ++++++ .../Repository/Events/Token/GetTokenEvent.php | 55 +++++ src/contracts/Repository/TokenService.php | 37 +++ .../Repository/Values/Token/Token.php | 62 +++++ .../Token/TokenGeneratorInterface.php | 14 ++ .../Base/Exceptions/TokenExpiredException.php | 36 +++ src/lib/Event/TokenService.php | 132 +++++++++++ .../Token/Doctrine/DoctrineGateway.php | 224 ++++++++++++++++++ .../Legacy/Token/Gateway/Token/Gateway.php | 50 ++++ .../TokenType/Doctrine/DoctrineGateway.php | 149 ++++++++++++ .../Token/Gateway/TokenType/Gateway.php | 35 +++ src/lib/Persistence/Legacy/Token/Handler.php | 126 ++++++++++ src/lib/Persistence/Legacy/Token/Mapper.php | 35 +++ src/lib/Repository/TokenService.php | 116 +++++++++ .../settings/repository/autowire.yml | 1 + .../Resources/settings/repository/event.yml | 4 + .../Resources/settings/repository/inner.yml | 4 + .../settings/storage_engines/legacy.yml | 1 + .../settings/storage_engines/legacy/token.yml | 19 ++ src/lib/Resources/settings/tokens.yml | 9 + src/lib/Token/WebSafeGenerator.php | 25 ++ .../Core/LegacyTestContainerBuilder.php | 1 + .../Resources/settings/integration_legacy.yml | 4 + 37 files changed, 1749 insertions(+) create mode 100644 src/bundle/Core/Resources/config/token.yml create mode 100644 src/contracts/Persistence/Token/CreateStruct.php create mode 100644 src/contracts/Persistence/Token/Handler.php create mode 100644 src/contracts/Persistence/Token/Token.php create mode 100644 src/contracts/Persistence/Token/TokenType.php create mode 100644 src/contracts/Repository/Decorator/TokenServiceDecorator.php create mode 100644 src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/CheckTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/DeleteTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/GenerateTokenEvent.php create mode 100644 src/contracts/Repository/Events/Token/GetTokenEvent.php create mode 100644 src/contracts/Repository/TokenService.php create mode 100644 src/contracts/Repository/Values/Token/Token.php create mode 100644 src/contracts/Token/TokenGeneratorInterface.php create mode 100644 src/lib/Base/Exceptions/TokenExpiredException.php create mode 100644 src/lib/Event/TokenService.php create mode 100644 src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php create mode 100644 src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php create mode 100644 src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php create mode 100644 src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php create mode 100644 src/lib/Persistence/Legacy/Token/Handler.php create mode 100644 src/lib/Persistence/Legacy/Token/Mapper.php create mode 100644 src/lib/Repository/TokenService.php create mode 100644 src/lib/Resources/settings/storage_engines/legacy/token.yml create mode 100644 src/lib/Resources/settings/tokens.yml create mode 100644 src/lib/Token/WebSafeGenerator.php diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php index 848836733a..f457d9afea 100644 --- a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -4,6 +4,8 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); + namespace Ibexa\Bundle\Core\DependencyInjection; use Ibexa\Bundle\Core\DependencyInjection\Compiler\QueryTypePass; @@ -390,6 +392,7 @@ private function handleApiLoading(ContainerBuilder $container, FileLoader $loade $coreLoader->load('user_preference.yml'); $coreLoader->load('events.yml'); $coreLoader->load('thumbnails.yml'); + $coreLoader->load('tokens.yml'); $coreLoader->load('content_location_mapper.yml'); // Public API services diff --git a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml index 2c2d93df70..a8c8a2da7b 100644 --- a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml +++ b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml @@ -638,3 +638,33 @@ tables: group: { type: string, nullable: false, length: 128 } identifier: { type: string, nullable: false, length: 128 } value: { type: json, nullable: false, length: 0 } + ibexa_token: + indexes: + ibexa_token_id: { fields: [id] } + uniqueConstraints: + ibexa_token_unique: { fields: [token, identifier, type_id] } + foreignKeys: + ibexa_token_type_id_fk: + fields: [type_id] + foreignTable: ibexa_token_type + foreignFields: [id] + options: + onDelete: CASCADE + onUpdate: 'NO ACTION' + id: + id: { type: integer, nullable: false, options: { autoincrement: true } } + fields: + type_id: { type: integer, nullable: false } + token: { type: string, nullable: false, length: 64 } + identifier: { type: string, nullable: true, length: 128 } + created: { type: integer, nullable: false, options: { default: '0' } } + expires: { type: integer, nullable: false, options: { default: '0' } } + ibexa_token_type: + indexes: + ibexa_token_type_id: { fields: [id] } + uniqueConstraints: + ibexa_token_type_unique: { fields: [identifier] } + id: + id: { type: integer, nullable: false, options: { autoincrement: true } } + fields: + identifier: { type: string, nullable: false, length: 32 } diff --git a/src/bundle/Core/Resources/config/token.yml b/src/bundle/Core/Resources/config/token.yml new file mode 100644 index 0000000000..2b8a2ef68f --- /dev/null +++ b/src/bundle/Core/Resources/config/token.yml @@ -0,0 +1,5 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false diff --git a/src/contracts/Persistence/Token/CreateStruct.php b/src/contracts/Persistence/Token/CreateStruct.php new file mode 100644 index 0000000000..61db2e8c2a --- /dev/null +++ b/src/contracts/Persistence/Token/CreateStruct.php @@ -0,0 +1,22 @@ +innerService = $innerService; + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + return $this->innerService->getToken( + $tokenType, + $token, + $identifier + ); + } + + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + return $this->innerService->checkToken( + $tokenType, + $token, + $identifier + ); + } + + public function generateToken( + string $type, + ?string $identifier, + int $ttl, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + return $this->innerService->generateToken( + $type, + $identifier, + $ttl, + $tokenLength, + $tokenGenerator + ); + } + + public function deleteToken(Token $token): void + { + $this->innerService->deleteToken($token); + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php new file mode 100644 index 0000000000..84a40eac0d --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -0,0 +1,67 @@ +tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): bool + { + if (!$this->hasResult()) { + throw new UnexpectedValueException('Return value is not set or not of type boolean. Check hasResult() or set it using setResult() before you call the getter.'); + } + + return $this->result; + } + + public function setResult(?bool $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result !== null; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php new file mode 100644 index 0000000000..0656b688bb --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php new file mode 100644 index 0000000000..cb267de081 --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -0,0 +1,87 @@ +tokenType = $type; + $this->identifier = $identifier; + $this->ttl = $ttl; + $this->tokenLength = $tokenLength; + $this->tokenGenerator = $tokenGenerator; + } + + public function getToken(): Token + { + if (!$this->hasToken()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasToken() or set it using setToken() before you call the getter.', Token::class)); + } + + return $this->token; + } + + public function setToken(?Token $token): void + { + $this->token = $token; + } + + public function hasToken(): bool + { + return $this->token instanceof Token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getTtl(): int + { + return $this->ttl; + } + + public function getTokenLength(): int + { + return $this->tokenLength; + } + + public function getTokenGenerator(): ?TokenGeneratorInterface + { + return $this->tokenGenerator; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php new file mode 100644 index 0000000000..bf4fa003bc --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -0,0 +1,68 @@ +tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): Token + { + if (!$this->hasResult()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', Token::class)); + } + + return $this->result; + } + + public function setResult(?Token $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result instanceof Token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/CheckTokenEvent.php b/src/contracts/Repository/Events/Token/CheckTokenEvent.php new file mode 100644 index 0000000000..898f0f3bd4 --- /dev/null +++ b/src/contracts/Repository/Events/Token/CheckTokenEvent.php @@ -0,0 +1,54 @@ +result = $result; + $this->tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): bool + { + return $this->result; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/DeleteTokenEvent.php b/src/contracts/Repository/Events/Token/DeleteTokenEvent.php new file mode 100644 index 0000000000..e93d4e34c8 --- /dev/null +++ b/src/contracts/Repository/Events/Token/DeleteTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Token/GenerateTokenEvent.php b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php new file mode 100644 index 0000000000..11f33fa803 --- /dev/null +++ b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php @@ -0,0 +1,74 @@ +token = $token; + $this->tokenType = $tokenType; + $this->identifier = $identifier; + $this->ttl = $ttl; + $this->tokenLength = $tokenLength; + $this->tokenGenerator = $tokenGenerator; + } + + public function getToken(): Token + { + return $this->token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getTtl(): int + { + return $this->ttl; + } + + public function getTokenLength(): int + { + return $this->tokenLength; + } + + public function getTokenGenerator(): ?TokenGeneratorInterface + { + return $this->tokenGenerator; + } +} diff --git a/src/contracts/Repository/Events/Token/GetTokenEvent.php b/src/contracts/Repository/Events/Token/GetTokenEvent.php new file mode 100644 index 0000000000..d8c2686771 --- /dev/null +++ b/src/contracts/Repository/Events/Token/GetTokenEvent.php @@ -0,0 +1,55 @@ +result = $result; + $this->tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): Token + { + return $this->result; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/TokenService.php b/src/contracts/Repository/TokenService.php new file mode 100644 index 0000000000..6bd6f54d7c --- /dev/null +++ b/src/contracts/Repository/TokenService.php @@ -0,0 +1,37 @@ +id; + } + + public function getType(): string + { + return $this->type; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getCreated(): DateTimeImmutable + { + return $this->created; + } + + public function getExpires(): DateTimeImmutable + { + return $this->expires; + } + + public function __toString(): string + { + return $this->token; + } +} diff --git a/src/contracts/Token/TokenGeneratorInterface.php b/src/contracts/Token/TokenGeneratorInterface.php new file mode 100644 index 0000000000..c897c509a3 --- /dev/null +++ b/src/contracts/Token/TokenGeneratorInterface.php @@ -0,0 +1,14 @@ +setMessageTemplate("Token '%tokenType%:%token%' expired on '%when%'"); + $this->setParameters([ + '%tokenType%' => $tokenType, + '%token%' => $token, + '%when%' => $when->format(DateTimeInterface::ATOM), + ]); + + parent::__construct($this->getBaseTranslation(), self::UNAUTHORIZED, $previous); + } +} diff --git a/src/lib/Event/TokenService.php b/src/lib/Event/TokenService.php new file mode 100644 index 0000000000..44b51b3b6c --- /dev/null +++ b/src/lib/Event/TokenService.php @@ -0,0 +1,132 @@ +eventDispatcher = $eventDispatcher; + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + $eventData = [$tokenType, $token, $identifier]; + + $beforeEvent = new BeforeGetTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getToken() + : $this->innerService->getToken(...$eventData); + + $this->eventDispatcher->dispatch( + new GetTokenEvent($result, ...$eventData) + ); + + return $result; + } + + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + $eventData = [$tokenType, $token, $identifier]; + + $beforeEvent = new BeforeCheckTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getResult() + : $this->innerService->checkToken(...$eventData); + + $this->eventDispatcher->dispatch( + new CheckTokenEvent($result, ...$eventData) + ); + + return $result; + } + + public function generateToken( + string $type, + ?string $identifier, + int $ttl, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + $eventData = [$type, $identifier, $ttl, $tokenLength, $tokenGenerator]; + + $beforeEvent = new BeforeGenerateTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getToken(); + } + + $token = $beforeEvent->hasToken() + ? $beforeEvent->getToken() + : $this->innerService->generateToken(...$eventData); + + $this->eventDispatcher->dispatch( + new GenerateTokenEvent($token, ...$eventData) + ); + + return $token; + } + + public function deleteToken(Token $token): void + { + $eventData = [$token]; + + $beforeEvent = new BeforeDeleteTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteToken($token); + + $this->eventDispatcher->dispatch( + new DeleteTokenEvent(...$eventData) + ); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php new file mode 100644 index 0000000000..1c44a585b4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -0,0 +1,224 @@ +connection = $connection; + $this->tokenTypeGateway = $tokenTypeGateway; + } + + public static function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_TYPE_ID, + self::COLUMN_TOKEN, + self::COLUMN_IDENTIFIER, + self::COLUMN_CREATED, + self::COLUMN_EXPIRES, + ]; + } + + public function insert( + int $typeId, + string $token, + ?string $identifier, + int $ttl + ): int { + $now = time(); + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::TABLE_NAME) + ->values([ + self::COLUMN_TYPE_ID => ':type_id', + self::COLUMN_TOKEN => ':token', + self::COLUMN_IDENTIFIER => ':identifier', + self::COLUMN_CREATED => ':created', + self::COLUMN_EXPIRES => ':expires', + ]) + ->setParameter(':type_id', $typeId, PDO::PARAM_INT) + ->setParameter(':token', $token, PDO::PARAM_STR) + ->setParameter(':identifier', $identifier, PDO::PARAM_STR) + ->setParameter(':created', $now, PDO::PARAM_INT) + ->setParameter(':expires', $now + $ttl, PDO::PARAM_INT); + + $query->execute(); + + return (int)$this->connection->lastInsertId(); + } + + public function delete(int $tokenId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NAME) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $tokenId, PDO::PARAM_INT); + + $query->execute(); + } + + public function deleteExpired(?int $typeId = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NAME) + ->andWhere( + $query->expr()->lt(self::COLUMN_EXPIRES, ':now') + ) + ->setParameter(':now', time(), PDO::PARAM_INT); + + if (!empty($typeId)) { + $query->andWhere( + $query->expr()->eq( + self::COLUMN_TYPE_ID, + ':type_id' + ) + ); + $query->setParameter(':type_id', $typeId, PDO::PARAM_INT); + } + + $query->execute(); + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): array { + $query = $this->getTokenSelectQueryBuilder($tokenType, $token, $identifier); + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token', "token: $token, type: $tokenType, identifier: $identifier"); + } + + return $row; + } + + public function getTokenById(int $tokenId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns()) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_ID), + ':tokenId' + ) + ); + + $query->setParameter(':tokenId', $tokenId, PDO::PARAM_INT); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token', "id: $tokenId"); + } + + return $row; + } + + public function getTokenSelectQueryBuilder( + string $tokenType, + string $token, + ?string $identifier = null, + bool $externalIdentifier = false + ): QueryBuilder { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select(...$this->getAliasedColumns()) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->innerJoin( + self::DEFAULT_TABLE_ALIAS, + TokenTypeGateway::TABLE_NAME, + TokenTypeGateway::DEFAULT_TABLE_ALIAS, + $expr->eq( + $this->getAliasedColumn(self::COLUMN_TYPE_ID), + $this->tokenTypeGateway->getAliasedColumn(TokenTypeGateway::COLUMN_ID) + ) + ) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_TOKEN), + ':token' + ) + ) + ->andWhere( + $query->expr()->eq( + $this->tokenTypeGateway->getAliasedColumn(TokenTypeGateway::COLUMN_IDENTIFIER), + ':tokenType' + ) + ); + + $query->setParameter(':tokenType', $tokenType, PDO::PARAM_STR); + $query->setParameter(':token', $token, PDO::PARAM_STR); + + if (!$externalIdentifier) { + $query->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_IDENTIFIER), + ':identifier' + ) + ); + $query->setParameter(':identifier', $identifier, PDO::PARAM_STR); + } + + return $query; + } + + public function getAliasedColumns( + string $alias = self::DEFAULT_TABLE_ALIAS, + array $columns = null + ): array { + if (empty($columns)) { + $columns = self::getColumns(); + } + + return array_map( + fn (string $column) => $this->getAliasedColumn($column, $alias), + $columns + ); + } + + public function getAliasedColumn( + string $column, + string $alias = self::DEFAULT_TABLE_ALIAS + ): string { + return sprintf('%s.%s', $alias, $column); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php new file mode 100644 index 0000000000..85451a5876 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php @@ -0,0 +1,50 @@ +connection = $connection; + } + + public static function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_IDENTIFIER, + ]; + } + + public function insert(string $identifier): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::TABLE_NAME) + ->values([self::COLUMN_IDENTIFIER => ':identifier']) + ->setParameter(':identifier', $identifier, PDO::PARAM_STR); + + $query->execute(); + + return (int)$this->connection->lastInsertId(); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function deleteById(int $typeId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NAME) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $typeId, PDO::PARAM_INT); + + $query->execute(); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function deleteByIdentifier(string $identifier): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NAME) + ->where($query->expr()->eq(self::COLUMN_IDENTIFIER, ':identifier')) + ->setParameter(':identifier', $identifier, PDO::PARAM_STR); + + $query->execute(); + } + + public function getTypeById(int $typeId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns()) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_ID), + ':typeId' + ) + ); + + $query->setParameter(':typeId', $typeId, PDO::PARAM_INT); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token_type', "id: $typeId"); + } + + return $row; + } + + public function getTypeByIdentifier(string $identifier): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns()) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_IDENTIFIER), + ':identifier' + ) + ); + + $query->setParameter(':identifier', $identifier, PDO::PARAM_STR); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token_type', "identifier: $identifier"); + } + + return $row; + } + + public function getAliasedColumns( + string $alias = self::DEFAULT_TABLE_ALIAS, + array $columns = null + ): array { + if (empty($columns)) { + $columns = self::getColumns(); + } + + return array_map( + fn (string $column) => $this->getAliasedColumn($column, $alias), + $columns + ); + } + + public function getAliasedColumn( + string $column, + string $alias = self::DEFAULT_TABLE_ALIAS + ): string { + return sprintf('%s.%s', $alias, $column); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php new file mode 100644 index 0000000000..8acf21ffe9 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php @@ -0,0 +1,35 @@ +mapper = $mapper; + $this->tokenGateway = $tokenGateway; + $this->tokenTypeGateway = $tokenTypeGateway; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + * @throws \Exception + */ + public function getToken( + string $tokenType, + string $token, + ?string $identifier + ): Token { + $token = $this->mapper->mapToken( + $this->tokenGateway->getToken($tokenType, $token, $identifier) + ); + + if ($token->expires < time()) { + throw new TokenExpiredException( + $tokenType, + $token->token, + new DateTimeImmutable('@' . $token->expires) + ); + } + + return $token; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function getTokenType( + string $identifier + ): TokenType { + return $this->mapper->mapTokenType( + $this->tokenTypeGateway->getTypeByIdentifier($identifier) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function createToken(CreateStruct $createStruct): Token + { + try { + $typeId = $this->getTokenType($createStruct->type)->id; + } catch (NotFoundException $exception) { + $typeId = $this->tokenTypeGateway->insert($createStruct->type); + } + + $tokenId = $this->tokenGateway->insert( + $typeId, + $createStruct->token, + $createStruct->identifier, + $createStruct->ttl + ); + + return $this->mapper->mapToken( + $this->tokenGateway->getTokenById($tokenId) + ); + } + + public function deleteToken(Token $token): void + { + $this->deleteTokenById($token->id); + } + + public function deleteTokenById(int $tokenId): void + { + $this->tokenGateway->delete($tokenId); + } + + public function deleteExpiredTokens(?string $tokenType = null): void + { + try { + if (null !== $tokenType) { + $typeId = $this->getTokenType($tokenType); + } + } catch (NotFoundException $exception) { + return; + } + + $this->tokenGateway->deleteExpired($typeId ?? null); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Mapper.php b/src/lib/Persistence/Legacy/Token/Mapper.php new file mode 100644 index 0000000000..ff0f8e26b0 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Mapper.php @@ -0,0 +1,35 @@ + (int)$tokenRow['id'], + 'typeId' => (int)$tokenRow['type_id'], + 'token' => (string)$tokenRow['token'], + 'identifier' => $tokenRow['identifier'] === null ? null : (string)$tokenRow['identifier'], + 'created' => (int)$tokenRow['created'], + 'expires' => (int)$tokenRow['expires'], + ]); + } + + public function mapTokenType(array $tokenTypeRow): TokenType + { + return new TokenType([ + 'id' => (int)$tokenTypeRow['id'], + 'identifier' => (string)$tokenTypeRow['identifier'], + ]); + } +} diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php new file mode 100644 index 0000000000..d488f91419 --- /dev/null +++ b/src/lib/Repository/TokenService.php @@ -0,0 +1,116 @@ +persistenceHandler = $persistenceHandler; + $this->defaultTokenGenerator = $defaultTokenGenerator; + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException + */ + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + $type = $this->persistenceHandler->getTokenType($tokenType); + $token = $this->persistenceHandler->getToken( + $type->identifier, + $token, + $identifier + ); + + return $this->buildDomainObject( + $token, + $type + ); + } + + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + try { + $this->getToken($tokenType, $token, $identifier); + + return true; + } catch (NotFoundException|TokenExpiredException $exception) { + } + + return false; + } + + public function generateToken( + string $type, + ?string $identifier, + int $ttl, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + $createStruct = new CreateStruct([ + 'type' => $type, + 'token' => ($tokenGenerator ?? $this->defaultTokenGenerator)->generateToken($tokenLength), + 'identifier' => $identifier, + 'ttl' => $ttl, + ]); + + $token = $this->persistenceHandler->createToken($createStruct); + $tokenType = $this->persistenceHandler->getTokenType($type); + + return $this->buildDomainObject( + $token, + $tokenType + ); + } + + public function deleteToken(Token $token): void + { + $this->persistenceHandler->deleteTokenById($token->getId()); + } + + /** + * @throws \Exception + */ + protected function buildDomainObject( + SPIToken $spiToken, + SPITokenType $spiTokenType + ): Token { + return new Token([ + 'id' => $spiToken->id, + 'type' => $spiTokenType->identifier, + 'token' => $spiToken->token, + 'identifier' => $spiToken->identifier, + 'created' => new DateTimeImmutable('@' . $spiToken->created), + 'expires' => new DateTimeImmutable('@' . $spiToken->expires), + ]); + } +} diff --git a/src/lib/Resources/settings/repository/autowire.yml b/src/lib/Resources/settings/repository/autowire.yml index c72fda418c..a462cd5a12 100644 --- a/src/lib/Resources/settings/repository/autowire.yml +++ b/src/lib/Resources/settings/repository/autowire.yml @@ -21,6 +21,7 @@ services: Ibexa\Contracts\Core\Repository\URLAliasService: '@ibexa.api.service.url_alias' Ibexa\Contracts\Core\Repository\TrashService: '@ibexa.api.service.trash' Ibexa\Contracts\Core\Repository\SettingService: '@Ibexa\Core\Event\SettingService' + Ibexa\Contracts\Core\Repository\TokenService: '@Ibexa\Core\Event\TokenService' Ibexa\Contracts\Core\Repository\PermissionService: '@Ibexa\Core\Repository\Permission\CachedPermissionService' Ibexa\Contracts\Core\Repository\PermissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionService' diff --git a/src/lib/Resources/settings/repository/event.yml b/src/lib/Resources/settings/repository/event.yml index f92c4c1e9e..7007cad563 100644 --- a/src/lib/Resources/settings/repository/event.yml +++ b/src/lib/Resources/settings/repository/event.yml @@ -96,3 +96,7 @@ services: Ibexa\Core\Event\SettingService: arguments: $innerService: '@Ibexa\Core\Repository\SettingService' + + Ibexa\Core\Event\TokenService: + arguments: + $innerService: '@Ibexa\Core\Repository\TokenService' diff --git a/src/lib/Resources/settings/repository/inner.yml b/src/lib/Resources/settings/repository/inner.yml index 279cf1f09d..147057611d 100644 --- a/src/lib/Resources/settings/repository/inner.yml +++ b/src/lib/Resources/settings/repository/inner.yml @@ -128,6 +128,10 @@ services: $settingHandler: '@Ibexa\Core\Persistence\Cache\SettingHandler' $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' + Ibexa\Core\Repository\TokenService: + autowire: true + autoconfigure: true + # Factories Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener: class: Ibexa\Core\Search\Common\BackgroundIndexer\NullIndexer diff --git a/src/lib/Resources/settings/storage_engines/legacy.yml b/src/lib/Resources/settings/storage_engines/legacy.yml index 1051bbdb74..3942b9e426 100644 --- a/src/lib/Resources/settings/storage_engines/legacy.yml +++ b/src/lib/Resources/settings/storage_engines/legacy.yml @@ -20,6 +20,7 @@ imports: - {resource: storage_engines/legacy/notification.yml} - {resource: storage_engines/legacy/user_preference.yml} - {resource: storage_engines/legacy/setting.yml} + - {resource: storage_engines/legacy/token.yml} services: Ibexa\Core\Persistence\Legacy\Handler: diff --git a/src/lib/Resources/settings/storage_engines/legacy/token.yml b/src/lib/Resources/settings/storage_engines/legacy/token.yml new file mode 100644 index 0000000000..23051a30a5 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/token.yml @@ -0,0 +1,19 @@ +services: + Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + $tokenTypeGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway' + + Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Token\Mapper: ~ + + Ibexa\Core\Persistence\Legacy\Token\Handler: + arguments: + $tokenGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway' + $tokenTypeGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway' + $mapper: '@Ibexa\Core\Persistence\Legacy\Token\Mapper' + + Ibexa\Contracts\Core\Persistence\Token\Handler: '@Ibexa\Core\Persistence\Legacy\Token\Handler' diff --git a/src/lib/Resources/settings/tokens.yml b/src/lib/Resources/settings/tokens.yml new file mode 100644 index 0000000000..09ba8d7f22 --- /dev/null +++ b/src/lib/Resources/settings/tokens.yml @@ -0,0 +1,9 @@ +services: + _defaults: + public: false + autoconfigure: true + autowire: true + + Ibexa\Core\Token\WebSafeGenerator: ~ + + Ibexa\Contracts\Core\Token\TokenGeneratorInterface: '@Ibexa\Core\Token\WebSafeGenerator' diff --git a/src/lib/Token/WebSafeGenerator.php b/src/lib/Token/WebSafeGenerator.php new file mode 100644 index 0000000000..fb594549d7 --- /dev/null +++ b/src/lib/Token/WebSafeGenerator.php @@ -0,0 +1,25 @@ +load('policies.yml'); $loader->load('events.yml'); $loader->load('thumbnails.yml'); + $loader->load('tokens.yml'); $loader->load('content_location_mapper.yml'); // tests/integration/Core/Resources/settings/common.yml diff --git a/tests/integration/Core/Resources/settings/integration_legacy.yml b/tests/integration/Core/Resources/settings/integration_legacy.yml index f1cdd2294f..a2e68068ad 100644 --- a/tests/integration/Core/Resources/settings/integration_legacy.yml +++ b/tests/integration/Core/Resources/settings/integration_legacy.yml @@ -57,4 +57,8 @@ services: public: true alias: Ibexa\Core\Event\SettingService + Ibexa\Contracts\Core\Repository\TokenService: + public: true + alias: Ibexa\Core\Event\TokenService + Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator: '@Ibexa\Tests\Integration\Core\Persistence\Variation\InMemoryVariationHandler' From c5657147c8a36c4e9160e390bc93b35bb244d146 Mon Sep 17 00:00:00 2001 From: Nattfarinn Date: Tue, 7 Mar 2023 02:05:16 +0100 Subject: [PATCH 02/46] fix: strict_types --- src/lib/Token/WebSafeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Token/WebSafeGenerator.php b/src/lib/Token/WebSafeGenerator.php index fb594549d7..41386a272c 100644 --- a/src/lib/Token/WebSafeGenerator.php +++ b/src/lib/Token/WebSafeGenerator.php @@ -17,7 +17,7 @@ class WebSafeGenerator implements TokenGeneratorInterface */ public function generateToken(int $length = 64): string { - $entropy = floor(($length + 1) * 0.75); + $entropy = (int)floor(($length + 1) * 0.75); $encoded = base64_encode(random_bytes($entropy)); return substr(rtrim(strtr($encoded, '+-', '/_'), '='), 0, $length); From 404e918c75bbb667b042f63801ff75e149add906 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 08:37:58 +0100 Subject: [PATCH 03/46] Marked classes as final --- src/contracts/Persistence/Token/CreateStruct.php | 6 +++--- src/contracts/Persistence/Token/Handler.php | 2 +- src/contracts/Persistence/Token/Token.php | 2 +- src/contracts/Persistence/Token/TokenType.php | 2 +- src/lib/Base/Exceptions/TokenExpiredException.php | 2 +- src/lib/Repository/TokenService.php | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/contracts/Persistence/Token/CreateStruct.php b/src/contracts/Persistence/Token/CreateStruct.php index 61db2e8c2a..69fb84268f 100644 --- a/src/contracts/Persistence/Token/CreateStruct.php +++ b/src/contracts/Persistence/Token/CreateStruct.php @@ -10,13 +10,13 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; -class CreateStruct extends ValueObject +final class CreateStruct extends ValueObject { public string $type; public string $token; - public ?string $identifier = null; - public int $ttl; + + public ?string $identifier = null; } diff --git a/src/contracts/Persistence/Token/Handler.php b/src/contracts/Persistence/Token/Handler.php index 50dc2be7d4..80d6b1d6b6 100644 --- a/src/contracts/Persistence/Token/Handler.php +++ b/src/contracts/Persistence/Token/Handler.php @@ -16,7 +16,7 @@ interface Handler public function getToken( string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ): Token; public function getTokenType( diff --git a/src/contracts/Persistence/Token/Token.php b/src/contracts/Persistence/Token/Token.php index b75ca23827..2ccaa6d7e9 100644 --- a/src/contracts/Persistence/Token/Token.php +++ b/src/contracts/Persistence/Token/Token.php @@ -10,7 +10,7 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; -class Token extends ValueObject +final class Token extends ValueObject { public int $id; diff --git a/src/contracts/Persistence/Token/TokenType.php b/src/contracts/Persistence/Token/TokenType.php index 7f87720be9..22e6cb521f 100644 --- a/src/contracts/Persistence/Token/TokenType.php +++ b/src/contracts/Persistence/Token/TokenType.php @@ -10,7 +10,7 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; -class TokenType extends ValueObject +final class TokenType extends ValueObject { public int $id; diff --git a/src/lib/Base/Exceptions/TokenExpiredException.php b/src/lib/Base/Exceptions/TokenExpiredException.php index 7a4e6da4d5..4157cc2c8d 100644 --- a/src/lib/Base/Exceptions/TokenExpiredException.php +++ b/src/lib/Base/Exceptions/TokenExpiredException.php @@ -14,7 +14,7 @@ use Ibexa\Core\Base\Translatable; use Ibexa\Core\Base\TranslatableBase; -class TokenExpiredException extends APIUnauthorizedException implements Httpable, Translatable +final class TokenExpiredException extends APIUnauthorizedException implements Httpable, Translatable { use TranslatableBase; diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php index d488f91419..54445fdbd9 100644 --- a/src/lib/Repository/TokenService.php +++ b/src/lib/Repository/TokenService.php @@ -19,7 +19,7 @@ use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; use Ibexa\Core\Base\Exceptions\TokenExpiredException; -class TokenService implements TokenServiceInterface +final class TokenService implements TokenServiceInterface { private Handler $persistenceHandler; @@ -71,8 +71,8 @@ public function checkToken( public function generateToken( string $type, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ): Token { @@ -100,7 +100,7 @@ public function deleteToken(Token $token): void /** * @throws \Exception */ - protected function buildDomainObject( + private function buildDomainObject( SPIToken $spiToken, SPITokenType $spiTokenType ): Token { From 5dacbadc1fe38f1213834b89806ab91b86af4d14 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 08:38:48 +0100 Subject: [PATCH 04/46] Marked TokenServiceDecorator as abstract class --- .../Repository/Decorator/TokenServiceDecorator.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contracts/Repository/Decorator/TokenServiceDecorator.php b/src/contracts/Repository/Decorator/TokenServiceDecorator.php index 90ed010c11..366508dd97 100644 --- a/src/contracts/Repository/Decorator/TokenServiceDecorator.php +++ b/src/contracts/Repository/Decorator/TokenServiceDecorator.php @@ -12,7 +12,7 @@ use Ibexa\Contracts\Core\Repository\Values\Token\Token; use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; -class TokenServiceDecorator implements TokenService +abstract class TokenServiceDecorator implements TokenService { protected TokenService $innerService; @@ -48,15 +48,15 @@ public function checkToken( public function generateToken( string $type, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ): Token { return $this->innerService->generateToken( $type, - $identifier, $ttl, + $identifier, $tokenLength, $tokenGenerator ); From 2d34fb920984df285712e6e591019a210f64daea Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 08:41:20 +0100 Subject: [PATCH 05/46] Fixed constructor argument --- .../Repository/Events/Token/BeforeCheckTokenEvent.php | 2 +- .../Repository/Events/Token/BeforeGenerateTokenEvent.php | 4 ++-- .../Repository/Events/Token/BeforeGetTokenEvent.php | 2 +- src/contracts/Repository/Events/Token/CheckTokenEvent.php | 2 +- .../Repository/Events/Token/GenerateTokenEvent.php | 2 +- src/contracts/Repository/Events/Token/GetTokenEvent.php | 2 +- src/contracts/Repository/TokenService.php | 2 +- src/contracts/Repository/Values/Token/Token.php | 2 +- src/lib/Event/TokenService.php | 8 ++++---- src/lib/Persistence/Legacy/Token/Handler.php | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php index 84a40eac0d..b2eb5965f2 100644 --- a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -24,7 +24,7 @@ final class BeforeCheckTokenEvent extends BeforeEvent public function __construct( string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ) { $this->tokenType = $tokenType; $this->token = $token; diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php index cb267de081..4b42aec048 100644 --- a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -29,14 +29,14 @@ final class BeforeGenerateTokenEvent extends BeforeEvent public function __construct( string $type, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ) { $this->tokenType = $type; - $this->identifier = $identifier; $this->ttl = $ttl; + $this->identifier = $identifier; $this->tokenLength = $tokenLength; $this->tokenGenerator = $tokenGenerator; } diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php index bf4fa003bc..5fc1687c1a 100644 --- a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -25,7 +25,7 @@ final class BeforeGetTokenEvent extends BeforeEvent public function __construct( string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ) { $this->tokenType = $tokenType; $this->token = $token; diff --git a/src/contracts/Repository/Events/Token/CheckTokenEvent.php b/src/contracts/Repository/Events/Token/CheckTokenEvent.php index 898f0f3bd4..0918a52b5e 100644 --- a/src/contracts/Repository/Events/Token/CheckTokenEvent.php +++ b/src/contracts/Repository/Events/Token/CheckTokenEvent.php @@ -24,7 +24,7 @@ public function __construct( bool $result, string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ) { $this->result = $result; $this->tokenType = $tokenType; diff --git a/src/contracts/Repository/Events/Token/GenerateTokenEvent.php b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php index 11f33fa803..6c60905cdf 100644 --- a/src/contracts/Repository/Events/Token/GenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php @@ -29,8 +29,8 @@ final class GenerateTokenEvent extends AfterEvent public function __construct( Token $token, string $tokenType, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ) { diff --git a/src/contracts/Repository/Events/Token/GetTokenEvent.php b/src/contracts/Repository/Events/Token/GetTokenEvent.php index d8c2686771..9e3ebc0842 100644 --- a/src/contracts/Repository/Events/Token/GetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/GetTokenEvent.php @@ -25,7 +25,7 @@ public function __construct( Token $result, string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ) { $this->result = $result; $this->tokenType = $tokenType; diff --git a/src/contracts/Repository/TokenService.php b/src/contracts/Repository/TokenService.php index 6bd6f54d7c..7f25e1c37d 100644 --- a/src/contracts/Repository/TokenService.php +++ b/src/contracts/Repository/TokenService.php @@ -27,8 +27,8 @@ public function checkToken( public function generateToken( string $type, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ): Token; diff --git a/src/contracts/Repository/Values/Token/Token.php b/src/contracts/Repository/Values/Token/Token.php index dadcbac40b..5d222af1d5 100644 --- a/src/contracts/Repository/Values/Token/Token.php +++ b/src/contracts/Repository/Values/Token/Token.php @@ -19,7 +19,7 @@ final class Token extends ValueObject protected string $token; - protected ?string $identifier; + protected ?string $identifier = null; protected DateTimeImmutable $created; diff --git a/src/lib/Event/TokenService.php b/src/lib/Event/TokenService.php index 44b51b3b6c..5212db37e7 100644 --- a/src/lib/Event/TokenService.php +++ b/src/lib/Event/TokenService.php @@ -22,9 +22,9 @@ use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -class TokenService extends TokenServiceDecorator +final class TokenService extends TokenServiceDecorator { - protected EventDispatcherInterface $eventDispatcher; + private EventDispatcherInterface $eventDispatcher; public function __construct( TokenServiceInterface $innerService, @@ -87,12 +87,12 @@ public function checkToken( public function generateToken( string $type, - ?string $identifier, int $ttl, + ?string $identifier = null, int $tokenLength = 64, ?TokenGeneratorInterface $tokenGenerator = null ): Token { - $eventData = [$type, $identifier, $ttl, $tokenLength, $tokenGenerator]; + $eventData = [$type, $ttl, $identifier, $tokenLength, $tokenGenerator]; $beforeEvent = new BeforeGenerateTokenEvent(...$eventData); diff --git a/src/lib/Persistence/Legacy/Token/Handler.php b/src/lib/Persistence/Legacy/Token/Handler.php index b1776516ce..25132d8794 100644 --- a/src/lib/Persistence/Legacy/Token/Handler.php +++ b/src/lib/Persistence/Legacy/Token/Handler.php @@ -46,7 +46,7 @@ public function __construct( public function getToken( string $tokenType, string $token, - ?string $identifier + ?string $identifier = null ): Token { $token = $this->mapper->mapToken( $this->tokenGateway->getToken($tokenType, $token, $identifier) From 4c7c2f37fd7735d2e6f4161df262576b0a3274d7 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 08:42:33 +0100 Subject: [PATCH 06/46] Added Token/AbstractGateway --- .../Legacy/Token/AbstractGateway.php | 29 +++++++++ .../Token/Doctrine/DoctrineGateway.php | 59 +++++++------------ .../TokenType/Doctrine/DoctrineGateway.php | 37 +++--------- 3 files changed, 57 insertions(+), 68 deletions(-) create mode 100644 src/lib/Persistence/Legacy/Token/AbstractGateway.php diff --git a/src/lib/Persistence/Legacy/Token/AbstractGateway.php b/src/lib/Persistence/Legacy/Token/AbstractGateway.php new file mode 100644 index 0000000000..07b8cedb4d --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/AbstractGateway.php @@ -0,0 +1,29 @@ + $this->getAliasedColumn($column, $alias), + $columns + ); + } + + protected function getAliasedColumn( + string $column, + string $alias + ): string { + return sprintf('%s.%s', $alias, $column); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index 1c44a585b4..006aa0a3af 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -11,11 +11,12 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Core\Base\Exceptions\NotFoundException as NotFound; +use Ibexa\Core\Persistence\Legacy\Token\AbstractGateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Gateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway as TokenTypeGateway; use PDO; -final class DoctrineGateway implements Gateway +final class DoctrineGateway extends AbstractGateway implements Gateway { public const TABLE_NAME = 'ibexa_token'; public const DEFAULT_TABLE_ALIAS = 'token'; @@ -29,14 +30,9 @@ final class DoctrineGateway implements Gateway private Connection $connection; - private TokenTypeGateway $tokenTypeGateway; - - public function __construct( - Connection $connection, - TokenTypeGateway $tokenTypeGateway - ) { + public function __construct(Connection $connection) + { $this->connection = $connection; - $this->tokenTypeGateway = $tokenTypeGateway; } public static function getColumns(): array @@ -132,11 +128,11 @@ public function getTokenById(int $tokenId): array { $query = $this->connection->createQueryBuilder(); $query - ->select(...$this->getAliasedColumns()) + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) ->andWhere( $query->expr()->eq( - $this->getAliasedColumn(self::COLUMN_ID), + $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), ':tokenId' ) ); @@ -152,7 +148,7 @@ public function getTokenById(int $tokenId): array return $row; } - public function getTokenSelectQueryBuilder( + private function getTokenSelectQueryBuilder( string $tokenType, string $token, ?string $identifier = null, @@ -161,26 +157,32 @@ public function getTokenSelectQueryBuilder( $query = $this->connection->createQueryBuilder(); $expr = $query->expr(); $query - ->select(...$this->getAliasedColumns()) + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) ->innerJoin( self::DEFAULT_TABLE_ALIAS, TokenTypeGateway::TABLE_NAME, TokenTypeGateway::DEFAULT_TABLE_ALIAS, $expr->eq( - $this->getAliasedColumn(self::COLUMN_TYPE_ID), - $this->tokenTypeGateway->getAliasedColumn(TokenTypeGateway::COLUMN_ID) + $this->getAliasedColumn(self::COLUMN_TYPE_ID, self::DEFAULT_TABLE_ALIAS), + $this->getAliasedColumn( + TokenTypeGateway::COLUMN_ID, + TokenTypeGateway::DEFAULT_TABLE_ALIAS + ) ) ) ->andWhere( $query->expr()->eq( - $this->getAliasedColumn(self::COLUMN_TOKEN), + $this->getAliasedColumn(self::COLUMN_TOKEN, self::DEFAULT_TABLE_ALIAS), ':token' ) ) ->andWhere( $query->expr()->eq( - $this->tokenTypeGateway->getAliasedColumn(TokenTypeGateway::COLUMN_IDENTIFIER), + $this->getAliasedColumn( + TokenTypeGateway::COLUMN_IDENTIFIER, + TokenTypeGateway::DEFAULT_TABLE_ALIAS + ), ':tokenType' ) ); @@ -188,10 +190,10 @@ public function getTokenSelectQueryBuilder( $query->setParameter(':tokenType', $tokenType, PDO::PARAM_STR); $query->setParameter(':token', $token, PDO::PARAM_STR); - if (!$externalIdentifier) { + if (!$externalIdentifier && null !== $identifier) { $query->andWhere( $query->expr()->eq( - $this->getAliasedColumn(self::COLUMN_IDENTIFIER), + $this->getAliasedColumn(self::COLUMN_IDENTIFIER, self::DEFAULT_TABLE_ALIAS), ':identifier' ) ); @@ -200,25 +202,4 @@ public function getTokenSelectQueryBuilder( return $query; } - - public function getAliasedColumns( - string $alias = self::DEFAULT_TABLE_ALIAS, - array $columns = null - ): array { - if (empty($columns)) { - $columns = self::getColumns(); - } - - return array_map( - fn (string $column) => $this->getAliasedColumn($column, $alias), - $columns - ); - } - - public function getAliasedColumn( - string $column, - string $alias = self::DEFAULT_TABLE_ALIAS - ): string { - return sprintf('%s.%s', $alias, $column); - } } diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index 2f93537521..92a10e49b8 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -10,10 +10,11 @@ use Doctrine\DBAL\Connection; use Ibexa\Core\Base\Exceptions\NotFoundException as NotFound; +use Ibexa\Core\Persistence\Legacy\Token\AbstractGateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway; use PDO; -final class DoctrineGateway implements Gateway +final class DoctrineGateway extends AbstractGateway implements Gateway { public const TABLE_NAME = 'ibexa_token_type'; public const DEFAULT_TABLE_ALIAS = 'token_type'; @@ -23,9 +24,8 @@ final class DoctrineGateway implements Gateway private Connection $connection; - public function __construct( - Connection $connection - ) { + public function __construct(Connection $connection) + { $this->connection = $connection; } @@ -82,11 +82,11 @@ public function getTypeById(int $typeId): array { $query = $this->connection->createQueryBuilder(); $query - ->select(...$this->getAliasedColumns()) + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) ->andWhere( $query->expr()->eq( - $this->getAliasedColumn(self::COLUMN_ID), + $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), ':typeId' ) ); @@ -106,11 +106,11 @@ public function getTypeByIdentifier(string $identifier): array { $query = $this->connection->createQueryBuilder(); $query - ->select(...$this->getAliasedColumns()) + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) ->andWhere( $query->expr()->eq( - $this->getAliasedColumn(self::COLUMN_IDENTIFIER), + $this->getAliasedColumn(self::COLUMN_IDENTIFIER, self::DEFAULT_TABLE_ALIAS), ':identifier' ) ); @@ -125,25 +125,4 @@ public function getTypeByIdentifier(string $identifier): array return $row; } - - public function getAliasedColumns( - string $alias = self::DEFAULT_TABLE_ALIAS, - array $columns = null - ): array { - if (empty($columns)) { - $columns = self::getColumns(); - } - - return array_map( - fn (string $column) => $this->getAliasedColumn($column, $alias), - $columns - ); - } - - public function getAliasedColumn( - string $column, - string $alias = self::DEFAULT_TABLE_ALIAS - ): string { - return sprintf('%s.%s', $alias, $column); - } } From 4346d240ec398dfa3c837d39f7422bd598245036 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 08:43:00 +0100 Subject: [PATCH 07/46] Fixed Token services config --- .../settings/storage_engines/legacy/token.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/lib/Resources/settings/storage_engines/legacy/token.yml b/src/lib/Resources/settings/storage_engines/legacy/token.yml index 23051a30a5..11ace00162 100644 --- a/src/lib/Resources/settings/storage_engines/legacy/token.yml +++ b/src/lib/Resources/settings/storage_engines/legacy/token.yml @@ -1,19 +1,25 @@ services: + _defaults: + autowire: true + autoconfigure: true + public: false + Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway: arguments: $connection: '@ibexa.persistence.connection' - $tokenTypeGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway' + + Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Gateway: + alias: Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway: arguments: $connection: '@ibexa.persistence.connection' + Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway: + alias: Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway + Ibexa\Core\Persistence\Legacy\Token\Mapper: ~ - Ibexa\Core\Persistence\Legacy\Token\Handler: - arguments: - $tokenGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway' - $tokenTypeGateway: '@Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway' - $mapper: '@Ibexa\Core\Persistence\Legacy\Token\Mapper' + Ibexa\Core\Persistence\Legacy\Token\Handler: ~ Ibexa\Contracts\Core\Persistence\Token\Handler: '@Ibexa\Core\Persistence\Legacy\Token\Handler' From 64877973a5a4e8491ce05baeea16a016c24aed0f Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 15 Mar 2023 14:15:21 +0100 Subject: [PATCH 08/46] Reordered token tables in schema --- .../config/storage/legacy/schema.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml index a8c8a2da7b..29d840c63d 100644 --- a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml +++ b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml @@ -638,6 +638,15 @@ tables: group: { type: string, nullable: false, length: 128 } identifier: { type: string, nullable: false, length: 128 } value: { type: json, nullable: false, length: 0 } + ibexa_token_type: + indexes: + ibexa_token_type_id: { fields: [id] } + uniqueConstraints: + ibexa_token_type_unique: { fields: [identifier] } + id: + id: { type: integer, nullable: false, options: { autoincrement: true } } + fields: + identifier: { type: string, nullable: false, length: 32 } ibexa_token: indexes: ibexa_token_id: { fields: [id] } @@ -659,12 +668,3 @@ tables: identifier: { type: string, nullable: true, length: 128 } created: { type: integer, nullable: false, options: { default: '0' } } expires: { type: integer, nullable: false, options: { default: '0' } } - ibexa_token_type: - indexes: - ibexa_token_type_id: { fields: [id] } - uniqueConstraints: - ibexa_token_type_unique: { fields: [identifier] } - id: - id: { type: integer, nullable: false, options: { autoincrement: true } } - fields: - identifier: { type: string, nullable: false, length: 32 } From 96be6c60427cc2686fc700d43b01ae273b1f7103 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 16 Mar 2023 08:06:40 +0100 Subject: [PATCH 09/46] Deleted token.yaml --- src/bundle/Core/Resources/config/token.yml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 src/bundle/Core/Resources/config/token.yml diff --git a/src/bundle/Core/Resources/config/token.yml b/src/bundle/Core/Resources/config/token.yml deleted file mode 100644 index 2b8a2ef68f..0000000000 --- a/src/bundle/Core/Resources/config/token.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false From 0f32b1d1dde6c35b06e331802035b576de8222ea Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 16 Mar 2023 08:28:48 +0100 Subject: [PATCH 10/46] Marked WebSafeGenerator as final --- src/lib/Token/WebSafeGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/Token/WebSafeGenerator.php b/src/lib/Token/WebSafeGenerator.php index 41386a272c..5451919411 100644 --- a/src/lib/Token/WebSafeGenerator.php +++ b/src/lib/Token/WebSafeGenerator.php @@ -10,7 +10,7 @@ use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; -class WebSafeGenerator implements TokenGeneratorInterface +final class WebSafeGenerator implements TokenGeneratorInterface { /** * @throws \Exception From 64aa8cbb44a1b8235dbd3c89f1b6ec36872ad59e Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 08:56:01 +0100 Subject: [PATCH 11/46] Fixed schema --- src/bundle/Core/Resources/config/storage/legacy/schema.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml index 29d840c63d..052b25cb0c 100644 --- a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml +++ b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml @@ -639,8 +639,6 @@ tables: identifier: { type: string, nullable: false, length: 128 } value: { type: json, nullable: false, length: 0 } ibexa_token_type: - indexes: - ibexa_token_type_id: { fields: [id] } uniqueConstraints: ibexa_token_type_unique: { fields: [identifier] } id: @@ -648,8 +646,6 @@ tables: fields: identifier: { type: string, nullable: false, length: 32 } ibexa_token: - indexes: - ibexa_token_id: { fields: [id] } uniqueConstraints: ibexa_token_unique: { fields: [token, identifier, type_id] } foreignKeys: @@ -664,7 +660,7 @@ tables: id: { type: integer, nullable: false, options: { autoincrement: true } } fields: type_id: { type: integer, nullable: false } - token: { type: string, nullable: false, length: 64 } + token: { type: string, nullable: false, length: 255 } identifier: { type: string, nullable: true, length: 128 } created: { type: integer, nullable: false, options: { default: '0' } } expires: { type: integer, nullable: false, options: { default: '0' } } From 50a3a4f7e72c5961c116f40ccbf318b064dd330f Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 09:09:16 +0100 Subject: [PATCH 12/46] Added missing internal annotation --- src/contracts/Persistence/Token/CreateStruct.php | 3 +++ src/contracts/Persistence/Token/Handler.php | 3 +++ src/contracts/Persistence/Token/Token.php | 3 +++ src/contracts/Persistence/Token/TokenType.php | 3 +++ src/lib/Persistence/Legacy/Token/AbstractGateway.php | 3 +++ .../Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php | 3 +++ .../Token/Gateway/TokenType/Doctrine/DoctrineGateway.php | 3 +++ src/lib/Persistence/Legacy/Token/Mapper.php | 3 +++ 8 files changed, 24 insertions(+) diff --git a/src/contracts/Persistence/Token/CreateStruct.php b/src/contracts/Persistence/Token/CreateStruct.php index 69fb84268f..abc93c0bf1 100644 --- a/src/contracts/Persistence/Token/CreateStruct.php +++ b/src/contracts/Persistence/Token/CreateStruct.php @@ -10,6 +10,9 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; +/** + * @internal + */ final class CreateStruct extends ValueObject { public string $type; diff --git a/src/contracts/Persistence/Token/Handler.php b/src/contracts/Persistence/Token/Handler.php index 80d6b1d6b6..9b1e68623d 100644 --- a/src/contracts/Persistence/Token/Handler.php +++ b/src/contracts/Persistence/Token/Handler.php @@ -8,6 +8,9 @@ namespace Ibexa\Contracts\Core\Persistence\Token; +/** + * @internal + */ interface Handler { /** diff --git a/src/contracts/Persistence/Token/Token.php b/src/contracts/Persistence/Token/Token.php index 2ccaa6d7e9..ae8fcdb584 100644 --- a/src/contracts/Persistence/Token/Token.php +++ b/src/contracts/Persistence/Token/Token.php @@ -10,6 +10,9 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; +/** + * @internal + */ final class Token extends ValueObject { public int $id; diff --git a/src/contracts/Persistence/Token/TokenType.php b/src/contracts/Persistence/Token/TokenType.php index 22e6cb521f..ec652679ed 100644 --- a/src/contracts/Persistence/Token/TokenType.php +++ b/src/contracts/Persistence/Token/TokenType.php @@ -10,6 +10,9 @@ use Ibexa\Contracts\Core\Persistence\ValueObject; +/** + * @internal + */ final class TokenType extends ValueObject { public int $id; diff --git a/src/lib/Persistence/Legacy/Token/AbstractGateway.php b/src/lib/Persistence/Legacy/Token/AbstractGateway.php index 07b8cedb4d..4d8b8f9752 100644 --- a/src/lib/Persistence/Legacy/Token/AbstractGateway.php +++ b/src/lib/Persistence/Legacy/Token/AbstractGateway.php @@ -8,6 +8,9 @@ namespace Ibexa\Core\Persistence\Legacy\Token; +/** + * @internal + */ abstract class AbstractGateway { protected function getAliasedColumns( diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index 006aa0a3af..b82dc6bd03 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -16,6 +16,9 @@ use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway as TokenTypeGateway; use PDO; +/** + * @internal + */ final class DoctrineGateway extends AbstractGateway implements Gateway { public const TABLE_NAME = 'ibexa_token'; diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index 92a10e49b8..a0c4e4d5fe 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -14,6 +14,9 @@ use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway; use PDO; +/** + * @internal + */ final class DoctrineGateway extends AbstractGateway implements Gateway { public const TABLE_NAME = 'ibexa_token_type'; diff --git a/src/lib/Persistence/Legacy/Token/Mapper.php b/src/lib/Persistence/Legacy/Token/Mapper.php index ff0f8e26b0..9b52479ae1 100644 --- a/src/lib/Persistence/Legacy/Token/Mapper.php +++ b/src/lib/Persistence/Legacy/Token/Mapper.php @@ -11,6 +11,9 @@ use Ibexa\Contracts\Core\Persistence\Token\Token; use Ibexa\Contracts\Core\Persistence\Token\TokenType; +/** + * @internal + */ final class Mapper { public function mapToken(array $tokenRow): Token From 4a63a6eb848da3c87700f0d0393860de76e2592e Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 09:23:28 +0100 Subject: [PATCH 13/46] Fixed TokenService --- src/lib/Repository/TokenService.php | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php index 54445fdbd9..cbd33ac378 100644 --- a/src/lib/Repository/TokenService.php +++ b/src/lib/Repository/TokenService.php @@ -11,8 +11,8 @@ use DateTimeImmutable; use Ibexa\Contracts\Core\Persistence\Token\CreateStruct; use Ibexa\Contracts\Core\Persistence\Token\Handler; -use Ibexa\Contracts\Core\Persistence\Token\Token as SPIToken; -use Ibexa\Contracts\Core\Persistence\Token\TokenType as SPITokenType; +use Ibexa\Contracts\Core\Persistence\Token\Token as PersistenceTokenValue; +use Ibexa\Contracts\Core\Persistence\Token\TokenType as PersistenceTokenType; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\TokenService as TokenServiceInterface; use Ibexa\Contracts\Core\Repository\Values\Token\Token; @@ -42,14 +42,13 @@ public function getToken( ?string $identifier = null ): Token { $type = $this->persistenceHandler->getTokenType($tokenType); - $token = $this->persistenceHandler->getToken( - $type->identifier, - $token, - $identifier - ); return $this->buildDomainObject( - $token, + $this->persistenceHandler->getToken( + $type->identifier, + $token, + $identifier + ), $type ); } @@ -64,9 +63,8 @@ public function checkToken( return true; } catch (NotFoundException|TokenExpiredException $exception) { + return false; } - - return false; } public function generateToken( @@ -101,8 +99,8 @@ public function deleteToken(Token $token): void * @throws \Exception */ private function buildDomainObject( - SPIToken $spiToken, - SPITokenType $spiTokenType + PersistenceTokenValue $spiToken, + PersistenceTokenType $spiTokenType ): Token { return new Token([ 'id' => $spiToken->id, From 357ca05ef367d8a18d70d60acdf2160bdf398b5d Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 09:29:30 +0100 Subject: [PATCH 14/46] Renamed token variable in Handler::getToken to avoid override --- src/lib/Persistence/Legacy/Token/Handler.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Handler.php b/src/lib/Persistence/Legacy/Token/Handler.php index 25132d8794..beff925b9a 100644 --- a/src/lib/Persistence/Legacy/Token/Handler.php +++ b/src/lib/Persistence/Legacy/Token/Handler.php @@ -48,19 +48,19 @@ public function getToken( string $token, ?string $identifier = null ): Token { - $token = $this->mapper->mapToken( + $persistenceTokenValue = $this->mapper->mapToken( $this->tokenGateway->getToken($tokenType, $token, $identifier) ); - if ($token->expires < time()) { + if ($persistenceTokenValue->expires < time()) { throw new TokenExpiredException( $tokenType, - $token->token, - new DateTimeImmutable('@' . $token->expires) + $persistenceTokenValue->token, + new DateTimeImmutable('@' . $persistenceTokenValue->expires) ); } - return $token; + return $persistenceTokenValue; } /** From 8540a49672feddbee00cc2de1a5235b6db5b744b Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 12:35:19 +0100 Subject: [PATCH 15/46] Used \Doctrine\DBAL\ParameterType instead of PDO --- .../Token/Doctrine/DoctrineGateway.php | 28 +++++++++---------- .../TokenType/Doctrine/DoctrineGateway.php | 12 ++++---- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index b82dc6bd03..afe5d985c1 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -9,12 +9,12 @@ namespace Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; use Ibexa\Core\Base\Exceptions\NotFoundException as NotFound; use Ibexa\Core\Persistence\Legacy\Token\AbstractGateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Gateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway as TokenTypeGateway; -use PDO; /** * @internal @@ -67,11 +67,11 @@ public function insert( self::COLUMN_CREATED => ':created', self::COLUMN_EXPIRES => ':expires', ]) - ->setParameter(':type_id', $typeId, PDO::PARAM_INT) - ->setParameter(':token', $token, PDO::PARAM_STR) - ->setParameter(':identifier', $identifier, PDO::PARAM_STR) - ->setParameter(':created', $now, PDO::PARAM_INT) - ->setParameter(':expires', $now + $ttl, PDO::PARAM_INT); + ->setParameter(':type_id', $typeId, ParameterType::INTEGER) + ->setParameter(':token', $token, ParameterType::STRING) + ->setParameter(':identifier', $identifier, ParameterType::STRING) + ->setParameter(':created', $now, ParameterType::INTEGER) + ->setParameter(':expires', $now + $ttl, ParameterType::INTEGER); $query->execute(); @@ -84,7 +84,7 @@ public function delete(int $tokenId): void $query ->delete(self::TABLE_NAME) ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $tokenId, PDO::PARAM_INT); + ->setParameter(':id', $tokenId, ParameterType::INTEGER); $query->execute(); } @@ -97,16 +97,16 @@ public function deleteExpired(?int $typeId = null): void ->andWhere( $query->expr()->lt(self::COLUMN_EXPIRES, ':now') ) - ->setParameter(':now', time(), PDO::PARAM_INT); + ->setParameter(':now', time(), ParameterType::INTEGER); - if (!empty($typeId)) { + if (null !== $typeId) { $query->andWhere( $query->expr()->eq( self::COLUMN_TYPE_ID, ':type_id' ) ); - $query->setParameter(':type_id', $typeId, PDO::PARAM_INT); + $query->setParameter(':type_id', $typeId, ParameterType::INTEGER); } $query->execute(); @@ -140,7 +140,7 @@ public function getTokenById(int $tokenId): array ) ); - $query->setParameter(':tokenId', $tokenId, PDO::PARAM_INT); + $query->setParameter(':tokenId', $tokenId, ParameterType::INTEGER); $row = $query->execute()->fetchAssociative(); @@ -190,8 +190,8 @@ private function getTokenSelectQueryBuilder( ) ); - $query->setParameter(':tokenType', $tokenType, PDO::PARAM_STR); - $query->setParameter(':token', $token, PDO::PARAM_STR); + $query->setParameter(':tokenType', $tokenType, ParameterType::STRING); + $query->setParameter(':token', $token, ParameterType::STRING); if (!$externalIdentifier && null !== $identifier) { $query->andWhere( @@ -200,7 +200,7 @@ private function getTokenSelectQueryBuilder( ':identifier' ) ); - $query->setParameter(':identifier', $identifier, PDO::PARAM_STR); + $query->setParameter(':identifier', $identifier, ParameterType::STRING); } return $query; diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index a0c4e4d5fe..7d01875a13 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -9,10 +9,10 @@ namespace Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ParameterType; use Ibexa\Core\Base\Exceptions\NotFoundException as NotFound; use Ibexa\Core\Persistence\Legacy\Token\AbstractGateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway; -use PDO; /** * @internal @@ -46,7 +46,7 @@ public function insert(string $identifier): int $query ->insert(self::TABLE_NAME) ->values([self::COLUMN_IDENTIFIER => ':identifier']) - ->setParameter(':identifier', $identifier, PDO::PARAM_STR); + ->setParameter(':identifier', $identifier, ParameterType::STRING); $query->execute(); @@ -62,7 +62,7 @@ public function deleteById(int $typeId): void $query ->delete(self::TABLE_NAME) ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $typeId, PDO::PARAM_INT); + ->setParameter(':id', $typeId, ParameterType::STRING); $query->execute(); } @@ -76,7 +76,7 @@ public function deleteByIdentifier(string $identifier): void $query ->delete(self::TABLE_NAME) ->where($query->expr()->eq(self::COLUMN_IDENTIFIER, ':identifier')) - ->setParameter(':identifier', $identifier, PDO::PARAM_STR); + ->setParameter(':identifier', $identifier, ParameterType::STRING); $query->execute(); } @@ -94,7 +94,7 @@ public function getTypeById(int $typeId): array ) ); - $query->setParameter(':typeId', $typeId, PDO::PARAM_INT); + $query->setParameter(':typeId', $typeId, ParameterType::INTEGER); $row = $query->execute()->fetchAssociative(); @@ -118,7 +118,7 @@ public function getTypeByIdentifier(string $identifier): array ) ); - $query->setParameter(':identifier', $identifier, PDO::PARAM_STR); + $query->setParameter(':identifier', $identifier, ParameterType::STRING); $row = $query->execute()->fetchAssociative(); From f9984fb827d49c591934de88339fab7ae07ca834 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 13:11:04 +0100 Subject: [PATCH 16/46] Fixed CS - Exception message line too long --- .../Repository/Events/Token/BeforeCheckTokenEvent.php | 5 ++++- .../Repository/Events/Token/BeforeGenerateTokenEvent.php | 8 +++++++- .../Repository/Events/Token/BeforeGetTokenEvent.php | 8 +++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php index b2eb5965f2..f947225878 100644 --- a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -34,7 +34,10 @@ public function __construct( public function getResult(): bool { if (!$this->hasResult()) { - throw new UnexpectedValueException('Return value is not set or not of type boolean. Check hasResult() or set it using setResult() before you call the getter.'); + throw new UnexpectedValueException( + 'Return value is not set or not of type boolean. + Check hasResult() or set it using setResult() before you call the getter.' + ); } return $this->result; diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php index 4b42aec048..fd2b2944da 100644 --- a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -44,7 +44,13 @@ public function __construct( public function getToken(): Token { if (!$this->hasToken()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasToken() or set it using setToken() before you call the getter.', Token::class)); + throw new UnexpectedValueException( + sprintf( + 'Return value is not set or not of type %s. + Check hasToken() or set it using setToken() before you call the getter.', + Token::class + ) + ); } return $this->token; diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php index 5fc1687c1a..81e2b6c11a 100644 --- a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -35,7 +35,13 @@ public function __construct( public function getResult(): Token { if (!$this->hasResult()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', Token::class)); + throw new UnexpectedValueException( + sprintf( + 'Return value is not set or not of type %s. + Check hasResult() or set it using setResult() before you call the getter.', + Token::class + ) + ); } return $this->result; From 69b725cd26af9ce8c7b3620561d8bb2851ea1dc2 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 14:01:02 +0100 Subject: [PATCH 17/46] Changed Repository\Token properties to private --- .../Repository/Values/Token/Token.php | 42 ++++++++++++++++--- src/lib/Repository/TokenService.php | 6 +-- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/contracts/Repository/Values/Token/Token.php b/src/contracts/Repository/Values/Token/Token.php index 5d222af1d5..28e553ce75 100644 --- a/src/contracts/Repository/Values/Token/Token.php +++ b/src/contracts/Repository/Values/Token/Token.php @@ -13,17 +13,35 @@ final class Token extends ValueObject { - protected int $id; + private int $id; - protected string $type; + private string $type; - protected string $token; + private string $token; - protected ?string $identifier = null; + private ?string $identifier; - protected DateTimeImmutable $created; + private DateTimeImmutable $created; - protected DateTimeImmutable $expires; + private DateTimeImmutable $expires; + + public function __construct( + int $id, + string $type, + string $token, + ?string $identifier, + DateTimeImmutable $created, + DateTimeImmutable $expires + ) { + parent::__construct(); + + $this->id = $id; + $this->type = $type; + $this->token = $token; + $this->identifier = $identifier; + $this->created = $created; + $this->expires = $expires; + } public function getId(): int { @@ -59,4 +77,16 @@ public function __toString(): string { return $this->token; } + + public static function fromArray(array $properties): self + { + return new self( + $properties['id'], + $properties['type'], + $properties['token'], + $properties['identifier'], + $properties['created'], + $properties['expires'], + ); + } } diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php index cbd33ac378..b11670c135 100644 --- a/src/lib/Repository/TokenService.php +++ b/src/lib/Repository/TokenService.php @@ -12,7 +12,7 @@ use Ibexa\Contracts\Core\Persistence\Token\CreateStruct; use Ibexa\Contracts\Core\Persistence\Token\Handler; use Ibexa\Contracts\Core\Persistence\Token\Token as PersistenceTokenValue; -use Ibexa\Contracts\Core\Persistence\Token\TokenType as PersistenceTokenType; +use Ibexa\Contracts\Core\Persistence\Token\TokenType as PersistenceTokenTypeValue; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Contracts\Core\Repository\TokenService as TokenServiceInterface; use Ibexa\Contracts\Core\Repository\Values\Token\Token; @@ -100,9 +100,9 @@ public function deleteToken(Token $token): void */ private function buildDomainObject( PersistenceTokenValue $spiToken, - PersistenceTokenType $spiTokenType + PersistenceTokenTypeValue $spiTokenType ): Token { - return new Token([ + return Token::fromArray([ 'id' => $spiToken->id, 'type' => $spiTokenType->identifier, 'token' => $spiToken->token, From 5b606805e84fc76f066756122f374a9fedcd2c5d Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 14:36:18 +0100 Subject: [PATCH 18/46] [PHPDOC] Fixed throws annotations for Handler --- src/contracts/Persistence/Token/Handler.php | 3 ++- src/lib/Persistence/Legacy/Token/Handler.php | 9 ++++++--- src/lib/Repository/TokenService.php | 14 +++++++++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/contracts/Persistence/Token/Handler.php b/src/contracts/Persistence/Token/Handler.php index 9b1e68623d..43be40af12 100644 --- a/src/contracts/Persistence/Token/Handler.php +++ b/src/contracts/Persistence/Token/Handler.php @@ -14,7 +14,8 @@ interface Handler { /** - * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function getToken( string $tokenType, diff --git a/src/lib/Persistence/Legacy/Token/Handler.php b/src/lib/Persistence/Legacy/Token/Handler.php index beff925b9a..bc94324617 100644 --- a/src/lib/Persistence/Legacy/Token/Handler.php +++ b/src/lib/Persistence/Legacy/Token/Handler.php @@ -18,6 +18,9 @@ use Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Gateway as TokenGateway; use Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway as TokenTypeGateway; +/** + * @internal + */ final class Handler implements HandlerInterface { private Mapper $mapper; @@ -37,10 +40,10 @@ public function __construct( } /** - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException * @throws \Doctrine\DBAL\Driver\Exception * @throws \Doctrine\DBAL\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException * @throws \Exception */ public function getToken( @@ -96,7 +99,7 @@ public function createToken(CreateStruct $createStruct): Token $createStruct->ttl ); - return $this->mapper->mapToken( + return $this->mapper->mapToken( $this->tokenGateway->getTokenById($tokenId) ); } diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php index b11670c135..f57819eff9 100644 --- a/src/lib/Repository/TokenService.php +++ b/src/lib/Repository/TokenService.php @@ -14,10 +14,10 @@ use Ibexa\Contracts\Core\Persistence\Token\Token as PersistenceTokenValue; use Ibexa\Contracts\Core\Persistence\Token\TokenType as PersistenceTokenTypeValue; use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; use Ibexa\Contracts\Core\Repository\TokenService as TokenServiceInterface; use Ibexa\Contracts\Core\Repository\Values\Token\Token; use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; -use Ibexa\Core\Base\Exceptions\TokenExpiredException; final class TokenService implements TokenServiceInterface { @@ -34,7 +34,9 @@ public function __construct( } /** - * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Exception */ public function getToken( string $tokenType, @@ -53,6 +55,9 @@ public function getToken( ); } + /** + * @throws \Exception + */ public function checkToken( string $tokenType, string $token, @@ -62,11 +67,14 @@ public function checkToken( $this->getToken($tokenType, $token, $identifier); return true; - } catch (NotFoundException|TokenExpiredException $exception) { + } catch (NotFoundException|UnauthorizedException $exception) { return false; } } + /** + * @throws \Exception + */ public function generateToken( string $type, int $ttl, From 62ad31465bdadea18e1995434f20f94b5797b1d2 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Wed, 22 Mar 2023 15:01:27 +0100 Subject: [PATCH 19/46] Fixed Gateways --- .../Token/Doctrine/DoctrineGateway.php | 22 +++++++++++-------- .../TokenType/Doctrine/DoctrineGateway.php | 4 ++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index afe5d985c1..d55c771eb6 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -56,7 +56,7 @@ public function insert( ?string $identifier, int $ttl ): int { - $now = time(); + $now = $this->getCurrentUnixTimestamp(); $query = $this->connection->createQueryBuilder(); $query ->insert(self::TABLE_NAME) @@ -97,7 +97,7 @@ public function deleteExpired(?int $typeId = null): void ->andWhere( $query->expr()->lt(self::COLUMN_EXPIRES, ':now') ) - ->setParameter(':now', time(), ParameterType::INTEGER); + ->setParameter(':now', $this->getCurrentUnixTimestamp(), ParameterType::INTEGER); if (null !== $typeId) { $query->andWhere( @@ -136,11 +136,11 @@ public function getTokenById(int $tokenId): array ->andWhere( $query->expr()->eq( $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), - ':tokenId' + ':token_id' ) ); - $query->setParameter(':tokenId', $tokenId, ParameterType::INTEGER); + $query->setParameter(':token_id', $tokenId, ParameterType::INTEGER); $row = $query->execute()->fetchAssociative(); @@ -151,11 +151,15 @@ public function getTokenById(int $tokenId): array return $row; } + private function getCurrentUnixTimestamp(): int + { + return time(); + } + private function getTokenSelectQueryBuilder( string $tokenType, string $token, - ?string $identifier = null, - bool $externalIdentifier = false + ?string $identifier = null ): QueryBuilder { $query = $this->connection->createQueryBuilder(); $expr = $query->expr(); @@ -186,14 +190,14 @@ private function getTokenSelectQueryBuilder( TokenTypeGateway::COLUMN_IDENTIFIER, TokenTypeGateway::DEFAULT_TABLE_ALIAS ), - ':tokenType' + ':token_type' ) ); - $query->setParameter(':tokenType', $tokenType, ParameterType::STRING); + $query->setParameter(':token_type', $tokenType, ParameterType::STRING); $query->setParameter(':token', $token, ParameterType::STRING); - if (!$externalIdentifier && null !== $identifier) { + if (null !== $identifier) { $query->andWhere( $query->expr()->eq( $this->getAliasedColumn(self::COLUMN_IDENTIFIER, self::DEFAULT_TABLE_ALIAS), diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index 7d01875a13..6099f886ad 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -90,11 +90,11 @@ public function getTypeById(int $typeId): array ->andWhere( $query->expr()->eq( $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), - ':typeId' + ':type_id' ) ); - $query->setParameter(':typeId', $typeId, ParameterType::INTEGER); + $query->setParameter(':type_id', $typeId, ParameterType::INTEGER); $row = $query->execute()->fetchAssociative(); From 0bc89b7dd7713d8f82fa648263a66683f55dbc18 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 23 Mar 2023 08:23:53 +0100 Subject: [PATCH 20/46] Added new line to exception message to avoid too long single line --- .../Repository/Events/Token/BeforeCheckTokenEvent.php | 4 ++-- .../Repository/Events/Token/BeforeGenerateTokenEvent.php | 4 ++-- src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php index f947225878..ed758de39f 100644 --- a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -35,8 +35,8 @@ public function getResult(): bool { if (!$this->hasResult()) { throw new UnexpectedValueException( - 'Return value is not set or not of type boolean. - Check hasResult() or set it using setResult() before you call the getter.' + 'Return value is not set or not of type boolean.' . PHP_EOL + . 'Check hasResult() or set it using setResult() before you call the getter.' ); } diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php index fd2b2944da..f2b8aa7d9e 100644 --- a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -46,8 +46,8 @@ public function getToken(): Token if (!$this->hasToken()) { throw new UnexpectedValueException( sprintf( - 'Return value is not set or not of type %s. - Check hasToken() or set it using setToken() before you call the getter.', + 'Return value is not set or not of type %s.' . PHP_EOL + . 'Check hasToken() or set it using setToken() before you call the getter.', Token::class ) ); diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php index 81e2b6c11a..c22c2c7c61 100644 --- a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -37,8 +37,8 @@ public function getResult(): Token if (!$this->hasResult()) { throw new UnexpectedValueException( sprintf( - 'Return value is not set or not of type %s. - Check hasResult() or set it using setResult() before you call the getter.', + 'Return value is not set or not of type %s.' . PHP_EOL + . 'Check hasResult() or set it using setResult() before you call the getter.', Token::class ) ); From 8710d8578973976a1b5df83c634bf9ba6b2c34b5 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 23 Mar 2023 09:52:32 +0100 Subject: [PATCH 21/46] Added WebSafeGeneratorTest --- tests/lib/Token/WebSafeGeneratorTest.php | 63 ++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 tests/lib/Token/WebSafeGeneratorTest.php diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php new file mode 100644 index 0000000000..0ce3f5321d --- /dev/null +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -0,0 +1,63 @@ +tokenGenerator = new WebSafeGenerator(); + } + + /** + * @dataProvider provideDataForTestGenerateToken + * + * @throws \Exception + */ + public function testGenerateToken(int $expectedTokenLength): void + { + $token = $this->tokenGenerator->generateToken($expectedTokenLength); + + // Check if Generator returns different tokens + self::assertNotSame( + $token, + $this->tokenGenerator->generateToken($expectedTokenLength) + ); + + self::assertSame( + $expectedTokenLength, + strlen($token) + ); + } + + /** + * @return iterable + */ + public function provideDataForTestGenerateToken(): iterable + { + yield [20]; + + yield [64]; + + yield [100]; + + yield [256]; + } +} From 8363aeb13e3ce98f2d1812a870cb1bac0852b703 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Thu, 23 Mar 2023 13:06:38 +0100 Subject: [PATCH 22/46] Added token length validation to avoid generate too long token --- src/contracts/Repository/TokenService.php | 3 ++ .../Repository/Values/Token/Token.php | 2 ++ .../Base/Exceptions/TokenLengthException.php | 32 +++++++++++++++++++ src/lib/Repository/TokenService.php | 22 ++++++++----- 4 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 src/lib/Base/Exceptions/TokenLengthException.php diff --git a/src/contracts/Repository/TokenService.php b/src/contracts/Repository/TokenService.php index 7f25e1c37d..aebd8a7730 100644 --- a/src/contracts/Repository/TokenService.php +++ b/src/contracts/Repository/TokenService.php @@ -25,6 +25,9 @@ public function checkToken( ?string $identifier = null ): bool; + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ public function generateToken( string $type, int $ttl, diff --git a/src/contracts/Repository/Values/Token/Token.php b/src/contracts/Repository/Values/Token/Token.php index 28e553ce75..ae3a7ac7fb 100644 --- a/src/contracts/Repository/Values/Token/Token.php +++ b/src/contracts/Repository/Values/Token/Token.php @@ -13,6 +13,8 @@ final class Token extends ValueObject { + public const MAX_LENGTH = 255; + private int $id; private string $type; diff --git a/src/lib/Base/Exceptions/TokenLengthException.php b/src/lib/Base/Exceptions/TokenLengthException.php new file mode 100644 index 0000000000..f3507ddec3 --- /dev/null +++ b/src/lib/Base/Exceptions/TokenLengthException.php @@ -0,0 +1,32 @@ + Token::MAX_LENGTH) { + throw new TokenLengthException($tokenLength); + } + $createStruct = new CreateStruct([ 'type' => $type, 'token' => ($tokenGenerator ?? $this->defaultTokenGenerator)->generateToken($tokenLength), @@ -107,16 +113,16 @@ public function deleteToken(Token $token): void * @throws \Exception */ private function buildDomainObject( - PersistenceTokenValue $spiToken, - PersistenceTokenTypeValue $spiTokenType + PersistenceTokenValue $token, + PersistenceTokenTypeValue $tokenType ): Token { return Token::fromArray([ - 'id' => $spiToken->id, - 'type' => $spiTokenType->identifier, - 'token' => $spiToken->token, - 'identifier' => $spiToken->identifier, - 'created' => new DateTimeImmutable('@' . $spiToken->created), - 'expires' => new DateTimeImmutable('@' . $spiToken->expires), + 'id' => $token->id, + 'type' => $tokenType->identifier, + 'token' => $token->token, + 'identifier' => $token->identifier, + 'created' => new DateTimeImmutable('@' . $token->created), + 'expires' => new DateTimeImmutable('@' . $token->expires), ]); } } From e9c6d29fa1241e6620f7337f854fcc9ccf46558e Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 28 Mar 2023 08:35:40 +0200 Subject: [PATCH 23/46] Added sequence name for last inserted ids --- .../Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php | 3 ++- .../Token/Gateway/TokenType/Doctrine/DoctrineGateway.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index d55c771eb6..16cbe1cb3f 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -30,6 +30,7 @@ final class DoctrineGateway extends AbstractGateway implements Gateway public const COLUMN_IDENTIFIER = 'identifier'; public const COLUMN_CREATED = 'created'; public const COLUMN_EXPIRES = 'expires'; + public const TOKEN_SEQ = 'ibexa_token_id_seq'; private Connection $connection; @@ -75,7 +76,7 @@ public function insert( $query->execute(); - return (int)$this->connection->lastInsertId(); + return (int)$this->connection->lastInsertId(self::TOKEN_SEQ); } public function delete(int $tokenId): void diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index 6099f886ad..b2313e0ece 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -24,6 +24,7 @@ final class DoctrineGateway extends AbstractGateway implements Gateway public const COLUMN_ID = 'id'; public const COLUMN_IDENTIFIER = 'identifier'; + public const TOKEN_TYPE_SEQ = 'ibexa_token_type_id_seq'; private Connection $connection; @@ -50,7 +51,7 @@ public function insert(string $identifier): int $query->execute(); - return (int)$this->connection->lastInsertId(); + return (int)$this->connection->lastInsertId(self::TOKEN_TYPE_SEQ); } /** From a2ea0fa5cd27c5db7f0c3d61158948cdb32d68cb Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 28 Mar 2023 11:23:22 +0200 Subject: [PATCH 24/46] Added TokenServiceTest --- .../Core/Repository/TokenServiceTest.php | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 tests/integration/Core/Repository/TokenServiceTest.php diff --git a/tests/integration/Core/Repository/TokenServiceTest.php b/tests/integration/Core/Repository/TokenServiceTest.php new file mode 100644 index 0000000000..b7fdbd65ea --- /dev/null +++ b/tests/integration/Core/Repository/TokenServiceTest.php @@ -0,0 +1,185 @@ +tokenService = $this->getTokenService(); + } + + /** + * @dataProvider provideTokenData + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGenerateToken( + string $type, + int $tll, + int $length = 64, + ?string $identifier = null + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); + + self::assertSame($type, $token->getType()); + self::assertSame($identifier, $token->getIdentifier()); + self::assertSame($length, strlen($token->getToken())); + } + + /** + * @dataProvider provideDataForTestCheckToken + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testCheckExistingToken( + string $type, + int $tll, + ?string $identifier = null + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier); + + self::assertTrue( + $this->tokenService->checkToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ) + ); + } + + public function testCheckNotExistentToken(): void + { + self::assertFalse( + $this->tokenService->checkToken( + 'bar', + '1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,', + 'test' + ) + ); + } + + /** + * @dataProvider provideTokenData + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGetToken( + string $type, + int $tll, + int $length = 64, + ?string $identifier = null + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); + + self::assertEquals( + $token, + $this->tokenService->getToken($type, $token->getToken(), $identifier) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testDeleteToken(): void + { + $token = $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ); + + $this->tokenService->deleteToken($token); + + self::assertFalse( + $this->tokenService->checkToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ) + ); + } + + /** + * @return iterable + */ + public function provideTokenData(): iterable + { + yield 'Token with default length 64 and custom identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + 64, + self::TOKEN_IDENTIFIER, + ]; + + yield 'Token with length 200 and custom identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + 200, + self::TOKEN_IDENTIFIER, + ]; + + yield 'Token without identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + ]; + } + + /** + * @return iterable + */ + public function provideDataForTestCheckToken(): iterable + { + yield 'Token with identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + ]; + + yield 'Token without identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + ]; + } + + /** + * @throws \ErrorException + */ + private function getTokenService(): TokenService + { + $container = $this->getSetupFactory()->getServiceContainer(); + + /** @var \Ibexa\Contracts\Core\Repository\TokenService $tokenService */ + $tokenService = $container->get(TokenService::class); + + return $tokenService; + } +} From 3528b5eee6d6d9d73065c5d619128ead5a4db147 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 28 Mar 2023 11:33:25 +0200 Subject: [PATCH 25/46] Added test to cover throwing TokenLengthException --- .../Core/Repository/TokenServiceTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/integration/Core/Repository/TokenServiceTest.php b/tests/integration/Core/Repository/TokenServiceTest.php index b7fdbd65ea..92e91dec48 100644 --- a/tests/integration/Core/Repository/TokenServiceTest.php +++ b/tests/integration/Core/Repository/TokenServiceTest.php @@ -9,6 +9,7 @@ namespace Ibexa\Tests\Integration\Core\Repository; use Ibexa\Contracts\Core\Repository\TokenService; +use Ibexa\Core\Base\Exceptions\TokenLengthException; /** * @covers \Ibexa\Core\Repository\TokenService @@ -46,6 +47,24 @@ public function testGenerateToken( self::assertSame($length, strlen($token->getToken())); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGenerateTokenThrowsTokenLengthException(): void + { + $length = 300; + + $this->expectException(TokenLengthException::class); + $this->expectExceptionMessage('Token length is too long: 300 characters. Max length is 255.'); + + $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + $length + ); + } + /** * @dataProvider provideDataForTestCheckToken * From b8486805efb84d2b57fe8a7ed4f1ce18943cd71d Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:16:58 +0200 Subject: [PATCH 26/46] Update src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- .../Token/Doctrine/DoctrineGateway.php | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index 16cbe1cb3f..3fd8689836 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -58,23 +58,20 @@ public function insert( int $ttl ): int { $now = $this->getCurrentUnixTimestamp(); - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::TABLE_NAME) - ->values([ - self::COLUMN_TYPE_ID => ':type_id', - self::COLUMN_TOKEN => ':token', - self::COLUMN_IDENTIFIER => ':identifier', - self::COLUMN_CREATED => ':created', - self::COLUMN_EXPIRES => ':expires', - ]) - ->setParameter(':type_id', $typeId, ParameterType::INTEGER) - ->setParameter(':token', $token, ParameterType::STRING) - ->setParameter(':identifier', $identifier, ParameterType::STRING) - ->setParameter(':created', $now, ParameterType::INTEGER) - ->setParameter(':expires', $now + $ttl, ParameterType::INTEGER); - - $query->execute(); + $this->connection->insert( + self::TABLE_NAME, + [ + self::COLUMN_TYPE_ID => $typeId, + self::COLUMN_TOKEN => $token, + self::COLUMN_IDENTIFIER => $identifier, + self::COLUMN_CREATED => $now, + self::COLUMN_EXPIRES => $now + $ttl, + ], [ + self::COLUMN_TYPE_ID => ParameterType::INTEGER, + self::COLUMN_CREATED => ParameterType::INTEGER, + self::COLUMN_EXPIRES => ParameterType::INTEGER, + ] + ); return (int)$this->connection->lastInsertId(self::TOKEN_SEQ); } From 20eb642387b9227e9a5c8d792b8826da72fd8300 Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:18:40 +0200 Subject: [PATCH 27/46] Update src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- .../Gateway/Token/Doctrine/DoctrineGateway.php | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index 3fd8689836..d83349a149 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -78,13 +78,14 @@ public function insert( public function delete(int $tokenId): void { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::TABLE_NAME) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $tokenId, ParameterType::INTEGER); - - $query->execute(); + $this->connection->delete( + self::TABLE_NAME, + [ + self::COLUMN_ID => $tokenId, + ], [ + self::COLUMN_ID => ParameterType::INTEGER, + ] + ); } public function deleteExpired(?int $typeId = null): void From 4e494131a4fd51bc4d2f22bfeeb83926f11a8030 Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:18:59 +0200 Subject: [PATCH 28/46] Update src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- .../Gateway/TokenType/Doctrine/DoctrineGateway.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index b2313e0ece..baf5f39b4b 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -43,13 +43,9 @@ public static function getColumns(): array public function insert(string $identifier): int { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::TABLE_NAME) - ->values([self::COLUMN_IDENTIFIER => ':identifier']) - ->setParameter(':identifier', $identifier, ParameterType::STRING); - - $query->execute(); + $this->connection->insert(self::TABLE_NAME, [ + self::COLUMN_IDENTIFIER => $identifier, + ]); return (int)$this->connection->lastInsertId(self::TOKEN_TYPE_SEQ); } From 7e803d0cd1e15b4d5bd9060b66e729682a5163b5 Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:19:25 +0200 Subject: [PATCH 29/46] Update src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- .../Gateway/TokenType/Doctrine/DoctrineGateway.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index baf5f39b4b..f52728a4e6 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -55,13 +55,9 @@ public function insert(string $identifier): int */ public function deleteById(int $typeId): void { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::TABLE_NAME) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $typeId, ParameterType::STRING); - - $query->execute(); + $this->connection->delete(self::TABLE_NAME, [ + self::COLUMN_ID => $typeId, + ]); } /** From 334e597c26b8f536f2ed0f8ba4b885fbf30b7a3e Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:19:52 +0200 Subject: [PATCH 30/46] Update src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- .../Gateway/TokenType/Doctrine/DoctrineGateway.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index f52728a4e6..5ea71d4db8 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -65,13 +65,9 @@ public function deleteById(int $typeId): void */ public function deleteByIdentifier(string $identifier): void { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::TABLE_NAME) - ->where($query->expr()->eq(self::COLUMN_IDENTIFIER, ':identifier')) - ->setParameter(':identifier', $identifier, ParameterType::STRING); - - $query->execute(); + $this->connection->delete(self::TABLE_NAME, [ + self::COLUMN_IDENTIFIER => $typeId, + ]); } public function getTypeById(int $typeId): array From e87468ed3e2157d49afe8bfd2b77eca4d1e8e924 Mon Sep 17 00:00:00 2001 From: ciastektk Date: Fri, 31 Mar 2023 15:23:51 +0200 Subject: [PATCH 31/46] Update src/bundle/Core/Resources/config/storage/legacy/schema.yaml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- src/bundle/Core/Resources/config/storage/legacy/schema.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml index 052b25cb0c..d67cd96eee 100644 --- a/src/bundle/Core/Resources/config/storage/legacy/schema.yaml +++ b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml @@ -655,7 +655,6 @@ tables: foreignFields: [id] options: onDelete: CASCADE - onUpdate: 'NO ACTION' id: id: { type: integer, nullable: false, options: { autoincrement: true } } fields: From 626936f84556b9f17df666f5d1517663112c4a45 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Fri, 31 Mar 2023 15:30:04 +0200 Subject: [PATCH 32/46] [Code style] Fixed CS --- .../Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php | 6 ++++-- .../Token/Gateway/TokenType/Doctrine/DoctrineGateway.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php index d83349a149..96be5225fc 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -66,7 +66,8 @@ public function insert( self::COLUMN_IDENTIFIER => $identifier, self::COLUMN_CREATED => $now, self::COLUMN_EXPIRES => $now + $ttl, - ], [ + ], + [ self::COLUMN_TYPE_ID => ParameterType::INTEGER, self::COLUMN_CREATED => ParameterType::INTEGER, self::COLUMN_EXPIRES => ParameterType::INTEGER, @@ -82,7 +83,8 @@ public function delete(int $tokenId): void self::TABLE_NAME, [ self::COLUMN_ID => $tokenId, - ], [ + ], + [ self::COLUMN_ID => ParameterType::INTEGER, ] ); diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php index 5ea71d4db8..c7e5e9af55 100644 --- a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php @@ -66,7 +66,7 @@ public function deleteById(int $typeId): void public function deleteByIdentifier(string $identifier): void { $this->connection->delete(self::TABLE_NAME, [ - self::COLUMN_IDENTIFIER => $typeId, + self::COLUMN_IDENTIFIER => $identifier, ]); } From 5641575b2e0c5762a340fcc8e2a9dabaff8ad32b Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Fri, 31 Mar 2023 15:49:48 +0200 Subject: [PATCH 33/46] [Tests] Fixed TokenServiceTest --- .../Core/Repository/TokenServiceTest.php | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/integration/Core/Repository/TokenServiceTest.php b/tests/integration/Core/Repository/TokenServiceTest.php index 92e91dec48..27ff04f199 100644 --- a/tests/integration/Core/Repository/TokenServiceTest.php +++ b/tests/integration/Core/Repository/TokenServiceTest.php @@ -37,8 +37,8 @@ protected function setUp(): void public function testGenerateToken( string $type, int $tll, - int $length = 64, - ?string $identifier = null + ?string $identifier, + int $length = 64 ): void { $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); @@ -73,7 +73,7 @@ public function testGenerateTokenThrowsTokenLengthException(): void public function testCheckExistingToken( string $type, int $tll, - ?string $identifier = null + ?string $identifier ): void { $token = $this->tokenService->generateToken($type, $tll, $identifier); @@ -105,8 +105,8 @@ public function testCheckNotExistentToken(): void public function testGetToken( string $type, int $tll, - int $length = 64, - ?string $identifier = null + ?string $identifier, + int $length = 64 ): void { $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); @@ -151,20 +151,21 @@ public function provideTokenData(): iterable yield 'Token with default length 64 and custom identifier' => [ self::TOKEN_TYPE, self::TOKEN_TTL, - 64, self::TOKEN_IDENTIFIER, + 64, ]; yield 'Token with length 200 and custom identifier' => [ self::TOKEN_TYPE, self::TOKEN_TTL, - 200, self::TOKEN_IDENTIFIER, + 200, ]; yield 'Token without identifier' => [ self::TOKEN_TYPE, self::TOKEN_TTL, + null, ]; } @@ -186,6 +187,7 @@ public function provideDataForTestCheckToken(): iterable yield 'Token without identifier' => [ self::TOKEN_TYPE, self::TOKEN_TTL, + null, ]; } @@ -196,9 +198,7 @@ private function getTokenService(): TokenService { $container = $this->getSetupFactory()->getServiceContainer(); - /** @var \Ibexa\Contracts\Core\Repository\TokenService $tokenService */ - $tokenService = $container->get(TokenService::class); - - return $tokenService; + /** @var \Ibexa\Contracts\Core\Repository\TokenService */ + return $container->get(TokenService::class); } } From 13e61554251b09ca79c4b38a497c7567c27a2e48 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Fri, 31 Mar 2023 15:59:16 +0200 Subject: [PATCH 34/46] Fixed token events exception messages --- src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php | 2 +- .../Repository/Events/Token/BeforeGenerateTokenEvent.php | 2 +- src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php index ed758de39f..cb18bd54bd 100644 --- a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -35,7 +35,7 @@ public function getResult(): bool { if (!$this->hasResult()) { throw new UnexpectedValueException( - 'Return value is not set or not of type boolean.' . PHP_EOL + 'Return value is not set.' . PHP_EOL . 'Check hasResult() or set it using setResult() before you call the getter.' ); } diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php index f2b8aa7d9e..2285bb6c5e 100644 --- a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -46,7 +46,7 @@ public function getToken(): Token if (!$this->hasToken()) { throw new UnexpectedValueException( sprintf( - 'Return value is not set or not of type %s.' . PHP_EOL + 'Return value is not set.' . PHP_EOL . 'Check hasToken() or set it using setToken() before you call the getter.', Token::class ) diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php index c22c2c7c61..dcb52db852 100644 --- a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -37,7 +37,7 @@ public function getResult(): Token if (!$this->hasResult()) { throw new UnexpectedValueException( sprintf( - 'Return value is not set or not of type %s.' . PHP_EOL + 'Return value is not set.' . PHP_EOL . 'Check hasResult() or set it using setResult() before you call the getter.', Token::class ) From 42c6ea38500279055dac34233c5170cecd6b1f67 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 13:10:07 +0200 Subject: [PATCH 35/46] Fixed Exception messages --- .../Repository/Events/Token/BeforeGenerateTokenEvent.php | 7 ++----- .../Repository/Events/Token/BeforeGetTokenEvent.php | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php index 2285bb6c5e..06d0fe1f88 100644 --- a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -45,11 +45,8 @@ public function getToken(): Token { if (!$this->hasToken()) { throw new UnexpectedValueException( - sprintf( - 'Return value is not set.' . PHP_EOL - . 'Check hasToken() or set it using setToken() before you call the getter.', - Token::class - ) + 'Return value is not set.' . PHP_EOL + . 'Check hasToken() or set it using setToken() before you call the getter.', ); } diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php index dcb52db852..517b8bc496 100644 --- a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -36,11 +36,8 @@ public function getResult(): Token { if (!$this->hasResult()) { throw new UnexpectedValueException( - sprintf( - 'Return value is not set.' . PHP_EOL - . 'Check hasResult() or set it using setResult() before you call the getter.', - Token::class - ) + 'Return value is not set.' . PHP_EOL + . 'Check hasResult() or set it using setResult() before you call the getter.' ); } From 70c631f9a75f99a20e0ad489551091a6d0481741 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 13:14:53 +0200 Subject: [PATCH 36/46] Moved token generating to RandomBytesGenerator --- src/lib/Resources/settings/tokens.yml | 9 +- src/lib/Token/RandomBytesGenerator.php | 24 +++++ src/lib/Token/WebSafeGenerator.php | 20 ++++- tests/lib/Token/WebSafeGeneratorTest.php | 108 ++++++++++++++++++++--- 4 files changed, 145 insertions(+), 16 deletions(-) create mode 100644 src/lib/Token/RandomBytesGenerator.php diff --git a/src/lib/Resources/settings/tokens.yml b/src/lib/Resources/settings/tokens.yml index 09ba8d7f22..6160e88aad 100644 --- a/src/lib/Resources/settings/tokens.yml +++ b/src/lib/Resources/settings/tokens.yml @@ -4,6 +4,11 @@ services: autoconfigure: true autowire: true - Ibexa\Core\Token\WebSafeGenerator: ~ + Ibexa\Core\Token\RandomBytesGenerator: ~ - Ibexa\Contracts\Core\Token\TokenGeneratorInterface: '@Ibexa\Core\Token\WebSafeGenerator' + Ibexa\Core\Token\WebSafeGenerator: + arguments: + $tokenGenerator: '@Ibexa\Core\Token\RandomBytesGenerator' + + Ibexa\Contracts\Core\Token\TokenGeneratorInterface: + alias: Ibexa\Core\Token\WebSafeGenerator diff --git a/src/lib/Token/RandomBytesGenerator.php b/src/lib/Token/RandomBytesGenerator.php new file mode 100644 index 0000000000..0af7ae8fb5 --- /dev/null +++ b/src/lib/Token/RandomBytesGenerator.php @@ -0,0 +1,24 @@ +tokenGenerator = $tokenGenerator; + } + /** * @throws \Exception */ public function generateToken(int $length = 64): string { - $entropy = (int)floor(($length + 1) * 0.75); - $encoded = base64_encode(random_bytes($entropy)); + $token = $this->tokenGenerator->generateToken($length); + $encoded = base64_encode($token); - return substr(rtrim(strtr($encoded, '+-', '/_'), '='), 0, $length); + return substr( + rtrim( + strtr($encoded, '+-', '/_'), + '=' + ), + 0, + $length + ); } } diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php index 0ce3f5321d..f25cf8be31 100644 --- a/tests/lib/Token/WebSafeGeneratorTest.php +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -19,45 +19,131 @@ final class WebSafeGeneratorTest extends TestCase { private TokenGeneratorInterface $tokenGenerator; + /** @var \Ibexa\Contracts\Core\Token\TokenGeneratorInterface&\PHPUnit\Framework\MockObject\MockObject */ + private TokenGeneratorInterface $randomBytesTokenGenerator; + protected function setUp(): void { - $this->tokenGenerator = new WebSafeGenerator(); + $this->randomBytesTokenGenerator = $this->createMock(TokenGeneratorInterface::class); + $this->tokenGenerator = new WebSafeGenerator($this->randomBytesTokenGenerator); } /** * @dataProvider provideDataForTestGenerateToken * + * @param array> $tokenGeneratingArguments + * @param array $tokens + * * @throws \Exception */ - public function testGenerateToken(int $expectedTokenLength): void - { - $token = $this->tokenGenerator->generateToken($expectedTokenLength); + public function testGenerateToken( + int $expectedTokenLength, + string $expectedToken, + array $tokenGeneratingArguments, + array $tokens + ): void { + $this->mockTokenGeneratorGenerateToken($tokenGeneratingArguments, $tokens); + + $generatedToken = $this->tokenGenerator->generateToken($expectedTokenLength); // Check if Generator returns different tokens self::assertNotSame( - $token, + $generatedToken, $this->tokenGenerator->generateToken($expectedTokenLength) ); self::assertSame( $expectedTokenLength, - strlen($token) + strlen($generatedToken) + ); + + // Check if generated token is web safe + self::assertStringNotContainsString( + $generatedToken, + '=' + ); + + self::assertStringNotContainsString( + $generatedToken, + '+-' ); } /** * @return iterable>, + * array * }> */ public function provideDataForTestGenerateToken(): iterable { - yield [20]; + yield [ + 20, + '1234561qaz2wsx3edc4rfv', + [ + [20], + [20], + ], + [ + '1234561qaz2wsx3edc4rfv', + '2wsx3edc4rfv5tgb04ddda', + ], + ]; + + yield [ + 64, + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3edc4rfv14567', + [ + [64], + [64], + ], + [ + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3edc4rfv14567', + '2wsx3edc4rfv5tgb04ddda2wsx3edc4rfv5tgb04dddazxcvb', + ], + ]; - yield [64]; + yield [ + 100, + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz', + [ + [100], + [100], + ], + [ + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz', + '2wsx3edc4rfv5tgb04ddda2ws2wsx3edc4rfv5tgb04dddazxcvb5678913ec4rfv12aaazccwwa', + ], + ]; - yield [100]; + yield [ + 256, + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', + [ + [256], + [256], + ], + [ + '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', + '4rfv1234561qaz2wsx3ec4rfv1234561qaz2561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edcrfv1234561qazwsx3edc4rfv14567yhnz3ec41234561qaz2wsx3edc4rfv12342wsx3edc4rfv14567yhnzz12345', + ], + ]; + } - yield [256]; + /** + * @param array> $tokenGeneratingArguments + * @param array $tokens + */ + private function mockTokenGeneratorGenerateToken( + array $tokenGeneratingArguments, + array $tokens + ): void { + $this->randomBytesTokenGenerator + ->expects(self::atLeastOnce()) + ->method('generateToken') + ->withConsecutive(...$tokenGeneratingArguments) + ->willReturnOnConsecutiveCalls(...$tokens); } } From 052aa058ff4171cab4a9d6e9be9d7e480b2edc7a Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 13:28:45 +0200 Subject: [PATCH 37/46] Fixed WebSafeGeneratorTest --- tests/lib/Token/WebSafeGeneratorTest.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php index f25cf8be31..6e0600a341 100644 --- a/tests/lib/Token/WebSafeGeneratorTest.php +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -38,7 +38,6 @@ protected function setUp(): void */ public function testGenerateToken( int $expectedTokenLength, - string $expectedToken, array $tokenGeneratingArguments, array $tokens ): void { @@ -72,7 +71,6 @@ public function testGenerateToken( /** * @return iterable>, * array * }> @@ -81,7 +79,6 @@ public function provideDataForTestGenerateToken(): iterable { yield [ 20, - '1234561qaz2wsx3edc4rfv', [ [20], [20], @@ -94,7 +91,6 @@ public function provideDataForTestGenerateToken(): iterable yield [ 64, - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3edc4rfv14567', [ [64], [64], @@ -107,7 +103,6 @@ public function provideDataForTestGenerateToken(): iterable yield [ 100, - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz', [ [100], [100], @@ -120,7 +115,6 @@ public function provideDataForTestGenerateToken(): iterable yield [ 256, - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', [ [256], [256], From b032ab512077a6a9e80a8dc2485f868a0d9fe7d0 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 13:34:37 +0200 Subject: [PATCH 38/46] Removed Token::fromArray --- src/contracts/Repository/Values/Token/Token.php | 12 ------------ src/lib/Repository/TokenService.php | 16 ++++++++-------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/contracts/Repository/Values/Token/Token.php b/src/contracts/Repository/Values/Token/Token.php index ae3a7ac7fb..d3c4fc8bce 100644 --- a/src/contracts/Repository/Values/Token/Token.php +++ b/src/contracts/Repository/Values/Token/Token.php @@ -79,16 +79,4 @@ public function __toString(): string { return $this->token; } - - public static function fromArray(array $properties): self - { - return new self( - $properties['id'], - $properties['type'], - $properties['token'], - $properties['identifier'], - $properties['created'], - $properties['expires'], - ); - } } diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php index 8634eb8de1..d8d6ffbad1 100644 --- a/src/lib/Repository/TokenService.php +++ b/src/lib/Repository/TokenService.php @@ -116,13 +116,13 @@ private function buildDomainObject( PersistenceTokenValue $token, PersistenceTokenTypeValue $tokenType ): Token { - return Token::fromArray([ - 'id' => $token->id, - 'type' => $tokenType->identifier, - 'token' => $token->token, - 'identifier' => $token->identifier, - 'created' => new DateTimeImmutable('@' . $token->created), - 'expires' => new DateTimeImmutable('@' . $token->expires), - ]); + return new Token( + $token->id, + $tokenType->identifier, + $token->token, + $token->identifier, + new DateTimeImmutable('@' . $token->created), + new DateTimeImmutable('@' . $token->expires) + ); } } From 61c2df559f932ced325aae93de926bfeadd54cf7 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 15:50:36 +0200 Subject: [PATCH 39/46] Fixed RandomBytesGeneratorTest --- tests/lib/Token/WebSafeGeneratorTest.php | 88 +++++++----------------- 1 file changed, 24 insertions(+), 64 deletions(-) diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php index 6e0600a341..fd8c05913d 100644 --- a/tests/lib/Token/WebSafeGeneratorTest.php +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -31,113 +31,73 @@ protected function setUp(): void /** * @dataProvider provideDataForTestGenerateToken * - * @param array> $tokenGeneratingArguments - * @param array $tokens - * * @throws \Exception */ public function testGenerateToken( int $expectedTokenLength, - array $tokenGeneratingArguments, - array $tokens + string $mockGeneratorOutputToken, + string $expectedToken ): void { - $this->mockTokenGeneratorGenerateToken($tokenGeneratingArguments, $tokens); + $this->mockTokenGeneratorGenerateToken( + strlen($mockGeneratorOutputToken), + $mockGeneratorOutputToken + ); $generatedToken = $this->tokenGenerator->generateToken($expectedTokenLength); - // Check if Generator returns different tokens - self::assertNotSame( - $generatedToken, - $this->tokenGenerator->generateToken($expectedTokenLength) - ); - self::assertSame( $expectedTokenLength, strlen($generatedToken) ); - // Check if generated token is web safe - self::assertStringNotContainsString( - $generatedToken, - '=' - ); - - self::assertStringNotContainsString( - $generatedToken, - '+-' + self::assertSame( + $expectedToken, + $generatedToken ); } /** * @return iterable>, - * array + * string, + * string * }> */ public function provideDataForTestGenerateToken(): iterable { yield [ 20, - [ - [20], - [20], - ], - [ - '1234561qaz2wsx3edc4rfv', - '2wsx3edc4rfv5tgb04ddda', - ], + '123456+-1az2w3edc4==', + 'MTIzNDU2Ky0xYXoydzNl', ]; yield [ 64, - [ - [64], - [64], - ], - [ - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3edc4rfv14567', - '2wsx3edc4rfv5tgb04ddda2wsx3edc4rfv5tgb04dddazxcvb', - ], + '123/561qaz2wsx3edc4rfv1234561qaz2wsx+-dc=3edc4rv1234561qarfv145=', + 'MTIzLzU2MXFhejJ3c3gzZWRjNHJmdjEyMzQ1NjFxYXoyd3N4Ky1kYz0zZWRjNHJ2', ]; yield [ 100, - [ - [100], - [100], - ], - [ - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz', - '2wsx3edc4rfv5tgb04ddda2ws2wsx3edc4rfv5tgb04dddazxcvb5678913ec4rfv12aaazccwwa', - ], + '+-34561qaz2wsx3ec4rfv1234561qax3edc4rfv5tgbz2wsxaz2wsxdc4rfv123ec4rfv1234561qaz2wsx3edc4rfv457yhnzz=', + 'Ky0zNDU2MXFhejJ3c3gzZWM0cmZ2MTIzNDU2MXFheDNlZGM0cmZ2NXRnYnoyd3N4YXoyd3N4ZGM0cmZ2MTIzZWM0cmZ2MTIzNDU2', ]; yield [ 256, - [ - [256], - [256], - ], - [ - '1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', - '4rfv1234561qaz2wsx3ec4rfv1234561qaz2561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz1234561qaz2wsx3edcrfv1234561qazwsx3edc4rfv14567yhnz3ec41234561qaz2wsx3edc4rfv12342wsx3edc4rfv14567yhnzz12345', - ], + '1234561qaz2wsx3ed+-rfv1234561qa561qaz2wsx3edc4rfv1==234561qaz2wsxz2wsx3ec4rfv1234561qaz2wsx3edc4rfv145=7yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv1fv1234561qaz2wsx3ec4rf==234564567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', + 'MTIzNDU2MXFhejJ3c3gzZWQrLXJmdjEyMzQ1NjFxYTU2MXFhejJ3c3gzZWRjNHJmdjE9PTIzNDU2MXFhejJ3c3h6MndzeDNlYzRyZnYxMjM0NTYxcWF6MndzeDNlZGM0cmZ2MTQ1PTd5aG56ejEyMzQ1NjFxYXoyd3N4M2VkYzRyZnYxMjM0NTYxcWF6MndzeDNlYzRyZnYxMjM0NTYxcWF6MndzeDNlZGM0cmZ2MWZ2MTIzNDU2MXFhejJ3c3gz', ]; } - /** - * @param array> $tokenGeneratingArguments - * @param array $tokens - */ private function mockTokenGeneratorGenerateToken( - array $tokenGeneratingArguments, - array $tokens + int $length, + string $token ): void { $this->randomBytesTokenGenerator - ->expects(self::atLeastOnce()) + ->expects(self::once()) ->method('generateToken') - ->withConsecutive(...$tokenGeneratingArguments) - ->willReturnOnConsecutiveCalls(...$tokens); + ->with($length) + ->willReturn($token); } } From 0975d24062fa70f4bf33f68605def0445fa60834 Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Mon, 3 Apr 2023 15:59:22 +0200 Subject: [PATCH 40/46] Added RandomBytesGeneratorTest --- tests/lib/Token/RandomBytesGeneratorTest.php | 78 ++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 tests/lib/Token/RandomBytesGeneratorTest.php diff --git a/tests/lib/Token/RandomBytesGeneratorTest.php b/tests/lib/Token/RandomBytesGeneratorTest.php new file mode 100644 index 0000000000..478d0fdded --- /dev/null +++ b/tests/lib/Token/RandomBytesGeneratorTest.php @@ -0,0 +1,78 @@ +tokenGenerator = new RandomBytesGenerator(); + } + + /** + * @dataProvider provideDataForTestGenerateToken + * + * @throws \Exception + */ + public function testGenerateToken( + int $expectedTokenLengthInBytes, + int $length + ): void { + $generatedToken = $this->tokenGenerator->generateToken($length); + + // Check if Generator returns different tokens + self::assertNotSame( + $generatedToken, + $this->tokenGenerator->generateToken($length) + ); + + self::assertSame( + $expectedTokenLengthInBytes, + strlen($generatedToken) + ); + } + + /** + * @return iterable + */ + public function provideDataForTestGenerateToken(): iterable + { + yield [ + 15, + 20, + ]; + + yield [ + 48, + 64, + ]; + + yield [ + 75, + 100, + ]; + + yield [ + 192, + 256, + ]; + } +} From 37fb30612d2ee3bac7b2b8458bc6fb9e324e222b Mon Sep 17 00:00:00 2001 From: ciastektk Date: Mon, 3 Apr 2023 16:20:15 +0200 Subject: [PATCH 41/46] Update tests/lib/Token/RandomBytesGeneratorTest.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- tests/lib/Token/RandomBytesGeneratorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/lib/Token/RandomBytesGeneratorTest.php b/tests/lib/Token/RandomBytesGeneratorTest.php index 478d0fdded..68f3cfdc95 100644 --- a/tests/lib/Token/RandomBytesGeneratorTest.php +++ b/tests/lib/Token/RandomBytesGeneratorTest.php @@ -35,10 +35,10 @@ public function testGenerateToken( ): void { $generatedToken = $this->tokenGenerator->generateToken($length); - // Check if Generator returns different tokens self::assertNotSame( $generatedToken, - $this->tokenGenerator->generateToken($length) + $this->tokenGenerator->generateToken($length), + 'Token generator should return different values on subsequent calls', ); self::assertSame( From 19d232d90d4cda6ab64c8669b9831b5e0bbb6177 Mon Sep 17 00:00:00 2001 From: ciastektk Date: Mon, 3 Apr 2023 16:25:49 +0200 Subject: [PATCH 42/46] Update tests/lib/Token/WebSafeGeneratorTest.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Paweł Niedzielski --- tests/lib/Token/WebSafeGeneratorTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php index fd8c05913d..e3ed9daafb 100644 --- a/tests/lib/Token/WebSafeGeneratorTest.php +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -39,7 +39,7 @@ public function testGenerateToken( string $expectedToken ): void { $this->mockTokenGeneratorGenerateToken( - strlen($mockGeneratorOutputToken), + $expectedTokenLength, $mockGeneratorOutputToken ); From 61e4f31b024ffb83575d32b82d3483eae056001c Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 4 Apr 2023 10:07:58 +0200 Subject: [PATCH 43/46] Fixed truncating token by RandomBytesGenerator --- src/lib/Token/RandomBytesGenerator.php | 4 +--- tests/lib/Token/RandomBytesGeneratorTest.php | 17 +++++------------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/lib/Token/RandomBytesGenerator.php b/src/lib/Token/RandomBytesGenerator.php index 0af7ae8fb5..341ef6deed 100644 --- a/src/lib/Token/RandomBytesGenerator.php +++ b/src/lib/Token/RandomBytesGenerator.php @@ -17,8 +17,6 @@ final class RandomBytesGenerator implements TokenGeneratorInterface */ public function generateToken(int $length = 64): string { - $entropy = (int)floor(($length + 1) * 0.75); - - return random_bytes($entropy); + return random_bytes($length); } } diff --git a/tests/lib/Token/RandomBytesGeneratorTest.php b/tests/lib/Token/RandomBytesGeneratorTest.php index 68f3cfdc95..138485510e 100644 --- a/tests/lib/Token/RandomBytesGeneratorTest.php +++ b/tests/lib/Token/RandomBytesGeneratorTest.php @@ -29,49 +29,42 @@ protected function setUp(): void * * @throws \Exception */ - public function testGenerateToken( - int $expectedTokenLengthInBytes, - int $length - ): void { - $generatedToken = $this->tokenGenerator->generateToken($length); + public function testGenerateToken(int $expectedTokenLength): void + { + $generatedToken = $this->tokenGenerator->generateToken($expectedTokenLength); self::assertNotSame( $generatedToken, - $this->tokenGenerator->generateToken($length), + $this->tokenGenerator->generateToken($expectedTokenLength), 'Token generator should return different values on subsequent calls', ); self::assertSame( - $expectedTokenLengthInBytes, + $expectedTokenLength, strlen($generatedToken) ); } /** * @return iterable */ public function provideDataForTestGenerateToken(): iterable { yield [ - 15, 20, ]; yield [ - 48, 64, ]; yield [ - 75, 100, ]; yield [ - 192, 256, ]; } From 4e5d7932aa1ef5146730940abe2b1c662768e1bf Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 4 Apr 2023 12:22:43 +0200 Subject: [PATCH 44/46] Fixed TokenServiceTest --- src/contracts/Test/IbexaTestKernel.php | 1 + .../Core/Repository/TokenServiceTest.php | 19 ++++++------------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/contracts/Test/IbexaTestKernel.php b/src/contracts/Test/IbexaTestKernel.php index a7ca42770a..ece393d629 100644 --- a/src/contracts/Test/IbexaTestKernel.php +++ b/src/contracts/Test/IbexaTestKernel.php @@ -88,6 +88,7 @@ class IbexaTestKernel extends Kernel Repository\SearchService::class, Repository\SectionService::class, Repository\UserService::class, + Repository\TokenService::class, ]; /** diff --git a/tests/integration/Core/Repository/TokenServiceTest.php b/tests/integration/Core/Repository/TokenServiceTest.php index 27ff04f199..066120c316 100644 --- a/tests/integration/Core/Repository/TokenServiceTest.php +++ b/tests/integration/Core/Repository/TokenServiceTest.php @@ -9,12 +9,13 @@ namespace Ibexa\Tests\Integration\Core\Repository; use Ibexa\Contracts\Core\Repository\TokenService; +use Ibexa\Contracts\Core\Test\IbexaKernelTestCase; use Ibexa\Core\Base\Exceptions\TokenLengthException; /** * @covers \Ibexa\Core\Repository\TokenService */ -final class TokenServiceTest extends BaseTest +final class TokenServiceTest extends IbexaKernelTestCase { private const TOKEN_TYPE = 'foo'; private const TOKEN_TTL = 100; @@ -26,7 +27,10 @@ protected function setUp(): void { parent::setUp(); - $this->tokenService = $this->getTokenService(); + self::loadSchema(); + self::loadFixtures(); + + $this->tokenService = self::getServiceByClassName(TokenService::class); } /** @@ -190,15 +194,4 @@ public function provideDataForTestCheckToken(): iterable null, ]; } - - /** - * @throws \ErrorException - */ - private function getTokenService(): TokenService - { - $container = $this->getSetupFactory()->getServiceContainer(); - - /** @var \Ibexa\Contracts\Core\Repository\TokenService */ - return $container->get(TokenService::class); - } } From 47abdc3d299dc0039bf1f141b8fb0d461d70e83a Mon Sep 17 00:00:00 2001 From: Tomasz Kryszan Date: Tue, 4 Apr 2023 14:46:51 +0200 Subject: [PATCH 45/46] Removed token settings from old integration test setup --- tests/integration/Core/LegacyTestContainerBuilder.php | 1 - .../Core/Resources/settings/integration_legacy.yml | 4 ---- 2 files changed, 5 deletions(-) diff --git a/tests/integration/Core/LegacyTestContainerBuilder.php b/tests/integration/Core/LegacyTestContainerBuilder.php index fcbff57704..132236b464 100644 --- a/tests/integration/Core/LegacyTestContainerBuilder.php +++ b/tests/integration/Core/LegacyTestContainerBuilder.php @@ -92,7 +92,6 @@ private function loadCoreSettings(string $settingsPath): LoaderInterface $loader->load('policies.yml'); $loader->load('events.yml'); $loader->load('thumbnails.yml'); - $loader->load('tokens.yml'); $loader->load('content_location_mapper.yml'); // tests/integration/Core/Resources/settings/common.yml diff --git a/tests/integration/Core/Resources/settings/integration_legacy.yml b/tests/integration/Core/Resources/settings/integration_legacy.yml index a2e68068ad..f1cdd2294f 100644 --- a/tests/integration/Core/Resources/settings/integration_legacy.yml +++ b/tests/integration/Core/Resources/settings/integration_legacy.yml @@ -57,8 +57,4 @@ services: public: true alias: Ibexa\Core\Event\SettingService - Ibexa\Contracts\Core\Repository\TokenService: - public: true - alias: Ibexa\Core\Event\TokenService - Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator: '@Ibexa\Tests\Integration\Core\Persistence\Variation\InMemoryVariationHandler' From 2982da03cecde50a8c1759edbc662a50211b9b44 Mon Sep 17 00:00:00 2001 From: Andrew Longosz Date: Tue, 4 Apr 2023 16:42:16 +0200 Subject: [PATCH 46/46] [Tests] Restored loading Tokens DIC for the container to compile --- tests/integration/Core/LegacyTestContainerBuilder.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/Core/LegacyTestContainerBuilder.php b/tests/integration/Core/LegacyTestContainerBuilder.php index 132236b464..fcbff57704 100644 --- a/tests/integration/Core/LegacyTestContainerBuilder.php +++ b/tests/integration/Core/LegacyTestContainerBuilder.php @@ -92,6 +92,7 @@ private function loadCoreSettings(string $settingsPath): LoaderInterface $loader->load('policies.yml'); $loader->load('events.yml'); $loader->load('thumbnails.yml'); + $loader->load('tokens.yml'); $loader->load('content_location_mapper.yml'); // tests/integration/Core/Resources/settings/common.yml