Completed
Pull Request — master (#67)
by Marco
04:35
created

SessionMiddlewareTest::ensureSameResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 9
nc 1
nop 3
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 Lcobucci\Clock\FrozenClock;
27
use Lcobucci\Clock\SystemClock;
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 Zend\Diactoros\Response;
40
use Zend\Diactoros\ServerRequest;
41
use Zend\Stratigility\MiddlewareInterface;
42
use Zend\Stratigility\Next;
43
use Zend\Stratigility\NoopFinalHandler;
44
45
final class SessionMiddlewareTest extends PHPUnit_Framework_TestCase
46
{
47
    public function testFromSymmetricKeyDefaultsUsesASecureCookie() : void
48
    {
49
        $response = SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
50
            ->__invoke(new ServerRequest(), new Response(), $this->writingNext());
51
52
        $cookie = $this->getCookie($response);
53
54
        self::assertTrue($cookie->getSecure());
55
        self::assertTrue($cookie->getHttpOnly());
56
    }
57
58
    public function testFromAsymmetricKeyDefaultsUsesASecureCookie() : void
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
            ->__invoke(new ServerRequest(), new Response(), $this->writingNext());
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) : void
78
    {
79
        $response = $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationNext());
80
81
        self::assertNull($this->getCookie($response)->getValue());
82
    }
83
84
    /**
85
     * @dataProvider validMiddlewaresProvider
86
     */
87
    public function testExtractsSessionContainerFromEmptyRequest(SessionMiddleware $middleware) : void
88
    {
89
        $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationNext());
90
    }
91
92
    /**
93
     * @dataProvider validMiddlewaresProvider
94
     */
95
    public function testInjectsSessionInResponseCookies(SessionMiddleware $middleware) : void
96
    {
97
        $initialResponse = new Response();
98
        $response = $middleware(new ServerRequest(), $initialResponse, $this->writingNext());
99
100
        self::assertNotSame($initialResponse, $response);
101
        self::assertEmpty($this->getCookie($response, 'non-existing')->getValue());
102
        self::assertInstanceOf(Token::class, (new Parser())->parse($this->getCookie($response)->getValue()));
103
    }
104
105
    /**
106
     * @dataProvider validMiddlewaresProvider
107
     */
108
    public function testSessionContainerCanBeReusedOverMultipleRequests(SessionMiddleware $middleware) : void
109
    {
110
        $sessionValue = uniqid('', true);
111
112
        $checkingNext = $this->fakeNext(
113
            function (ServerRequestInterface $request, ResponseInterface $response) use ($sessionValue) {
114
                /* @var $session SessionInterface */
115
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
116
117
                self::assertSame($sessionValue, $session->get('foo'));
118
                self::assertFalse($session->hasChanged());
119
120
                $session->set('foo', $sessionValue . 'changed');
121
122
                self::assertTrue(
123
                    $session->hasChanged(),
124
                    'ensuring that the cookie is sent again: '
125
                    . 'non-modified session containers are not to be re-serialized into a token'
126
                );
127
128
                return $response;
129
            }
130
        );
131
132
        $firstResponse = $middleware(new ServerRequest(), new Response(), $this->writingNext($sessionValue));
133
134
        $initialResponse = new Response();
135
136
        $response = $middleware(
137
            $this->requestWithResponseCookies($firstResponse),
138
            $initialResponse,
139
            $checkingNext
140
        );
141
142
        self::assertNotSame($initialResponse, $response);
143
    }
144
145
    /**
146
     * @dataProvider validMiddlewaresProvider
147
     */
148
    public function testWillIgnoreRequestsWithExpiredTokens(SessionMiddleware $middleware) : void
149
    {
150
        $expiredToken = (new ServerRequest())
151
            ->withCookieParams([
152
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
153
                    $middleware,
154
                    new \DateTime('-1 day'),
155
                    new \DateTime('-2 day')
156
                )
157
            ]);
