Test Setup Failed
Pull Request — master (#79)
by Marco
31:16
created

testSessionTokenParsingIsDelayedWhenSessionIsNotBeingUsed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 9.408
c 0
b 0
f 0
cc 1
nc 1
nop 0
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\Modifier\SameSite;
26
use Dflydev\FigCookies\SetCookie;
27
use Lcobucci\Clock\FrozenClock;
28
use Lcobucci\Clock\SystemClock;
29
use Lcobucci\JWT\Builder;
30
use Lcobucci\JWT\Parser;
31
use Lcobucci\JWT\Signer;
32
use Lcobucci\JWT\Signer\Hmac\Sha256;
33
use PHPUnit\Framework\MockObject\MockObject;
34
use PHPUnit\Framework\TestCase;
35
use Psr\Http\Message\RequestInterface;
36
use Psr\Http\Message\ResponseInterface;
37
use Psr\Http\Message\ServerRequestInterface;
38
use Psr\Http\Server\RequestHandlerInterface;
39
use PSR7Sessions\Storageless\Http\SessionMiddleware;
40
use PSR7Sessions\Storageless\Session\DefaultSessionData;
41
use PSR7Sessions\Storageless\Session\SessionInterface;
42
use Zend\Diactoros\Response;
43
use Zend\Diactoros\ServerRequest;
44
use function file_get_contents;
45
use function random_int;
46
use function time;
47
use function uniqid;
48
49
final class SessionMiddlewareTest extends TestCase
50
{
51
    public function testFromSymmetricKeyDefaultsUsesASecureCookie() : void
52
    {
53
        $response = SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
54
            ->process(new ServerRequest(), $this->writingMiddleware());
55
56
        $cookie = $this->getCookie($response);
57
58
        self::assertTrue($cookie->getSecure());
59
        self::assertTrue($cookie->getHttpOnly());
60
    }
61
62
    public function testFromAsymmetricKeyDefaultsUsesASecureCookie() : void
63
    {
64
        $response = SessionMiddleware::fromAsymmetricKeyDefaults(
65
            self::privateKey(),
66
            self::publicKey(),
67
            200
68
        )
69
            ->process(new ServerRequest(), $this->writingMiddleware());
70
71
        $cookie = $this->getCookie($response);
72
73
        self::assertTrue($cookie->getSecure());
74
        self::assertTrue($cookie->getHttpOnly());
75
    }
76
77
    /**
78
     * @dataProvider validMiddlewaresProvider
79
     */
80
    public function testSkipsInjectingSessionCookieOnEmptyContainer(SessionMiddleware $middleware) : void
81
    {
82
        $response = $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationMiddleware());
83
84
        self::assertNull($this->getCookie($response)->getValue());
85
    }
86
87
    /**
88
     * @dataProvider validMiddlewaresProvider
89
     */
90
    public function testExtractsSessionContainerFromEmptyRequest(SessionMiddleware $middleware) : void
91
    {
92
        $this->ensureSameResponse($middleware, new ServerRequest(), $this->emptyValidationMiddleware());
93
    }
94
95
    /**
96
     * @dataProvider validMiddlewaresProvider
97
     */
98
    public function testInjectsSessionInResponseCookies(SessionMiddleware $middleware) : void
99
    {
100
        $initialResponse = new Response();
101
        $response        = $middleware->process(new ServerRequest(), $this->writingMiddleware());
102
103
        self::assertNotSame($initialResponse, $response);
104
        self::assertEmpty($this->getCookie($response, 'non-existing')->getValue());
105
106
        $token = $this->getCookie($response)->getValue();
107
108
        self::assertInternalType('string', $token);
109
        self::assertEquals((object) ['foo' => 'bar'], (new Parser())->parse($token)->getClaim('session-data'));
110
    }
111
112
    /**
113
     * @dataProvider validMiddlewaresProvider
114
     */
115
    public function testSessionContainerCanBeReusedOverMultipleRequests(SessionMiddleware $middleware) : void
116
    {
117
        $sessionValue = uniqid('', true);
118
119
        $checkingMiddleware = $this->fakeDelegate(
120
            function (ServerRequestInterface $request) use ($sessionValue) {
121
                /** @var SessionInterface $session */
122
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
123
124
                self::assertSame($sessionValue, $session->get('foo'));
125
                self::assertFalse($session->hasChanged());
126
127
                $session->set('foo', $sessionValue . 'changed');
128
129
                self::assertTrue(
130
                    $session->hasChanged(),
131
                    'ensuring that the cookie is sent again: '
132
                    . 'non-modified session containers are not to be re-serialized into a token'
133
                );
134
135
                return new Response();
136
            }
137
        );
138
139
        $firstResponse = $middleware->process(new ServerRequest(), $this->writingMiddleware($sessionValue));
140
141
        $response = $middleware->process(
142
            $this->requestWithResponseCookies($firstResponse),
143
            $checkingMiddleware
144
        );
145
146
        self::assertNotSame($response, $firstResponse);
147
    }
148
149
    /**
150
     * @dataProvider validMiddlewaresProvider
151
     */
152
    public function testSessionContainerCanBeCreatedEvenIfTokenDataIsMalformed(SessionMiddleware $middleware) : void
153
    {
154
        $sessionValue = uniqid('not valid session data', true);
155
156
        $checkingMiddleware = $this->fakeDelegate(
157
            function (ServerRequestInterface $request) use ($sessionValue) {
158
                /** @var SessionInterface $session */
159
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
160
161
                self::assertSame($sessionValue, $session->get('scalar'));
162
                self::assertFalse($session->hasChanged());
163
164
                return new Response();
165
            }
166
        );
167
168
        $this->createTokenWithCustomClaim(
169
            $middleware,
170
            new \DateTime('-1 day'),
171
            new \DateTime('+1 day'),
172
            'not valid session data'
173
        );
174
175
        $middleware->process(
176
            (new ServerRequest())
177
                ->withCookieParams([
178
                    SessionMiddleware::DEFAULT_COOKIE => $this->createTokenWithCustomClaim(
179
                        $middleware,
180
                        new \DateTime('-1 day'),
181
                        new \DateTime('+1 day'),
182
                        $sessionValue
183
                    ),
184
                ]),
185
            $checkingMiddleware
186
        );
187
    }
188
189
    /**
190
     * @dataProvider validMiddlewaresProvider
191
     */
192
    public function testWillIgnoreRequestsWithExpiredTokens(SessionMiddleware $middleware) : void
193
    {
194
        $expiredToken = (new ServerRequest())
195
            ->withCookieParams([
196
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
197
                    $middleware,
198
                    new \DateTime('-1 day'),
199
                    new \DateTime('-2 day')
200
                ),
201
            ]);
202
203
        $this->ensureSameResponse($middleware, $expiredToken, $this->emptyValidationMiddleware());
204
    }
