‪TYPO3CMS  ‪main
RequestTokenTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
28 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
29 
30 final class ‪RequestTokenTest extends UnitTestCase
31 {
32  #[Test]
33  public function ‪isCreated(): void
34  {
35  $scope = $this->‪createRandomString();
36  $token = ‪RequestToken::create($scope);
37  $now = $this->‪createCurrentTime();
38  self::assertSame($scope, $token->scope);
39  self::assertEquals($now, $token->time);
40  self::assertSame([], $token->params);
41  }
42 
43  #[Test]
44  public function ‪isCreatedWithProperties(): void
45  {
46  $scope = $this->‪createRandomString();
47  $time = $this->‪createRandomTime();
48  $params = ['value' => bin2hex(random_bytes(4))];
49  $token = new ‪RequestToken($scope, $time, $params);
50  self::assertSame($scope, $token->scope);
51  self::assertEquals($time, $token->time);
52  self::assertSame($params, $token->params);
53  }
54 
55  #[Test]
56  public function ‪paramsAreOverriddenInNewInstance(): void
57  {
58  $scope = $this->‪createRandomString();
59  $params = ['nested' => ['value' => bin2hex(random_bytes(4))]];
60  $token = ‪RequestToken::create($scope)->withParams(['nested' => ['original' => true]]);
61  $modifiedToken = $token->withParams($params);
62  self::assertNotSame($token, $modifiedToken);
63  self::assertSame($params, $modifiedToken->params);
64  }
65 
66  #[Test]
67  public function ‪paramsAreMergedInNewInstance(): void
68  {
69  $scope = $this->‪createRandomString();
70  $params = ['nested' => ['value' => bin2hex(random_bytes(4))]];
71  $token = ‪RequestToken::create($scope)->withParams(['nested' => ['original' => true]]);
72  $modifiedToken = $token->withMergedParams($params);
73  self::assertNotSame($token, $modifiedToken);
74  self::assertSame(array_merge_recursive($token->params, $params), $modifiedToken->params);
75  }
76 
77  public static function ‪isEncodedAndDecodedDataProvider(): \Generator
78  {
79  $nonce = ‪Nonce::create();
80  $pool = new ‪NoncePool([$nonce->getSigningIdentifier()->name => $nonce]);
81 
82  yield 'nonce secret' => [$nonce, $nonce];
83  yield 'secret to be resolved' => [$nonce, new ‪SigningSecretResolver(['nonce' => $pool])];
84  }
85 
86  #[DataProvider('isEncodedAndDecodedDataProvider')]
87  #[Test]
89  {
90  $scope = $this->‪createRandomString();
91  $time = $this->‪createRandomTime();
92  $params = ['value' => bin2hex(random_bytes(4))];
93  $token = new ‪RequestToken($scope, $time, $params);
94 
95  $recodedToken = ‪RequestToken::fromHashSignedJwt($token->toHashSignedJwt($nonce), $secret);
96  self::assertSame($recodedToken->scope, $token->scope);
97  self::assertEquals($recodedToken->time, $token->time);
98  self::assertSame($recodedToken->params, $token->params);
99  self::assertSame('nonce', $recodedToken->getSigningSecretIdentifier()->type);
100  self::assertEquals($nonce->‪getSigningIdentifier(), $recodedToken->getSigningSecretIdentifier());
101  }
102 
103  public static function ‪invalidJwtThrowsExceptionDataProvider(): \Generator
104  {
105  $nonce = ‪Nonce::create();
106  $pool = new ‪NoncePool([$nonce->getSigningIdentifier()->name => $nonce]);
107 
108  yield 'nonce secret (plain)' => ['no-jwt-at-all', $nonce, 1651771352];
109  yield 'use resolver (plain)' => ['no-jwt-at-all', new ‪SigningSecretResolver(['nonce' => $pool]), 1664202134];
110  yield 'nonce secret (JWT-like)' => ['eyJuIjowfQ.eyJuIjowfQ.eyJuIjowfQ', $nonce, 1651771352];
111  yield 'use resolver (JWT-like)' => ['eyJuIjowfQ.eyJuIjowfQ.eyJuIjowfQ', new ‪SigningSecretResolver(['nonce' => $pool]), 1664202134];
112  yield 'nonce secret (bogus)' => ['no.jwt.value', $nonce, 1651771352];
113  yield 'use resolver (bogus)' => ['no.jwt.value', new ‪SigningSecretResolver(['nonce' => $pool]), 1664202134];
114  }
115 
116  #[DataProvider('invalidJwtThrowsExceptionDataProvider')]
117  #[Test]
118  public function ‪invalidJwtThrowsException(string $jwt, ‪SigningSecretInterface|‪SigningSecretResolver $secret, int $exceptionCode): void
119  {
120  $this->expectException(RequestTokenException::class);
121  $this->expectExceptionCode($exceptionCode);
122  ‪RequestToken::fromHashSignedJwt($jwt, $secret);
123  }
124 
125  private function ‪createRandomString(): string
126  {
127  return bin2hex(random_bytes(4));
128  }
129 
130  private function ‪createRandomTime(): \DateTimeImmutable
131  {
132  $now = $this->‪createCurrentTime();
133  $delta = random_int(-7200, 7200);
134  $interval = new \DateInterval(sprintf('PT%dS', abs($delta)));
135  return $delta < 0 ? $now->sub($interval) : $now->add($interval);
136  }
137 
138  private function ‪createCurrentTime(): \DateTimeImmutable
139  {
140  // drop microtime, second is the minimum date-interval
141  return \DateTimeImmutable::createFromFormat(
142  \DateTimeImmutable::RFC3339,
143  (new \DateTimeImmutable())->format(\DateTimeImmutable::RFC3339)
144  );
145  }
146 }
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\isEncodedAndDecodedDataProvider
‪static isEncodedAndDecodedDataProvider()
Definition: RequestTokenTest.php:77
‪TYPO3\CMS\Core\Security\SigningSecretResolver
Definition: SigningSecretResolver.php:26
‪TYPO3\CMS\Core\Security\Nonce\getSigningIdentifier
‪getSigningIdentifier()
Definition: Nonce.php:72
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\createCurrentTime
‪createCurrentTime()
Definition: RequestTokenTest.php:138
‪TYPO3\CMS\Core\Security\RequestTokenException
Definition: RequestTokenException.php:25
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\paramsAreOverriddenInNewInstance
‪paramsAreOverriddenInNewInstance()
Definition: RequestTokenTest.php:56
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\isCreated
‪isCreated()
Definition: RequestTokenTest.php:33
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\invalidJwtThrowsException
‪invalidJwtThrowsException(string $jwt, SigningSecretInterface|SigningSecretResolver $secret, int $exceptionCode)
Definition: RequestTokenTest.php:118
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\paramsAreMergedInNewInstance
‪paramsAreMergedInNewInstance()
Definition: RequestTokenTest.php:67
‪TYPO3\CMS\Core\Security\SigningSecretInterface
Definition: SigningSecretInterface.php:26
‪TYPO3\CMS\Core\Security\RequestToken
Definition: RequestToken.php:26
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\createRandomTime
‪createRandomTime()
Definition: RequestTokenTest.php:130
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\isCreatedWithProperties
‪isCreatedWithProperties()
Definition: RequestTokenTest.php:44
‪TYPO3\CMS\Core\Security\Nonce\create
‪static create(int $length=self::MIN_BYTES)
Definition: Nonce.php:37
‪TYPO3\CMS\Core\Security\NoncePool
Definition: NoncePool.php:24
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\invalidJwtThrowsExceptionDataProvider
‪static invalidJwtThrowsExceptionDataProvider()
Definition: RequestTokenTest.php:103
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\createRandomString
‪createRandomString()
Definition: RequestTokenTest.php:125
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest\isEncodedAndDecoded
‪isEncodedAndDecoded(Nonce $nonce, SigningSecretInterface|SigningSecretResolver $secret)
Definition: RequestTokenTest.php:88
‪TYPO3\CMS\Core\Security\Nonce
Definition: Nonce.php:29
‪TYPO3\CMS\Core\Tests\Unit\Security\RequestTokenTest
Definition: RequestTokenTest.php:31
‪TYPO3\CMS\Core\Security\RequestToken\create
‪static create(string $scope)
Definition: RequestToken.php:43
‪TYPO3\CMS\Core\Security\RequestToken\fromHashSignedJwt
‪static fromHashSignedJwt(string $jwt, SigningSecretInterface|SigningSecretResolver $secret)
Definition: RequestToken.php:48
‪TYPO3\CMS\Core\Tests\Unit\Security