158
159
        $this->ensureSameResponse($middleware, $expiredToken, $this->emptyValidationNext());
160
    }
161
162
    /**
163
     * @dataProvider validMiddlewaresProvider
164
     */
165
    public function testWillIgnoreRequestsWithTokensFromFuture(SessionMiddleware $middleware) : void
166
    {
167
        $tokenInFuture = (new ServerRequest())
168
            ->withCookieParams([
169
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
170
                    $middleware,
171
                    new \DateTime('+1 day'),
172
                    new \DateTime('-2 day')
173
                )
174
            ]);
175
176
        $this->ensureSameResponse($middleware, $tokenInFuture, $this->emptyValidationNext());
177
    }
178
179
    /**
180
     * @dataProvider validMiddlewaresProvider
181
     */
182
    public function testWillIgnoreUnSignedTokens(SessionMiddleware $middleware) : void
183
    {
184
        $unsignedToken = (new ServerRequest())
185
            ->withCookieParams([
186
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
187
                    ->setIssuedAt((new \DateTime('-1 day'))->getTimestamp())
188
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
189
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
190
                    ->getToken()
191
            ]);
192
193
        $this->ensureSameResponse($middleware, $unsignedToken, $this->emptyValidationNext());
194
    }
195
196
    /**
197
     * @dataProvider validMiddlewaresProvider
198
     */
199
    public function testWillNotRefreshSignedTokensWithoutIssuedAt(SessionMiddleware $middleware) : void
200
    {
201
        $unsignedToken = (new ServerRequest())
202
            ->withCookieParams([
203
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
204
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
205
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
206
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
207
                    ->getToken()
208
            ]);
209
210
        $this->ensureSameResponse($middleware, $unsignedToken, new NoopFinalHandler());
211
    }
212
213
    public function testWillRefreshTokenWithIssuedAtExactlyAtTokenRefreshTimeThreshold() : void
214
    {
215
        // forcing ourselves to think of time as a mutable value:
216
        $time = time() + random_int(-100, +100);
217
218
        $clock = new FrozenClock(new \DateTimeImmutable('@' . $time));
219
220
        $middleware = new SessionMiddleware(
221
            new Sha256(),
222
            'foo',
223
            'foo',
224
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
225
            new Parser(),
226
            1000,
227
            $clock,
228
            100
229
        );
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->__invoke($requestWithTokenIssuedInThePast, new Response(), new NoopFinalHandler()));
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) : void
252
    {
253
        $this->ensureSameResponse(
254
            $middleware,
255
            $this->requestWithResponseCookies(
256
                $middleware(new ServerRequest(), new Response(), $this->writingNext())
257
            ),
258
            $this->fakeNext(
259
                function (ServerRequestInterface $request, ResponseInterface $response) {
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 $response;
269
                }
270
            )
271
        );
272
    }
273
274
    /**
275
     * @dataProvider validMiddlewaresProvider
276
     */
277
    public function testWillSendExpirationCookieWhenSessionContentsAreCleared(SessionMiddleware $middleware) : void
278
    {
279
        $this->ensureClearsSessionCookie(
280
            $middleware,
281
            $this->requestWithResponseCookies(
282
                $middleware(new ServerRequest(), new Response(), $this->writingNext())
283
            ),
284
            $this->fakeNext(
285
                function (ServerRequestInterface $request, ResponseInterface $response) {
286
                    /* @var $session SessionInterface */
287
                    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
288
289
                    $session->clear();
290
291
                    return $response;
292
                }
293
            )
294
        );
295
    }
296
297
    /**
298
     * @dataProvider validMiddlewaresProvider
299
     */
300
    public function testWillIgnoreMalformedTokens(SessionMiddleware $middleware) : void
301
    {
302
        $this->ensureSameResponse(
303
            $middleware,
304
            (new ServerRequest())->withCookieParams([SessionMiddleware::DEFAULT_COOKIE => 'malformed content']),
305
            $this->emptyValidationNext()
306
        );
307
    }
