Completed
Pull Request — master (#59)
by Geert
02:39
created

testWillIgnoreUnSignedTokens()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 1
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license.
17
 */
18
19
declare(strict_types=1);
20
21
namespace PSR7SessionsTest\Storageless\Http;
22
23
use DateTimeImmutable;
24
use Dflydev\FigCookies\FigResponseCookies;
25
use Dflydev\FigCookies\SetCookie;
26
use Interop\Http\ServerMiddleware\DelegateInterface;
27
use Interop\Http\ServerMiddleware\MiddlewareInterface;
28
use Lcobucci\JWT\Builder;
29
use Lcobucci\JWT\Parser;
30
use Lcobucci\JWT\Signer;
31
use Lcobucci\JWT\Signer\Hmac\Sha256;
32
use Lcobucci\JWT\Token;
33
use PHPUnit_Framework_TestCase;
34
use Psr\Http\Message\ResponseInterface;
35
use Psr\Http\Message\ServerRequestInterface;
36
use PSR7Sessions\Storageless\Http\SessionMiddleware;
37
use PSR7Sessions\Storageless\Session\DefaultSessionData;
38
use PSR7Sessions\Storageless\Session\SessionInterface;
39
use PSR7Sessions\Storageless\Time\CurrentTimeProviderInterface;
40
use PSR7Sessions\Storageless\Time\SystemCurrentTime;
41
use PSR7SessionsTest\Storageless\Time\FakeCurrentTime;
42
use Zend\Diactoros\Response;
43
use Zend\Diactoros\ServerRequest;
44
45
final class SessionMiddlewareTest extends PHPUnit_Framework_TestCase
46
{
47
    public function testFromSymmetricKeyDefaultsUsesASecureCookie()
48
    {
49
        $response = SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
50
            ->process(new ServerRequest(), $this->writingDelegate());
51
52
        $cookie = $this->getCookie($response);
53
54
        self::assertTrue($cookie->getSecure());
55
        self::assertTrue($cookie->getHttpOnly());
56
    }
57
58
    public function testFromAsymmetricKeyDefaultsUsesASecureCookie()
59
    {
60
        $response = SessionMiddleware
61
            ::fromAsymmetricKeyDefaults(
62
                file_get_contents(__DIR__ . '/../../keys/private_key.pem'),
63
                file_get_contents(__DIR__ . '/../../keys/public_key.pem'),
64
                200
65
            )
66
            ->process(new ServerRequest(), $this->writingDelegate());
67
68
        $cookie = $this->getCookie($response);
69
70
        self::assertTrue($cookie->getSecure());
71
        self::assertTrue($cookie->getHttpOnly());
72
    }
73
74
    /**
75
     * @dataProvider validMiddlewaresProvider
76
     */
77
    public function testSkipsInjectingSessionCookieOnEmptyContainer(SessionMiddleware $middleware)
78
    {
79
        $response = $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationDelegate());
80
81
        self::assertNull($this->getCookie($response)->getValue());
82
    }
83
84
    /**
85
     * @dataProvider validMiddlewaresProvider
86
     */
87
    public function testExtractsSessionContainerFromEmptyRequest(SessionMiddleware $middleware)
88
    {
89
        $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationDelegate());
90
    }
91
92
    /**
93
     * @dataProvider validMiddlewaresProvider
94
     */
95
    public function testInjectsSessionInResponseCookies(SessionMiddleware $middleware)
96
    {
97
        $response = $this->ensureNotSameResponse($middleware, new ServerRequest(), $this->writingDelegate());
98
99
        self::assertEmpty($this->getCookie($response, 'non-existing')->getValue());
100
        self::assertInstanceOf(Token::class, (new Parser())->parse($this->getCookie($response)->getValue()));
101
    }
102
103
    /**
104
     * @dataProvider validMiddlewaresProvider
105
     */
106
    public function testSessionContainerCanBeReusedOverMultipleRequests(SessionMiddleware $middleware)