205
206
    /**
207
     * @dataProvider validMiddlewaresProvider
208
     */
209
    public function testWillIgnoreRequestsWithTokensFromFuture(SessionMiddleware $middleware) : void
210
    {
211
        $tokenInFuture = (new ServerRequest())
212
            ->withCookieParams([
213
                SessionMiddleware::DEFAULT_COOKIE => $this->createToken(
214
                    $middleware,
215
                    new \DateTime('+1 day'),
216
                    new \DateTime('-2 day')
217
                ),
218
            ]);
219
220
        $this->ensureSameResponse($middleware, $tokenInFuture, $this->emptyValidationMiddleware());
221
    }
222
223
    /**
224
     * @dataProvider validMiddlewaresProvider
225
     */
226
    public function testWillIgnoreUnSignedTokens(SessionMiddleware $middleware) : void
227
    {
228
        $unsignedToken = (new ServerRequest())
229
            ->withCookieParams([
230
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
231
                    ->setIssuedAt((new \DateTime('-1 day'))->getTimestamp())
232
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
233
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
234
                    ->getToken(),
235
            ]);
236
237
        $this->ensureSameResponse($middleware, $unsignedToken, $this->emptyValidationMiddleware());
238
    }
239
240
    /**
241
     * @dataProvider validMiddlewaresProvider
242
     */
243
    public function testWillNotRefreshSignedTokensWithoutIssuedAt(SessionMiddleware $middleware) : void
244
    {
245
        $unsignedToken = (new ServerRequest())
246
            ->withCookieParams([
247
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
248
                    ->setExpiration((new \DateTime('+1 day'))->getTimestamp())
249
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
250
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
251
                    ->getToken(),
252
            ]);
253
254
        $this->ensureSameResponse($middleware, $unsignedToken);
255
    }