308
309
    public function testRejectsTokensWithInvalidSignature() : void
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 SystemClock()
319
        );
320
321
        $this->ensureSameResponse(
322
            $middleware,
323
            $this->requestWithResponseCookies(
324
                $middleware(new ServerRequest(), new Response(), $this->writingNext())
325
            ),
326
            $this->emptyValidationNext()
327
        );
328
    }
329
330
    public function testAllowsModifyingCookieDetails() : void
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 FrozenClock($dateTime),
348
            123
349
        );
350
351
        $initialResponse = new Response();
352
        $response = $middleware(new ServerRequest(), $initialResponse, $this->writingNext());
353
354
        self::assertNotSame($initialResponse, $response);
355
        self::assertNull($this->getCookie($response)->getValue());
356
357
        $tokenCookie = $this->getCookie($response, 'a-different-cookie-name');
358
359
        self::assertNotEmpty($tokenCookie->getValue());
360
        self::assertNotSame($defaultCookie->getValue(), $tokenCookie->getValue());
361
        self::assertSame($defaultCookie->getDomain(), $tokenCookie->getDomain());
362
        self::assertSame($defaultCookie->getPath(), $tokenCookie->getPath());
363
        self::assertSame($defaultCookie->getHttpOnly(), $tokenCookie->getHttpOnly());
364
        self::assertSame($defaultCookie->getMaxAge(), $tokenCookie->getMaxAge());
365
        self::assertEquals($dateTime->getTimestamp() + 123456, $tokenCookie->getExpires());
366
    }
367
368
    public function testSessionTokenParsingIsDelayedWhenSessionIsNotBeingUsed() : void
369
    {
370
        /* @var $signer Signer|\PHPUnit_Framework_MockObject_MockObject */
371
        $signer = $this->createMock(Signer::class);
372
373
        $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...
374
        $signer->method('getAlgorithmId')->willReturn('HS256');
375
376
        $currentTimeProvider = new SystemClock();
377
        $setCookie  = SetCookie::create(SessionMiddleware::DEFAULT_COOKIE);
378
        $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 371 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...
379
        $request    = (new ServerRequest())
380
            ->withCookieParams([
381
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
382
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
383
                    ->setIssuedAt(time())
384
                    ->sign(new Sha256(), 'foo')
385
                    ->getToken()
386
            ]);
387
388
        $middleware(
389
            $request,
390
            new Response(),
391
            $this->fakeNext(function (ServerRequestInterface $request, ResponseInterface $response) {
392
                self::assertInstanceOf(
393
                    SessionInterface::class,
394
                    $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE)
395
                );
396
397
                return $response;
398
            })
399
        );
400
    }
401
402
    public function testShouldRegenerateTokenWhenRequestHasATokenThatIsAboutToExpire() : void
403
    {
404
        $dateTime   = new DateTimeImmutable();
405
        $middleware = new SessionMiddleware(
406
            new Sha256(),
407
            'foo',
408
            'foo',
409
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
410
            new Parser(),
411
            1000,
412
            new FrozenClock($dateTime),
413
            300
414
        );
415
416
        $expiringToken = (new ServerRequest())
417
            ->withCookieParams([
418
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
419
                    ->setIssuedAt((new \DateTime('-800 second'))->getTimestamp())
420
                    ->setExpiration((new \DateTime('+200 second'))->getTimestamp())
421
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
422
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
423
                    ->getToken()
424
            ]);
425
426
        $initialResponse = new Response();
427
        $response = $middleware($expiringToken, $initialResponse, new NoopFinalHandler());
428
429
        self::assertNotSame($initialResponse, $response);
430
431
        $tokenCookie = $this->getCookie($response);
432
433
        self::assertNotEmpty($tokenCookie->getValue());
434
        self::assertEquals($dateTime->getTimestamp() + 1000, $tokenCookie->getExpires());
435
    }