107
    {
108
        $sessionValue = uniqid('', true);
109
110
        $checkingDelegate = $this->fakeDelegate(
111
            function (ServerRequestInterface $request) use ($sessionValue) {
112
                /* @var $session SessionInterface */
113
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
114
115
                self::assertSame($sessionValue, $session->get('foo'));
116
                self::assertFalse($session->hasChanged());
117
118
                $session->set('foo', $sessionValue . 'changed');
119
120
                self::assertTrue(
121
                    $session->hasChanged(),
122
                    'ensuring that the cookie is sent again: '
123
                    . 'non-modified session containers are not to be re-serialized into a token'
124
                );
125
126
                return new Response();
127
            }
128
        );
129
130
        $firstResponse = $middleware->process(new ServerRequest(), $this->writingDelegate($sessionValue));
131
132
        $initialResponse = new Response();
133
134
        $response = $middleware->process(
135
            $this->requestWithResponseCookies($firstResponse),
136
            $checkingDelegate
137
        );
138
139
        self::assertNotSame($initialResponse, $response);
140
    }
141
142
    /**
143
     * @dataProvider validMiddlewaresProvider
144
     */
145
    public function testWillIgnoreRequestsWithExpiredTokens(SessionMiddleware $middleware)
146
    {
147
        $expiredToken = (new ServerRequest())
148
            ->withCookieParams([
149
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
150
                    $middleware,
151
                    new \DateTime('-1 day'),
152
                    new \DateTime('-2 day')
153
                )
154
            ]);
155
156
        $this->ensureSameResponse($middleware, $expiredToken, $this->emptyValidationDelegate());
157
    }
158
159
    /**
160
     * @dataProvider validMiddlewaresProvider
161
     */
162
    public function testWillIgnoreRequestsWithTokensFromFuture(SessionMiddleware $middleware)
163
    {
164
        $tokenInFuture = (new ServerRequest())
165
            ->withCookieParams([
166
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
167
                    $middleware,
168
                    new \DateTime('+1 day'),
169
                    new \DateTime('-2 day')
170
                )
171
            ]);
172
173
        $this->ensureSameResponse($middleware, $tokenInFuture, $this->emptyValidationDelegate());
174
    }
175
176
    /**
177
     * @dataProvider validMiddlewaresProvider
178
     */
179
    public function testWillIgnoreUnSignedTokens(SessionMiddleware $middleware)
180
    {
181
        $unsignedToken = (new ServerRequest())
182
            ->withCookieParams([
183
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
184
                    ->setIssuedAt((new \DateTime('-1 day'))->getTimestamp())
185
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
186
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
187
                    ->getToken()
188
            ]);
189
190
        $this->ensureSameResponse($middleware, $unsignedToken, $this->emptyValidationDelegate());
191
    }
192
193
    /**
194
     * @dataProvider validMiddlewaresProvider
195
     */
196
    public function testWillNotRefreshSignedTokensWithoutIssuedAt(SessionMiddleware $middleware)
197
    {
198
        $unsignedToken = (new ServerRequest())
199
            ->withCookieParams([
200
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
201
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
202
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
203
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
204
                    ->getToken()
205
            ]);
206
207
        $this->ensureSameResponse($middleware, $unsignedToken, $this->defaultDelegate());
208
    }
209
210
    public function testWillRefreshTokenWithIssuedAtExactlyAtTokenRefreshTimeThreshold()
