testShouldHandleRequestWithoutAuthorization()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
c 0
b 0
f 0
rs 9.488
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Facile\OAuth2\HttpClient\Test;
6
7
use Facile\OAuth2\HttpClient\Authorization\AuthorizationProvider;
8
use Facile\OAuth2\HttpClient\OAuth2Plugin;
9
use Facile\OAuth2\HttpClient\Request\OAuth2Request;
10
use Facile\OAuth2\HttpClient\Request\OAuth2RequestInterface;
11
use Facile\OpenIDClient\Client\ClientInterface;
12
use Facile\OpenIDClient\Service\AuthorizationService;
13
use Facile\OpenIDClient\Token\TokenSetInterface;
14
use Http\Client\Common\Plugin;
15
use Http\Promise\FulfilledPromise;
16
use Laminas\Diactoros\RequestFactory;
17
use PHPUnit\Framework\TestCase;
18
use Prophecy\Argument;
19
use Psr\Http\Message\RequestInterface;
20
use Psr\Http\Message\ResponseInterface;
21
22
class OAuth2PluginTest extends TestCase
23
{
24
    public function testShouldHandleRequestWithNoAuthorization(): void
25
    {
26
        $psrRequest = (new RequestFactory())->createRequest('GET', 'https://example.com/foo');
27
        $request = (new OAuth2Request($psrRequest))
28
            ->withGrantParams([
29
                'actor_token' => 'foo',
30
            ]);
31
32
        $authorizationService = $this->prophesize(AuthorizationService::class);
33
        $client = $this->prophesize(ClientInterface::class);
34
35
        $plugin = new OAuth2Plugin(
36
            $authorizationService->reveal(),
37
            $client->reveal(),
38
            null,
39
            [],
40
            false
41
        );
42
43
        $response = $this->prophesize(ResponseInterface::class);
44
        $response->getStatusCode()->shouldBeCalled()->willReturn(200);
45
46
        $next = function () use ($response) {
47
            return new FulfilledPromise($response->reveal());
48
        };
49
        $first = static function (): void {
50
        };
51
52
        $responsePromise = $plugin->handleRequest($request, $next, $first);
53
        $this->assertSame($response->reveal(), $responsePromise->wait());
54
    }
55
56
    public function authZRequestProvider(): iterable
57
    {
58
        yield 'Normal Request' => [(new RequestFactory())->createRequest('GET', 'https://example.com/foo')];
59
        yield 'OAuth2Request' => [
60
            (new OAuth2Request((new RequestFactory())->createRequest('GET', 'https://example.com/foo')))
61
                ->withGrantParams([
62
                    'actor_token' => 'foo',
63
                ]),
64
        ];
65
    }
66
67
    /**
68
     * @dataProvider authZRequestProvider
69
     *
70
     * @param RequestInterface $request
71
     */
72
    public function testShouldHandleRequestWithRequestedAuthorization(RequestInterface $request): void
73
    {
74
        $requestGrantParams = $request instanceof OAuth2RequestInterface
75
            ? $request->getGrantParams()
76
            : [];
77
        $defaultGrantParams = [
78
            'token_type' => 'bar',
79
            'actor_token' => 'baz',
80
        ];
81
82
        $authorizationService = $this->prophesize(AuthorizationService::class);
83
        $client = $this->prophesize(ClientInterface::class);
84
85
        $plugin = new OAuth2Plugin(
86
            $authorizationService->reveal(),
87
            $client->reveal(),
88
            null,
89
            $defaultGrantParams,
90
            false
91
        );
92
93
        $response = $this->prophesize(ResponseInterface::class);
94
        $response->getStatusCode()->shouldBeCalled()->willReturn(401);
95
96
        $authorizedResponse = $this->prophesize(ResponseInterface::class);
97
98
        $tokenSet = $this->prophesize(TokenSetInterface::class);
99
        $tokenSet->getAccessToken()->willReturn('access-token');
100
        $tokenSet->getExpiresIn()->willReturn(999);
101
        $tokenSet->getTokenType()->willReturn('bearer');
102
103
        $authorizationService->grant($client->reveal(), array_merge(
104
            ['grant_type' => 'client_credentials'],
105
            $defaultGrantParams,
106
            $requestGrantParams
107
        ))
108
            ->shouldBeCalled()
109
            ->willReturn($tokenSet->reveal());
110
111
        $this->assertExecution(
112
            $plugin,
113
            $request,
114
            $response->reveal(),
115
            $authorizedResponse->reveal()
116
        );
117
    }
118
119
    public function testShouldHandleRequestWithoutAuthorization(): void
120
    {
121
        $request = (new RequestFactory())->createRequest('GET', 'https://example.com/foo');
122
        $authorizationService = $this->prophesize(AuthorizationService::class);
123
        $client = $this->prophesize(ClientInterface::class);
124
125
        $plugin = new OAuth2Plugin(
126
            $authorizationService->reveal(),
127
            $client->reveal(),
128
            null,
129
            [],
130
            false
131
        );
132
133
        $response = $this->prophesize(ResponseInterface::class);
134
        $response->getStatusCode()->shouldBeCalled()->willReturn(200);
135
136
        $authorizationService->grant(Argument::cetera())
137
            ->shouldNotBeCalled();
138
139
        $this->assertExecution(
140
            $plugin,
141
            $request,
142
            $response->reveal(),
143
            $response->reveal()
144
        );
145
    }
146
147
    public function testShouldNotCallAuthorizationServiceWithRequestWithAuthorizationHeader(): void
148
    {
149
        $request = (new RequestFactory())->createRequest('GET', 'https://example.com/foo')
150
            ->withHeader('Authorization', 'foo');
151
152
        $authorizationService = $this->prophesize(AuthorizationService::class);
153
        $client = $this->prophesize(ClientInterface::class);
154
155
        $plugin = new OAuth2Plugin(
156
            $authorizationService->reveal(),
157
            $client->reveal(),
158
            null,
159
            [],
160
            true
161
        );
162
163
        $response = $this->prophesize(ResponseInterface::class);
164
        $response->getStatusCode()->shouldBeCalled()->willReturn(200);
165
166
        $authorizationService->grant(Argument::cetera())
167
            ->shouldNotBeCalled();
168
169
        $next = function (RequestInterface $request) use ($response) {
170
            $this->assertTrue($request->hasHeader('Authorization'));
171
            $this->assertSame('foo', $request->getHeader('Authorization')[0]);
172
173
            return new FulfilledPromise($response->reveal());
174
        };
175
176
        $first = static function (): void {
177
        };
178
179
        $responsePromise = $plugin->handleRequest($request, $next, $first);
180
        $this->assertSame($response->reveal(), $responsePromise->wait());
181
    }
182
183
    /**
184
     * @dataProvider authZRequestProvider
185
     *
186
     * @param RequestInterface $request
187
     */
188
    public function testShouldAuthorizeBeforeCallWhenAuthorizationFirst(RequestInterface $request): void
189
    {
190
        $requestGrantParams = $request instanceof OAuth2RequestInterface
191
            ? $request->getGrantParams()
192
            : [];
193
        $defaultGrantParams = [
194
            'token_type' => 'bar',
195
            'actor_token' => 'baz',
196
        ];
197
198
        $authorizationService = $this->prophesize(AuthorizationService::class);
199
        $client = $this->prophesize(ClientInterface::class);
200
201
        $plugin = new OAuth2Plugin(
202
            $authorizationService->reveal(),
203
            $client->reveal(),
204
            null,
205
            $defaultGrantParams,
206
            true
207
        );
208
209
        $response = $this->prophesize(ResponseInterface::class);
210
        $response->getStatusCode()->shouldBeCalled()->willReturn(200);
211
212
        $tokenSet = $this->prophesize(TokenSetInterface::class);
213
        $tokenSet->getAccessToken()->willReturn('access-token');
214
        $tokenSet->getExpiresIn()->willReturn(999);
215
        $tokenSet->getTokenType()->willReturn('bearer');
216
217
        $authorizationService->grant($client->reveal(), array_merge(
218
            ['grant_type' => 'client_credentials'],
219
            $defaultGrantParams,
220
            $requestGrantParams
221
        ))
222
            ->shouldBeCalled()
223
            ->willReturn($tokenSet->reveal());
224
225
        $next = function (RequestInterface $request) use ($response) {
226
            $this->assertSame('Bearer access-token', $request->getHeader('Authorization')[0]);
227
228
            return new FulfilledPromise($response->reveal());
229
        };
230
231
        $first = static function (): void {
232
        };
233
234
        $responsePromise = $plugin->handleRequest($request, $next, $first);
235
        $this->assertSame($response->reveal(), $responsePromise->wait());
236
    }
237
238
    public function testShouldUseProvidedAuthorization(): void
239
    {
240
        $request = (new OAuth2Request((new RequestFactory())->createRequest('GET', 'https://example.com/foo')))
241
            ->withGrantParams([
242
                'actor_token' => 'foo',
243
            ]);
244
        $defaultGrantParams = [
245
            'token_type' => 'bar',
246
            'actor_token' => 'baz',
247
        ];
248
249
        $authorizationProvider = $this->prophesize(AuthorizationProvider::class);
250
        $authorizationService = $this->prophesize(AuthorizationService::class);
251
        $client = $this->prophesize(ClientInterface::class);
252
253
        $plugin = new OAuth2Plugin(
254
            $authorizationService->reveal(),
255
            $client->reveal(),
256
            $authorizationProvider->reveal(),
257
            $defaultGrantParams
258
        );
259
260
        $response = $this->prophesize(ResponseInterface::class);
261
        $response->getStatusCode()->shouldBeCalled()->willReturn(200);
262
263
        $authorizationProvider->getAuthorization($client->reveal(), Argument::type(OAuth2RequestInterface::class))
264
            ->shouldBeCalled()
265
            ->willReturn('bearer foo');
266
267
        $authorizationService->grant(Argument::cetera())
268
            ->shouldNotBeCalled();
269
270
        $next = function (RequestInterface $request) use ($response) {
271
            $this->assertTrue($request->hasHeader('Authorization'));
272
            $this->assertSame('bearer foo', $request->getHeader('Authorization')[0]);
273
274
            return new FulfilledPromise($response->reveal());
275
        };
276
277
        $first = static function (): void {
278
        };
279
280
        $responsePromise = $plugin->handleRequest($request, $next, $first);
281
        $this->assertSame($response->reveal(), $responsePromise->wait());
282
    }
283
284
    public function testShouldUpdateAuthorizationAndSaveIt(): void
285
    {
286
        $requestGrantParams = [
287
            'actor_token' => 'foo',
288
        ];
289
        $defaultGrantParams = [
290
            'token_type' => 'bar',
291
            'actor_token' => 'baz',
292
        ];
293
        $request = (new OAuth2Request((new RequestFactory())->createRequest('GET', 'https://example.com/foo')))
294
            ->withGrantParams($requestGrantParams);
295
296
        $authorizationProvider = $this->prophesize(AuthorizationProvider::class);
297
        $authorizationService = $this->prophesize(AuthorizationService::class);
298
        $client = $this->prophesize(ClientInterface::class);
299
300
        $plugin = new OAuth2Plugin(
301
            $authorizationService->reveal(),
302
            $client->reveal(),
303
            $authorizationProvider->reveal(),
304
            $defaultGrantParams
305
        );
306
307
        $response = $this->prophesize(ResponseInterface::class);
308
        $response->getStatusCode()->shouldBeCalled()->willReturn(403);
309
310
        $authorizedResponse = $this->prophesize(ResponseInterface::class);
311
312
        $authorizationProvider->getAuthorization($client->reveal(), Argument::type(OAuth2RequestInterface::class))
313
            ->shouldBeCalled()
314
            ->willReturn('Bearer access-token1');
315
316
        $authorizationProvider->saveAuthorization(
317
            $client->reveal(),
318
            Argument::type(OAuth2RequestInterface::class),
319
            'Bearer access-token2',
320
            999
321
        )
322
            ->shouldBeCalled();
323
324
        $tokenSet = $this->prophesize(TokenSetInterface::class);
325
        $tokenSet->getAccessToken()->willReturn('access-token2');
326
        $tokenSet->getExpiresIn()->willReturn(999);
327
        $tokenSet->getTokenType()->willReturn('bearer');
328
329
        $authorizationService->grant($client->reveal(), array_merge(
330
            ['grant_type' => 'client_credentials'],
331
            $defaultGrantParams,
332
            $requestGrantParams
333
        ))
334
            ->shouldBeCalled()
335
            ->willReturn($tokenSet->reveal());
336
337
        $nextFuncs = [
338
            function (RequestInterface $request) use ($response) {
339
                $this->assertTrue($request->hasHeader('Authorization'));
340
                $this->assertSame('Bearer access-token1', $request->getHeader('Authorization')[0]);
341
342
                return new FulfilledPromise($response->reveal());
343
            },
344
            function (RequestInterface $request) use ($authorizedResponse) {
345
                $this->assertTrue($request->hasHeader('Authorization'));
346
                $this->assertSame('Bearer access-token2', $request->getHeader('Authorization')[0]);
347
348
                return new FulfilledPromise($authorizedResponse->reveal());
349
            },
350
        ];
351
352
        $nextCount = 0;
353
354
        $next = static function (RequestInterface $request) use ($nextFuncs, &$nextCount) {
355
            return ($nextFuncs[$nextCount++])($request);
356
        };
357
358
        $first = static function (): void {
359
        };
360
361
        $responsePromise = $plugin->handleRequest($request, $next, $first);
362
        $this->assertSame($authorizedResponse->reveal(), $responsePromise->wait());
363
    }
364
365
    private function assertExecution(
366
        Plugin $plugin,
367
        RequestInterface $request,
368
        ResponseInterface $response,
369
        ResponseInterface $authorizedResponse
370
    ): void {
371
        $nextFuncs = [
372
            function (RequestInterface $request) use ($response) {
373
                $this->assertFalse($request->hasHeader('Authorization'));
374
375
                return new FulfilledPromise($response);
376
            },
377
            function (RequestInterface $request) use ($authorizedResponse) {
378
                $this->assertTrue($request->hasHeader('Authorization'));
379
                $this->assertSame('Bearer access-token', $request->getHeader('Authorization')[0]);
380
381
                return new FulfilledPromise($authorizedResponse);
382
            },
383
        ];
384
385
        $nextCount = 0;
386
387
        $next = static function (RequestInterface $request) use ($nextFuncs, &$nextCount) {
388
            return ($nextFuncs[$nextCount++])($request);
389
        };
390
391
        $first = static function (): void {
392
        };
393
394
        $responsePromise = $plugin->handleRequest($request, $next, $first);
395
        $this->assertSame($authorizedResponse, $responsePromise->wait());
396
    }
397
}
398