436
437
    public function testShouldNotRegenerateTokenWhenRequestHasATokenThatIsFarFromExpiration() : void
438
    {
439
        $middleware = new SessionMiddleware(
440
            new Sha256(),
441
            'foo',
442
            'foo',
443
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
444
            new Parser(),
445
            1000,
446
            new SystemClock(),
447
            300
448
        );
449
450
        $validToken = (new ServerRequest())
451
            ->withCookieParams([
452
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
453
                    ->setIssuedAt((new \DateTime('-100 second'))->getTimestamp())
454
                    ->setExpiration((new \DateTime('+900 second'))->getTimestamp())
455
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
456
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
457
                    ->getToken()
458
            ]);
459
460
        $this->ensureSameResponse($middleware, $validToken, new NoopFinalHandler());
461
    }
462
463
    /**
464
     * @return SessionMiddleware[][]
465
     */
466
    public function validMiddlewaresProvider() : array
467
    {
468
        return [
469
            [new SessionMiddleware(
470
                new Sha256(),
471
                'foo',
472
                'foo',
473
                SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
474
                new Parser(),
475
                100,
476
                new SystemClock()
477
            )],
478
            [SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)],
479
            [SessionMiddleware::fromAsymmetricKeyDefaults(
480
                file_get_contents(__DIR__ . '/../../keys/private_key.pem'),
481
                file_get_contents(__DIR__ . '/../../keys/public_key.pem'),
482
                200
483
            )],
484
        ];
485
    }
486
487
    /**
488
     * @group #46
489
     */
490
    public function testFromSymmetricKeyDefaultsWillHaveADefaultSessionPath() : void
491
    {
492
        self::assertSame(
493
            '/',
494
            $this
495
                ->getCookie(
496
                    SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
497
                        ->__invoke(new ServerRequest(), new Response(), $this->writingNext())
498
                )
499
                ->getPath()
500
        );
501
    }
502
503
    /**
504
     * @group #46
505
     *
506
     * @throws \InvalidArgumentException
507
     * @throws \OutOfBoundsException
508
     */
509
    public function testFromAsymmetricKeyDefaultsWillHaveADefaultSessionPath() : void
510
    {
511
        self::assertSame(
512
            '/',
513
            $this
514
                ->getCookie(
515
                    SessionMiddleware
516
                        ::fromAsymmetricKeyDefaults(
517
                            file_get_contents(__DIR__ . '/../../keys/private_key.pem'),
518
                            file_get_contents(__DIR__ . '/../../keys/public_key.pem'),
519
                            200
520
                        )
521
                        ->__invoke(new ServerRequest(), new Response(), $this->writingNext())
522
                )
523
                ->getPath()
524
        );
525
    }
526
527
    /**
528
     * @param SessionMiddleware $middleware
529
     * @param ServerRequestInterface $request
530
     * @param callable $next
531
     *
532
     * @return ResponseInterface
533
     */
534
    private function ensureSameResponse(
535
        SessionMiddleware $middleware,
536
        ServerRequestInterface $request,
537
        callable $next = null
538
    ) : ResponseInterface {
539
        $initialResponse = new Response();
540
        $response = $middleware($request, $initialResponse, $next);
541
542
        self::assertSame($initialResponse, $response);
543
544
        return $response;
545
    }
546
547
    /**
548
     * @param SessionMiddleware $middleware
549
     * @param ServerRequestInterface $request
550
     * @param callable $next
551
     *
552
     * @return ResponseInterface
553
     */
554
    private function ensureClearsSessionCookie(
555
        SessionMiddleware $middleware,
556
        ServerRequestInterface $request,
557
        callable $next = null
558
    ) : ResponseInterface {
559
        $initialResponse = new Response();
560
        $response = $middleware($request, $initialResponse, $next);
561
562
        self::assertNotSame($initialResponse, $response);
563
564
        $cookie = $this->getCookie($response);
565
566
        self::assertLessThan((new \DateTime('-29 day'))->getTimestamp(), $cookie->getExpires());
567
        self::assertEmpty($cookie->getValue());
568
569
        return $response;
570
    }