211
    {
212
        /* @var $currentTimeProvider CurrentTimeProviderInterface|\PHPUnit_Framework_MockObject_MockObject */
213
        $currentTimeProvider = $this->createMock(CurrentTimeProviderInterface::class);
214
215
        $middleware = new SessionMiddleware(
216
            new Sha256(),
217
            'foo',
218
            'foo',
219
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
220
            new Parser(),
221
            1000,
222
            $currentTimeProvider,
0 ignored issues
show
Bug introduced by
It seems like $currentTimeProvider defined by $this->createMock(\PSR7S...oviderInterface::class) on line 213 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, PSR7Sessions\Storageless...ddleware::__construct() does only seem to accept object<PSR7Sessions\Stor...tTimeProviderInterface>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
223
            100
224
        );
225
226
        // forcing ourselves to think of time as a mutable value:
227
        $time = time() + random_int(-100, +100);
228
229
        $currentTimeProvider->expects(self::any())->method('__invoke')->willReturn(new \DateTimeImmutable('@' . $time));
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in PSR7Sessions\Storageless...ntTimeProviderInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
230
231
        $requestWithTokenIssuedInThePast = (new ServerRequest())
232
            ->withCookieParams([
233
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
234
                    ->setExpiration($time + 10000)
235
                    ->setIssuedAt($time - 100)
236
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
237
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
238
                    ->getToken()
239
            ]);
240
241
        $cookie = $this->getCookie($middleware->process($requestWithTokenIssuedInThePast, $this->defaultDelegate()));
242
243
        $token = (new Parser())->parse($cookie->getValue());
244
245
        self::assertEquals($time, $token->getClaim(SessionMiddleware::ISSUED_AT_CLAIM), 'Token was refreshed');
246
    }
247
248
    /**
249
     * @dataProvider validMiddlewaresProvider
250
     */
251
    public function testWillSkipInjectingSessionCookiesWhenSessionIsNotChanged(SessionMiddleware $middleware)
252
    {
253
        $this->ensureSameResponse(
254
            $middleware,
255
            $this->requestWithResponseCookies(
256
                $middleware->process(new ServerRequest(), $this->writingDelegate())
257
            ),
258
            $this->fakeDelegate(
259
                function (ServerRequestInterface $request) {
260
                    /* @var $session SessionInterface */
261
                    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
262
263
                    // note: we set the same data just to make sure that we are indeed interacting with the session
264
                    $session->set('foo', 'bar');
265
266
                    self::assertFalse($session->hasChanged());
267
268
                    return new Response();
269
                }
270
            )
271
        );
272
    }
273
274
    /**
275
     * @dataProvider validMiddlewaresProvider
276
     */
277
    public function testWillSendExpirationCookieWhenSessionContentsAreCleared(SessionMiddleware $middleware)
278
    {
279
        $this->ensureClearsSessionCookie(
280
            $middleware,
281
            $this->requestWithResponseCookies(
282
                $middleware->process(new ServerRequest(), $this->writingDelegate())
283
            ),
284
            $this->fakeDelegate(
285
                function (ServerRequestInterface $request) {
286
                    /* @var $session SessionInterface */
287
                    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
288
289
                    $session->clear();
290
291
                    return new Response();
292
                }
293
            )
294
        );
295
    }
296
297
    /**
298
     * @dataProvider validMiddlewaresProvider
299
     */
300
    public function testWillIgnoreMalformedTokens(SessionMiddleware $middleware)
301
    {
302
        $this->ensureSameResponse(
303
            $middleware,
304
            (new ServerRequest())->withCookieParams([SessionMiddleware::DEFAULT_COOKIE => 'malformed content']),
305
            $this->emptyValidationDelegate()
306
        );
307
    }
308
309
    public function testRejectsTokensWithInvalidSignature()
310
    {
311
        $middleware = new SessionMiddleware(
312
            new Sha256(),
313
            'foo',
314
            'bar', // wrong symmetric key (on purpose)
315
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
316
            new Parser(),
317
            100,
318
            new SystemCurrentTime()
319
        );
320
321
        $this->ensureSameResponse(
322
            $middleware,
323
            $this->requestWithResponseCookies(
324
                $middleware->process(new ServerRequest(), $this->writingDelegate())
325
            ),
326
            $this->emptyValidationDelegate()
327
        );
328
    }
329
330
    public function testAllowsModifyingCookieDetails()