256
257
    public function testWillRefreshTokenWithIssuedAtExactlyAtTokenRefreshTimeThreshold() : void
258
    {
259
        // forcing ourselves to think of time as a mutable value:
260
        $time = time() + random_int(-100, +100);
261
262
        $clock = new FrozenClock(new \DateTimeImmutable('@' . $time));
263
264
        $middleware = new SessionMiddleware(
265
            new Sha256(),
266
            'foo',
267
            'foo',
268
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
269
            new Parser(),
270
            1000,
271
            $clock,
272
            100
273
        );
274
275
        $requestWithTokenIssuedInThePast = (new ServerRequest())
276
            ->withCookieParams([
277
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
278
                    ->setExpiration($time + 10000)
279
                    ->setIssuedAt($time - 100)
280
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
281
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
282
                    ->getToken(),
283
            ]);
284
285
        $tokenString = $this
286
            ->getCookie($middleware->process($requestWithTokenIssuedInThePast, $this->fakeDelegate(function () {
287
                return new Response();
288
            })))
289
            ->getValue();
290
291
        self::assertInternalType('string', $tokenString);
292
293
        $token = (new Parser())->parse($tokenString);
294
295
        self::assertEquals($time, $token->getClaim(SessionMiddleware::ISSUED_AT_CLAIM), 'Token was refreshed');
296
    }
297
298
    /**
299
     * @dataProvider validMiddlewaresProvider
300
     */
301
    public function testWillSkipInjectingSessionCookiesWhenSessionIsNotChanged(SessionMiddleware $middleware) : void
302
    {
303
        $this->ensureSameResponse(
304
            $middleware,
305
            $this->requestWithResponseCookies(
306
                $middleware->process(new ServerRequest(), $this->writingMiddleware())
307
            ),
308
            $this->fakeDelegate(
309
                function (ServerRequestInterface $request) {
310
                    /** @var SessionInterface $session */
311
                    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
312
313
                    // note: we set the same data just to make sure that we are indeed interacting with the session
314
                    $session->set('foo', 'bar');
315
316
                    self::assertFalse($session->hasChanged());
317
318
                    return new Response();
319
                }
320
            )
321
        );
322
    }
323
324
    /**
325
     * @dataProvider validMiddlewaresProvider
326
     */
327
    public function testWillSendExpirationCookieWhenSessionContentsAreCleared(SessionMiddleware $middleware) : void
328
    {
329
        $this->ensureClearsSessionCookie(
330
            $middleware,
331
            $this->requestWithResponseCookies(
332
                $middleware->process(new ServerRequest(), $this->writingMiddleware())
333
            ),
334
            $this->fakeDelegate(
335
                function (ServerRequestInterface $request) {
336
                    /** @var SessionInterface $session */
337
                    $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
338
339
                    $session->clear();
340
341
                    return new Response();
342
                }
343
            )
344
        );
345
    }
346
347
    /**
348
     * @dataProvider validMiddlewaresProvider
349
     */
350
    public function testWillIgnoreMalformedTokens(SessionMiddleware $middleware) : void
351
    {
352
        $this->ensureSameResponse(
353
            $middleware,
354
            (new ServerRequest())->withCookieParams([SessionMiddleware::DEFAULT_COOKIE => 'malformed content']),
355
            $this->emptyValidationMiddleware()
356
        );
357
    }
358
359
    public function testRejectsTokensWithInvalidSignature() : void
360
    {
361
        $middleware = new SessionMiddleware(
362
            new Sha256(),
363
            'foo',
364
            'bar', // wrong symmetric key (on purpose)
365
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
366
            new Parser(),
367
            100,
368
            new SystemClock()
369
        );
370
371
        $this->ensureSameResponse(
372
            $middleware,
373
            $this->requestWithResponseCookies(
374
                $middleware->process(new ServerRequest(), $this->writingMiddleware())
375
            ),
376
            $this->emptyValidationMiddleware()
377
        );
378
    }
379
380
    public function testAllowsModifyingCookieDetails() : void