571
572
    /**
573
     * @param SessionMiddleware $middleware
574
     * @param \DateTime $issuedAt
575
     * @param \DateTime $expiration
576
     *
577
     * @return string
578
     */
579
    private function createToken(SessionMiddleware $middleware, \DateTime $issuedAt, \DateTime $expiration) : string
580
    {
581
        return (string) (new Builder())
582
            ->setIssuedAt($issuedAt->getTimestamp())
583
            ->setExpiration($expiration->getTimestamp())
584
            ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
585
            ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
586
            ->getToken();
587
    }
588
589
    /**
590
     * @return Next
591
     */
592
    private function emptyValidationNext() : Next
593
    {
594
        return $this->fakeNext(
595
            function (ServerRequestInterface $request, ResponseInterface $response) {
596
                /* @var $session SessionInterface */
597
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
598
599
                self::assertInstanceOf(SessionInterface::class, $session);
600
                self::assertTrue($session->isEmpty());
601
602
                return $response;
603
            }
604
        );
605
    }
606
607
    /**
608
     * @param string $value
609
     *
610
     * @return Next
611
     */
612
    private function writingNext(string $value = 'bar') : Next
613
    {
614
        return $this->fakeNext(
615
            function (ServerRequestInterface $request, ResponseInterface $response) use ($value) {
616
                /* @var $session SessionInterface */
617
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
618
                $session->set('foo', $value);
619
620
                return $response;
621
            }
622
        );
623
    }
624
625
    /**
626
     * @param callable $callback
627
     *
628
     * @return Next
629
     */
630
    private function fakeNext(callable $callback) : Next
631
    {
632
        $next = $this->createMock(Next::class);
633
634
        $next->expects($this->once())
635
           ->method('__invoke')
636
           ->willReturnCallback($callback)
637
           ->with(
638
               self::isInstanceOf(ServerRequestInterface::class),
639
               self::isInstanceOf(ResponseInterface::class)
640
           );
641
642
        return $next;
643
    }
644
645
    /**
646
     * @param ResponseInterface $response
647
     *
648
     * @return \Zend\Diactoros\ServerRequest
649
     */
650
    private function requestWithResponseCookies(ResponseInterface $response) : ServerRequestInterface
651
    {
652
        return (new ServerRequest())->withCookieParams([
653
            SessionMiddleware::DEFAULT_COOKIE => $this->getCookie($response)->getValue()
654
        ]);
655
    }
656
657
    /**
658
     * @param ResponseInterface $response
659
     *
660
     * @return SetCookie
661
     */
662
    private function getCookie(ResponseInterface $response, string $name = SessionMiddleware::DEFAULT_COOKIE) : SetCookie
663
    {
664
        return FigResponseCookies::get($response, $name);
665
    }
666
667
    /**
668
     * @param SessionMiddleware $middleware
669
     *
670
     * @return Signer
671
     */
672
    private function getSigner(SessionMiddleware $middleware) : Signer
673
    {
674
        return $this->getPropertyValue($middleware, 'signer');
675
    }
676
677
    /**
678
     * @param SessionMiddleware $middleware
679
     *
680
     * @return string
681
     */
682
    private function getSignatureKey(SessionMiddleware $middleware) : string
683
    {
684
        return $this->getPropertyValue($middleware, 'signatureKey');
685
    }
686
687
    /**
688
     * @param object $object
689
     * @param string $name
690
     *
691
     * @return mixed
692
     */
693
    private function getPropertyValue($object, string $name)
694
    {
695
        $propertyReflection = new \ReflectionProperty($object, $name);
696
        $propertyReflection->setAccessible(true);
697
698
        return $propertyReflection->getValue($object);
699
    }
700
}
701