331
    {
332
        $defaultCookie = SetCookie::create('a-different-cookie-name')
333
            ->withDomain('foo.bar')
334
            ->withPath('/yadda')
335
            ->withHttpOnly(false)
336
            ->withMaxAge('123123')
337
            ->withValue('a-random-value');
338
339
        $dateTime   = new DateTimeImmutable();
340
        $middleware = new SessionMiddleware(
341
            new Sha256(),
342
            'foo',
343
            'foo',
344
            $defaultCookie,
345
            new Parser(),
346
            123456,
347
            new FakeCurrentTime($dateTime),
348
            123
349
        );
350
351
        $response = $this->ensureNotSameResponse($middleware, new ServerRequest(), $this->writingDelegate());
352
353
        self::assertNull($this->getCookie($response)->getValue());
354
355
        $tokenCookie = $this->getCookie($response, 'a-different-cookie-name');
356
357
        self::assertNotEmpty($tokenCookie->getValue());
358
        self::assertNotSame($defaultCookie->getValue(), $tokenCookie->getValue());
359
        self::assertSame($defaultCookie->getDomain(), $tokenCookie->getDomain());
360
        self::assertSame($defaultCookie->getPath(), $tokenCookie->getPath());
361
        self::assertSame($defaultCookie->getHttpOnly(), $tokenCookie->getHttpOnly());
362
        self::assertSame($defaultCookie->getMaxAge(), $tokenCookie->getMaxAge());
363
        self::assertEquals($dateTime->getTimestamp() + 123456, $tokenCookie->getExpires());
364
    }
365
366
    public function testSessionTokenParsingIsDelayedWhenSessionIsNotBeingUsed()
367
    {
368
        /* @var $signer Signer|\PHPUnit_Framework_MockObject_MockObject */
369
        $signer = $this->createMock(Signer::class);
370
371
        $signer->expects($this->never())->method('verify');
0 ignored issues
show
Bug introduced by
The method expects does only exist in PHPUnit_Framework_MockObject_MockObject, but not in Lcobucci\JWT\Signer.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
372
373
        $currentTimeProvider = new SystemCurrentTime();
374
        $setCookie  = SetCookie::create(SessionMiddleware::DEFAULT_COOKIE);
375
        $middleware = new SessionMiddleware($signer, 'foo', 'foo', $setCookie, new Parser(), 100, $currentTimeProvider);
0 ignored issues
show
Bug introduced by
It seems like $signer defined by $this->createMock(\Lcobucci\JWT\Signer::class) on line 369 can also be of type object<PHPUnit_Framework_MockObject_MockObject>; however, PSR7Sessions\Storageless...ddleware::__construct() does only seem to accept object<Lcobucci\JWT\Signer>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
376
        $request    = (new ServerRequest())
377
            ->withCookieParams([
378
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
379
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
380
                    ->getToken()
381
            ]);
382
383
        $middleware->process(
384
            $request,
385
            $this->fakeDelegate(function (ServerRequestInterface $request) {
386
                self::assertInstanceOf(
387
                    SessionInterface::class,
388
                    $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE)
389
                );
390
391
                return new Response();
392
            })
393
        );
394
    }
395
396
    public function testShouldRegenerateTokenWhenRequestHasATokenThatIsAboutToExpire()
397
    {
398
        $dateTime   = new DateTimeImmutable();
399
        $middleware = new SessionMiddleware(
400
            new Sha256(),
401
            'foo',
402
            'foo',
403
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
404
            new Parser(),
405
            1000,
406
            new FakeCurrentTime($dateTime),
407
            300
408
        );
409
410
        $expiringToken = (new ServerRequest())
411
            ->withCookieParams([
412
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
413
                    ->setIssuedAt((new \DateTime('-800 second'))->getTimestamp())
414
                    ->setExpiration((new \DateTime('+200 second'))->getTimestamp())
415
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
416
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
417
                    ->getToken()
418
            ]);
419
420
        $response = $this->ensureNotSameResponse($middleware, $expiringToken, $this->writingDelegate());
421
422
        $tokenCookie = $this->getCookie($response);
423
424
        self::assertNotEmpty($tokenCookie->getValue());
425
        self::assertEquals($dateTime->getTimestamp() + 1000, $tokenCookie->getExpires());
426
    }
427
428
    public function testShouldNotRegenerateTokenWhenRequestHasATokenThatIsFarFromExpiration()