381
    {
382
        $defaultCookie = SetCookie::create('a-different-cookie-name')
383
            ->withDomain('foo.bar')
384
            ->withPath('/yadda')
385
            ->withHttpOnly(false)
386
            ->withMaxAge(123123)
387
            ->withValue('a-random-value');
388
389
        $dateTime   = new DateTimeImmutable();
390
        $middleware = new SessionMiddleware(
391
            new Sha256(),
392
            'foo',
393
            'foo',
394
            $defaultCookie,
395
            new Parser(),
396
            123456,
397
            new FrozenClock($dateTime),
398
            123
399
        );
400
401
        $response = $middleware->process(new ServerRequest(), $this->writingMiddleware());
402
403
        self::assertNull($this->getCookie($response)->getValue());
404
405
        $tokenCookie = $this->getCookie($response, 'a-different-cookie-name');
406
407
        self::assertNotEmpty($tokenCookie->getValue());
408
        self::assertNotSame($defaultCookie->getValue(), $tokenCookie->getValue());
409
        self::assertSame($defaultCookie->getDomain(), $tokenCookie->getDomain());
410
        self::assertSame($defaultCookie->getPath(), $tokenCookie->getPath());
411
        self::assertSame($defaultCookie->getHttpOnly(), $tokenCookie->getHttpOnly());
412
        self::assertSame($defaultCookie->getMaxAge(), $tokenCookie->getMaxAge());
413
        self::assertEquals($dateTime->getTimestamp() + 123456, $tokenCookie->getExpires());
414
    }
415
416
    public function testSessionTokenParsingIsDelayedWhenSessionIsNotBeingUsed() : void
417
    {
418
        /** @var Signer|MockObject $signer */
419
        $signer = $this->createMock(Signer::class);
420
421
        $signer->expects(self::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...
422
        $signer->method('getAlgorithmId')->willReturn('HS256');
423
424
        $currentTimeProvider = new SystemClock();
425
        $setCookie           = SetCookie::create(SessionMiddleware::DEFAULT_COOKIE);
426
        $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 419 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...
427
        $request             = (new ServerRequest())
428
            ->withCookieParams([
429
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
430
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
431
                    ->setIssuedAt(time())
432
                    ->sign(new Sha256(), 'foo')
433
                    ->getToken(),
434
            ]);
435
436
        $middleware->process(
437
            $request,
438
            $this->fakeDelegate(function (ServerRequestInterface $request) {
439
                self::assertInstanceOf(
440
                    SessionInterface::class,
441
                    $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE)
442
                );
443
444
                return new Response();
445
            })
446
        );
447
    }
448
449
    public function testShouldRegenerateTokenWhenRequestHasATokenThatIsAboutToExpire() : void
450
    {
451
        $dateTime   = new DateTimeImmutable();
452
        $middleware = new SessionMiddleware(
453
            new Sha256(),
454
            'foo',
455
            'foo',
456
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
457
            new Parser(),
458
            1000,
459
            new FrozenClock($dateTime),
460
            300
461
        );
462
463
        $expiringToken = (new ServerRequest())
464
            ->withCookieParams([
465
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
466
                    ->setIssuedAt((new \DateTime('-800 second'))->getTimestamp())
467
                    ->setExpiration((new \DateTime('+200 second'))->getTimestamp())
468
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
469
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
470
                    ->getToken(),
471
            ]);
472
473
        $initialResponse = new Response();
474
475
        $response = $middleware->process($expiringToken, $this->fakeDelegate(function () use ($initialResponse) {
476
            return $initialResponse;
477
        }));
478
479
        self::assertNotSame($initialResponse, $response);
480
481
        $tokenCookie = $this->getCookie($response);
482
483
        self::assertNotEmpty($tokenCookie->getValue());
484
        self::assertEquals($dateTime->getTimestamp() + 1000, $tokenCookie->getExpires());
485
    }
486
487
    public function testShouldNotRegenerateTokenWhenRequestHasATokenThatIsFarFromExpiration() : void
488
    {
489
        $middleware = new SessionMiddleware(
490
            new Sha256(),
491
            'foo',
492
            'foo',
493
            SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
494
            new Parser(),
495
            1000,
496
            new SystemClock(),
497
            300
498
        );
499
500
        $validToken = (new ServerRequest())
501
            ->withCookieParams([
502
                SessionMiddleware::DEFAULT_COOKIE => (string) (new Builder())
503
                    ->setIssuedAt((new \DateTime('-100 second'))->getTimestamp())
504
                    ->setExpiration((new \DateTime('+900 second'))->getTimestamp())
505
                    ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
506
                    ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
507
                    ->getToken(),
508
            ]);
509
510
        $this->ensureSameResponse($middleware, $validToken);
511
    }