429
    {
430
        $middleware = new SessionMiddleware(
431
            new Sha256(),
432
            'foo',
433
            'foo',
434
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
435
            new Parser(),
436
            1000,
437
            new SystemCurrentTime(),
438
            300
439
        );
440
441
        $validToken = (new ServerRequest())
442
            ->withCookieParams([
443
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
444
                    ->setIssuedAt((new \DateTime('-100 second'))->getTimestamp())
445
                    ->setExpiration((new \DateTime('+900 second'))->getTimestamp())
446
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
447
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
448
                    ->getToken()
449
            ]);
450
451
        $this->ensureSameResponse($middleware, $validToken, $this->defaultDelegate());
452
    }
453
454
    /**
455
     * @return SessionMiddleware[][]
456
     */
457
    public function validMiddlewaresProvider()
458
    {
459
        return [
460
            [new SessionMiddleware(
461
                new Sha256(),
462
                'foo',
463
                'foo',
464
                SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
465
                new Parser(),
466
                100,
467
                new SystemCurrentTime()
468
            )],
469
            [SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)],
470
            [SessionMiddleware::fromAsymmetricKeyDefaults(
471
                file_get_contents(__DIR__ . '/../../keys/private_key.pem'),
472
                file_get_contents(__DIR__ . '/../../keys/public_key.pem'),
473
                200
474
            )],
475
        ];
476
    }
477
478
    /**
479
     * @group #46
480
     */
481
    public function testFromSymmetricKeyDefaultsWillHaveADefaultSessionPath()
482
    {
483
        self::assertSame(
484
            '/',
485
            $this
486
                ->getCookie(
487
                    SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
488
                        ->process(new ServerRequest(), $this->writingDelegate())
489
                )
490
                ->getPath()
491
        );
492
    }
493
494
    /**
495
     * @group #46
496
     *
497
     * @throws \InvalidArgumentException
498
     * @throws \OutOfBoundsException
499
     */
500
    public function testFromAsymmetricKeyDefaultsWillHaveADefaultSessionPath()
501
    {
502
        self::assertSame(
503
            '/',
504
            $this
505
                ->getCookie(
506
                    SessionMiddleware
507
                        ::fromAsymmetricKeyDefaults(
508
                            file_get_contents(__DIR__ . '/../../keys/private_key.pem'),
509
                            file_get_contents(__DIR__ . '/../../keys/public_key.pem'),
510
                            200
511
                        )
512
                        ->process(new ServerRequest(), $this->writingDelegate())
513
                )
514
                ->getPath()
515
        );
516
    }
517
518
    /**
519
     * @param SessionMiddleware      $middleware
520
     * @param ServerRequestInterface $request
521
     * @param DelegateInterface      $delegate
522
     *
523
     * @return ResponseInterface
524
     */
525
    private function ensureSameResponse(
526
        SessionMiddleware $middleware,
527
        ServerRequestInterface $request,
528
        DelegateInterface $delegate
529
    ): ResponseInterface {
530
        $initialResponse = new Response();
531
        $response = $middleware->process($request, $delegate);
532
533
        self::assertSame($initialResponse->getHeaders(), $response->getHeaders());
534
535
        return $response;
536
    }
537
538
    private function ensureNotSameResponse(
539
        SessionMiddleware $middleware,
540
        ServerRequestInterface $request,
541
        DelegateInterface $delegate
542
    ): ResponseInterface {
543
        $initialResponse = new Response();
544
        $response = $middleware->process($request, $delegate);
545
546
        self::assertNotSame($initialResponse->getHeaders(), $response->getHeaders());
547
548
        return $response;
549
    }
550
551
    /**
552
     * @param SessionMiddleware      $middleware
553
     * @param ServerRequestInterface $request
554
     * @param DelegateInterface      $delegate
555
     *
556
     * @return ResponseInterface
557
     */
558
    private function ensureClearsSessionCookie(
559
        SessionMiddleware $middleware,
560
        ServerRequestInterface $request,
561
        DelegateInterface $delegate
562
    ): ResponseInterface {
563
        $initialResponse = new Response();
564
        $response = $middleware->process($request, $delegate);
565
566
        self::assertNotSame($initialResponse->getHeaders(), $response->getHeaders());
567
568
        $cookie = $this->getCookie($response);
569
570
        self::assertLessThan((new \DateTime('-29 day'))->getTimestamp(), $cookie->getExpires());
571
        self::assertEmpty($cookie->getValue());
572
573
        return $response;
574
    }
575
576
    /**
577
     * @param SessionMiddleware $middleware
578
     * @param \DateTime $issuedAt
579
     * @param \DateTime $expiration
580
     *
581
     * @return string
582
     */
583
    private function createToken(SessionMiddleware $middleware, \DateTime $issuedAt, \DateTime $expiration): string
584
    {
585
        return (string) (new Builder())
586
            ->setIssuedAt($issuedAt->getTimestamp())
587
            ->setExpiration($expiration->getTimestamp())
588
            ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
589
            ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
590
            ->getToken();
591
    }
592
593
    /**
594
     * @return DelegateInterface
595
     */
596
    private function emptyValidationDelegate(): DelegateInterface
597
    {
598
        return $this->fakeDelegate(
599
            function (ServerRequestInterface $request) {
600
                /* @var $session SessionInterface */
601
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
602
603
                self::assertInstanceOf(SessionInterface::class, $session);
604
                self::assertTrue($session->isEmpty());
605
606
                return new Response();
607
            }
608
        );
609
    }
610
611
    /**
612
     * @param string $value
613
     *
614
     * @return DelegateInterface
615
     */
616
    private function writingDelegate($value = 'bar'): DelegateInterface
617
    {
618
        return $this->fakeDelegate(
619
            function (ServerRequestInterface $request) use ($value) {
620
                /* @var $session SessionInterface */
621
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
622
                $session->set('foo', $value);
623
624
                return new Response();
625
            }
626
        );
627
    }
628
629
    /**
630
     * @return DelegateInterface
631
     */
632
    private function defaultDelegate(): DelegateInterface
633
    {
634
        return $this->fakeDelegate(
635
            function (ServerRequestInterface $request) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
636
                return new Response();
637
            }
638
        );
639
    }
640
641
    /**
642
     * @param callable $callback
643
     *
644
     * @return DelegateInterface
645
     */
646
    private function fakeDelegate(callable $callback): DelegateInterface
647
    {
648
        $delegate = $this->createMock(DelegateInterface::class);
649
650
        $delegate->expects($this->once())
651
            ->method('process')
652
            ->willReturnCallback($callback)
653
            ->with(
654
                self::isInstanceOf(ServerRequestInterface::class)
655
            );
656
657
        return $delegate;
658
    }
659
660
    /**
661
     * @param ResponseInterface $response
662
     *
663
     * @return \Zend\Diactoros\ServerRequest
664
     */
665
    private function requestWithResponseCookies(ResponseInterface $response): ServerRequestInterface
666
    {
667
        return (new ServerRequest())->withCookieParams([
668
            SessionMiddleware::DEFAULT_COOKIE => $this->getCookie($response)->getValue()
669
        ]);
670
    }
671
672
    /**
673
     * @param ResponseInterface $response
674
     *
675
     * @return SetCookie
676
     */
677
    private function getCookie(ResponseInterface $response, string $name = SessionMiddleware::DEFAULT_COOKIE): SetCookie
678
    {
679
        return FigResponseCookies::get($response, $name);
680
    }
681
682
    /**
683
     * @param SessionMiddleware $middleware
684
     *
685
     * @return Signer
686
     */
687
    private function getSigner(SessionMiddleware $middleware): Signer
688
    {
689
        return $this->getPropertyValue($middleware, 'signer');
690
    }
691
692
    /**
693
     * @param SessionMiddleware $middleware
694
     *
695
     * @return string
696
     */
697
    private function getSignatureKey(SessionMiddleware $middleware): string
698
    {
699
        return $this->getPropertyValue($middleware, 'signatureKey');
700
    }
701
702
    /**
703
     * @param object $object
704
     * @param string $name
705
     *
706
     * @return mixed
707
     */
708
    private function getPropertyValue($object, string $name)
709
    {
710
        $propertyReflection = new \ReflectionProperty($object, $name);
711
        $propertyReflection->setAccessible(true);
712
713
        return $propertyReflection->getValue($object);
714
    }
715
}
716