512
513
    /**
514
     * @return SessionMiddleware[][]
515
     */
516
    public function validMiddlewaresProvider() : array
517
    {
518
        return [
519
            [new SessionMiddleware(
520
                new Sha256(),
521
                'foo',
522
                'foo',
523
                SetCookie::create(SessionMiddleware::DEFAULT_COOKIE),
524
                new Parser(),
525
                100,
526
                new SystemClock()
527
            ),
528
            ],
529
            [SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)],
530
            [SessionMiddleware::fromAsymmetricKeyDefaults(
531
                self::privateKey(),
532
                self::publicKey(),
533
                200
534
            ),
535
            ],
536
        ];
537
    }
538
539
    /**
540
     * @group #46
541
     */
542
    public function testFromSymmetricKeyDefaultsWillHaveADefaultSessionPath() : void
543
    {
544
        self::assertSame(
545
            '/',
546
            $this
547
                ->getCookie(
548
                    SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
549
                        ->process(new ServerRequest(), $this->writingMiddleware())
550
                )
551
                ->getPath()
552
        );
553
    }
554
555
    public function testFromSymmetricKeyDefaultsWillHaveALaxSameSitePolicy() : void
556
    {
557
        self::assertEquals(
558
            SameSite::lax(),
559
            $this
560
                ->getCookie(
561
                    SessionMiddleware::fromSymmetricKeyDefaults('not relevant', 100)
562
                        ->process(new ServerRequest(), $this->writingMiddleware())
563
                )
564
                ->getSameSite()
565
        );
566
    }
567
568
    /**
569
     * @group #46
570
     *
571
     * @throws \InvalidArgumentException
572
     * @throws \OutOfBoundsException
573
     */
574
    public function testFromAsymmetricKeyDefaultsWillHaveADefaultSessionPath() : void
575
    {
576
        self::assertSame(
577
            '/',
578
            $this
579
                ->getCookie(
580
                    SessionMiddleware::fromAsymmetricKeyDefaults(
581
                        self::privateKey(),
582
                        self::publicKey(),
583
                        200
584
                    )
585
                        ->process(new ServerRequest(), $this->writingMiddleware())
586
                )
587
                ->getPath()
588
        );
589
    }
590
591
    public function testFromAsymmetricKeyDefaultsWillHaveALaxSameSitePolicy() : void
592
    {
593
        self::assertEquals(
594
            SameSite::lax(),
595
            $this
596
                ->getCookie(
597
                    SessionMiddleware::fromAsymmetricKeyDefaults(
598
                        self::privateKey(),
599
                        self::publicKey(),
600
                        200
601
                    )
602
                        ->process(new ServerRequest(), $this->writingMiddleware())
603
                )
604
                ->getSameSite()
605
        );
606
    }
607
608
    private function ensureSameResponse(
609
        SessionMiddleware $middleware,
610
        ServerRequestInterface $request,
611
        ?RequestHandlerInterface $next = null
612
    ) : ResponseInterface {
613
        $initialResponse = new Response();
614
615
        $handleRequest = $this->createMock(RequestHandlerInterface::class);
616
617
        if ($next === null) {
618
            $handleRequest
619
                ->expects(self::once())
620
                ->method('handle')
621
                ->willReturn($initialResponse);
622
        } else {
623
            // capturing `$initialResponse` from the `$next` handler
624
            $handleRequest
625
                ->expects(self::once())
626
                ->method('handle')
627
                ->willReturnCallback(function (ServerRequestInterface $serverRequest) use ($next, & $initialResponse) {
628
                    $initialResponse = $next->handle($serverRequest);
629
630
                    return $initialResponse;
631
                });
632
        }
633
634
        $response = $middleware->process($request, $handleRequest);
0 ignored issues
show
Documentation introduced by
$handleRequest is of type object<PHPUnit\Framework\MockObject\MockObject>, but the function expects a object<Psr\Http\Server\RequestHandlerInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
635
636
        self::assertSame($initialResponse, $response);
637
638
        return $response;
639
    }
640
641
    private function ensureClearsSessionCookie(
642
        SessionMiddleware $middleware,
643
        ServerRequestInterface $request,
644
        RequestHandlerInterface $next
645
    ) : ResponseInterface {
646
        $response = $middleware->process($request, $next);
647
648
        $cookie = $this->getCookie($response);
649
650
        self::assertLessThan((new \DateTime('-29 day'))->getTimestamp(), $cookie->getExpires());
651
        self::assertEmpty($cookie->getValue());
652
653
        return $response;
654
    }
655
656
    private function createToken(SessionMiddleware $middleware, \DateTime $issuedAt, \DateTime $expiration) : string
657
    {
658
        return (string) (new Builder())
659
            ->setIssuedAt($issuedAt->getTimestamp())
660
            ->setExpiration($expiration->getTimestamp())
661
            ->set(SessionMiddleware::SESSION_CLAIM, DefaultSessionData::fromTokenData(['foo' => 'bar']))
662
            ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
663
            ->getToken();
664
    }
665
666
    /** @param mixed $claim */
667
    private function createTokenWithCustomClaim(
668
        SessionMiddleware $middleware,
669
        \DateTime $issuedAt,
670
        \DateTime $expiration,
671
        $claim
672
    ) : string {
673
        return (string) (new Builder())
674
            ->setIssuedAt($issuedAt->getTimestamp())
675
            ->setExpiration($expiration->getTimestamp())
676
            ->set(SessionMiddleware::SESSION_CLAIM, $claim)
677
            ->sign($this->getSigner($middleware), $this->getSignatureKey($middleware))
678
            ->getToken();
679
    }
680
681
    private function emptyValidationMiddleware() : RequestHandlerInterface
682
    {
683
        return $this->fakeDelegate(
684
            function (ServerRequestInterface $request) {
685
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
686
687
                self::assertInstanceOf(SessionInterface::class, $session);
688
                self::assertTrue($session->isEmpty());
689
690
                return new Response();
691
            }
692
        );
693
    }
694
695
    private function writingMiddleware(string $value = 'bar') : RequestHandlerInterface
696
    {
697
        return $this->fakeDelegate(
698
            function (ServerRequestInterface $request) use ($value) {
699
                /** @var SessionInterface $session */
700
                $session = $request->getAttribute(SessionMiddleware::SESSION_ATTRIBUTE);
701
                $session->set('foo', $value);
702
703
                return new Response();
704
            }
705
        );
706
    }
707
708
    private function fakeDelegate(callable $callback) : RequestHandlerInterface
709
    {
710
        $middleware = $this->createMock(RequestHandlerInterface::class);
711
712
        $middleware
713
            ->expects(self::once())
714
           ->method('handle')
715
           ->willReturnCallback($callback)
716
           ->with(self::isInstanceOf(RequestInterface::class));
717
718
        return $middleware;
719
    }
720
721
    /**
722
     *
723
     * @return ServerRequest
724
     */
725
    private function requestWithResponseCookies(ResponseInterface $response) : ServerRequestInterface
726
    {
727
        return (new ServerRequest())->withCookieParams([
728
            SessionMiddleware::DEFAULT_COOKIE => $this->getCookie($response)->getValue(),
729
        ]);
730
    }
731
732
    private function getCookie(ResponseInterface $response, string $name = SessionMiddleware::DEFAULT_COOKIE) : SetCookie
733
    {
734
        return FigResponseCookies::get($response, $name);
735
    }
736
737
    private function getSigner(SessionMiddleware $middleware) : Signer
738
    {
739
        return self::getObjectAttribute($middleware, 'signer');
740
    }
741
742
    private function getSignatureKey(SessionMiddleware $middleware) : string
743
    {
744
        return self::getObjectAttribute($middleware, 'signatureKey');
745
    }
746
747
    private static function privateKey() : string
748
    {
749
        $key = file_get_contents(__DIR__ . '/../../keys/private_key.pem');
750
751
        self::assertInternalType('string', $key);
752
753
        return $key;
754
    }
755
756
    private static function publicKey() : string
757
    {
758
        $key = file_get_contents(__DIR__ . '/../../keys/public_key.pem');
759
760
        self::assertInternalType('string', $key);
761
762
        return $key;
763
    }
764
}
765