Skip to content

Commit a8fc518

Browse files
author
ogorkun
committed
MC-38539: Introduce JWT wrapper
1 parent 4fb151c commit a8fc518

File tree

8 files changed

+668
-0
lines changed

8 files changed

+668
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\JwtFrameworkAdapter\Test\Unit\Model;
10+
11+
use Magento\Framework\Jwt\HeaderInterface;
12+
use Magento\Framework\Jwt\Payload\ArbitraryPayload;
13+
use Magento\Framework\Jwt\Payload\ClaimsPayloadInterface;
14+
use Magento\Framework\Jwt\Payload\NestedPayloadInterface;
15+
use Magento\JwtFrameworkAdapter\Model\JweFactory;
16+
use PHPUnit\Framework\TestCase;
17+
18+
class JweFactoryTest extends TestCase
19+
{
20+
/**
21+
* @var JweFactory
22+
*/
23+
private $model;
24+
25+
/**
26+
* @inheritDoc
27+
*/
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
$this->model = new JweFactory();
33+
}
34+
35+
public function getCreateCases(): array
36+
{
37+
return [
38+
'compact-arbitrary' => [
39+
['cty' => 'MyType', 'typ' => 'JWT'],
40+
'some-value',
41+
null,
42+
null,
43+
ArbitraryPayload::class
44+
],
45+
'compact-claims' => [
46+
['typ' => 'JWT'],
47+
'{"tst1":"val1","tst2":2,"tst3":true}',
48+
null,
49+
null,
50+
ClaimsPayloadInterface::class
51+
],
52+
'compact-nested' => [
53+
['typ' => 'JWT', 'cty' => NestedPayloadInterface::CONTENT_TYPE],
54+
'eyJhbGciOiJub25lIn0.'
55+
.'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.',
56+
null,
57+
null,
58+
NestedPayloadInterface::class
59+
],
60+
'json-arbitrary' => [
61+
['typ' => 'JWT'],
62+
'arbitrary',
63+
['cty' => 'SomeType'],
64+
['crit' => 'exp'],
65+
ArbitraryPayload::class
66+
],
67+
'json-claims' => [
68+
['typ' => 'JWT'],
69+
'{"tst1":"val1","tst2":2,"tst3":true}',
70+
['aud' => 'magento'],
71+
['custom' => 'value'],
72+
ClaimsPayloadInterface::class
73+
]
74+
];
75+
}
76+
77+
/**
78+
* Test "create" method.
79+
*
80+
* @param array $headers
81+
* @param string $content
82+
* @param array|null $unprotected
83+
* @param array|null $perRecipient
84+
* @param string $payloadClass
85+
* @return void
86+
* @dataProvider getCreateCases
87+
*/
88+
public function testCreate(
89+
array $headers,
90+
string $content,
91+
?array $unprotected,
92+
?array $perRecipient,
93+
string $payloadClass
94+
): void {
95+
$jwe = $this->model->create($headers, $content, $unprotected, $perRecipient);
96+
97+
$payload = $jwe->getPayload();
98+
$this->assertEquals($content, $payload->getContent());
99+
$this->assertInstanceOf($payloadClass, $payload);
100+
if ($payload instanceof ClaimsPayloadInterface) {
101+
$actualClaims = [];
102+
foreach ($payload->getClaims() as $claim) {
103+
$actualClaims[$claim->getName()] = $claim->getValue();
104+
}
105+
$this->assertEquals(json_decode($content, true), $actualClaims);
106+
}
107+
108+
$this->validateHeader($headers, $jwe->getHeader());
109+
if ($unprotected === null) {
110+
$this->assertNull($jwe->getSharedUnprotectedHeader());
111+
} else {
112+
$this->assertNotNull($jwe->getSharedUnprotectedHeader());
113+
$this->validateHeader($unprotected, $jwe->getSharedUnprotectedHeader());
114+
}
115+
if ($perRecipient === null) {
116+
$this->assertNull($jwe->getSharedUnprotectedHeader());
117+
} else {
118+
$this->assertNotNull($jwe->getSharedUnprotectedHeader());
119+
$this->validateHeader($unprotected, $jwe->getSharedUnprotectedHeader());
120+
}
121+
}
122+
123+
private function validateHeader(array $expectedHeaders, HeaderInterface $actual): void
124+
{
125+
foreach ($expectedHeaders as $header => $value) {
126+
$parameter = $actual->getParameter($header);
127+
$this->assertNotNull($parameter);
128+
$this->assertEquals($value, $parameter->getValue());
129+
}
130+
}
131+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\JwtFrameworkAdapter\Test\Unit\Model;
10+
11+
use Magento\Framework\Jwt\HeaderInterface;
12+
use Magento\Framework\Jwt\Payload\ArbitraryPayload;
13+
use Magento\Framework\Jwt\Payload\ClaimsPayloadInterface;
14+
use Magento\Framework\Jwt\Payload\NestedPayloadInterface;
15+
use Magento\JwtFrameworkAdapter\Model\JwsFactory;
16+
use PHPUnit\Framework\TestCase;
17+
18+
class JwsFactoryTest extends TestCase
19+
{
20+
/**
21+
* @var JwsFactory
22+
*/
23+
private $model;
24+
25+
/**
26+
* @inheritDoc
27+
*/
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
$this->model = new JwsFactory();
33+
}
34+
35+
public function getCreateCases(): array
36+
{
37+
return [
38+
'compact-arbitrary' => [
39+
['cty' => 'MyType', 'typ' => 'JWT'],
40+
'some-value',
41+
null,
42+
ArbitraryPayload::class
43+
],
44+
'compact-claims' => [
45+
['typ' => 'JWT'],
46+
'{"tst1":"val1","tst2":2,"tst3":true}',
47+
null,
48+
ClaimsPayloadInterface::class
49+
],
50+
'compact-nested' => [
51+
['typ' => 'JWT', 'cty' => NestedPayloadInterface::CONTENT_TYPE],
52+
'eyJhbGciOiJub25lIn0.'
53+
.'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.',
54+
null,
55+
NestedPayloadInterface::class
56+
],
57+
'json-arbitrary' => [
58+
['typ' => 'JWT'],
59+
'arbitrary',
60+
['cty' => 'SomeType'],
61+
ArbitraryPayload::class
62+
],
63+
'json-claims' => [
64+
['typ' => 'JWT'],
65+
'{"tst1":"val1","tst2":2,"tst3":true}',
66+
['aud' => 'magento'],
67+
ClaimsPayloadInterface::class
68+
]
69+
];
70+
}
71+
72+
/**
73+
* Test "create" method.
74+
*
75+
* @param array $headers
76+
* @param string $content
77+
* @param array|null $unprotected
78+
* @param string $payloadClass
79+
* @return void
80+
* @dataProvider getCreateCases
81+
*/
82+
public function testCreate(
83+
array $headers,
84+
string $content,
85+
?array $unprotected,
86+
string $payloadClass
87+
): void {
88+
$jws = $this->model->create($headers, $content, $unprotected);
89+
90+
$payload = $jws->getPayload();
91+
$this->assertEquals($content, $payload->getContent());
92+
$this->assertInstanceOf($payloadClass, $payload);
93+
if ($payload instanceof ClaimsPayloadInterface) {
94+
$actualClaims = [];
95+
foreach ($payload->getClaims() as $claim) {
96+
$actualClaims[$claim->getName()] = $claim->getValue();
97+
}
98+
$this->assertEquals(json_decode($content, true), $actualClaims);
99+
}
100+
101+
$this->validateHeader($headers, $jws->getHeader());
102+
if ($unprotected === null) {
103+
$this->assertEmpty($jws->getUnprotectedHeaders());
104+
} else {
105+
$this->assertNotEmpty($jws->getUnprotectedHeaders());
106+
$this->validateHeader($unprotected, array_values($jws->getUnprotectedHeaders())[0]);
107+
}
108+
}
109+
110+
private function validateHeader(array $expectedHeaders, HeaderInterface $actual): void
111+
{
112+
foreach ($expectedHeaders as $header => $value) {
113+
$parameter = $actual->getParameter($header);
114+
$this->assertNotNull($parameter);
115+
$this->assertEquals($value, $parameter->getValue());
116+
}
117+
}
118+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\JwtFrameworkAdapter\Test\Unit\Model;
10+
11+
use Magento\Framework\Jwt\HeaderInterface;
12+
use Magento\Framework\Jwt\Payload\ArbitraryPayload;
13+
use Magento\Framework\Jwt\Payload\ClaimsPayloadInterface;
14+
use Magento\Framework\Jwt\Payload\NestedPayloadInterface;
15+
use Magento\JwtFrameworkAdapter\Model\UnsecuredJwtFactory;
16+
use PHPUnit\Framework\TestCase;
17+
18+
class UnsecuredJwtFactoryTest extends TestCase
19+
{
20+
/**
21+
* @var UnsecuredJwtFactory
22+
*/
23+
private $model;
24+
25+
/**
26+
* @inheritDoc
27+
*/
28+
protected function setUp(): void
29+
{
30+
parent::setUp();
31+
32+
$this->model = new UnsecuredJwtFactory();
33+
}
34+
35+
public function getCreateCases(): array
36+
{
37+
return [
38+
'compact-arbitrary' => [
39+
[['cty' => 'MyType', 'typ' => 'JWT']],
40+
'some-value',
41+
null,
42+
ArbitraryPayload::class
43+
],
44+
'compact-claims' => [
45+
[['typ' => 'JWT']],
46+
'{"tst1":"val1","tst2":2,"tst3":true}',
47+
null,
48+
ClaimsPayloadInterface::class
49+
],
50+
'compact-nested' => [
51+
[['typ' => 'JWT', 'cty' => NestedPayloadInterface::CONTENT_TYPE]],
52+
'eyJhbGciOiJub25lIn0.'
53+
.'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.',
54+
null,
55+
NestedPayloadInterface::class
56+
],
57+
'json-flat-arbitrary' => [
58+
[['typ' => 'JWT']],
59+
'arbitrary',
60+
[['cty' => 'SomeType']],
61+
ArbitraryPayload::class
62+
],
63+
'json-flat-claims' => [
64+
[['typ' => 'JWT']],
65+
'{"tst1":"val1","tst2":2,"tst3":true}',
66+
[['aud' => 'magento']],
67+
ClaimsPayloadInterface::class
68+
],
69+
'json-arbitrary' => [
70+
[['typ' => 'JWT'], ['typ' => 'JWT', 'aud' => 'magento']],
71+
'value',
72+
[['cty' => 'MyType'], ['cty' => 'MyType', 'crit' => 'exp']],
73+
ArbitraryPayload::class
74+
]
75+
];
76+
}
77+
78+
/**
79+
* Test "create" method.
80+
*
81+
* @param array $headers
82+
* @param string $content
83+
* @param array|null $unprotected
84+
* @param string $payloadClass
85+
* @return void
86+
* @dataProvider getCreateCases
87+
*/
88+
public function testCreate(
89+
array $headers,
90+
string $content,
91+
?array $unprotected,
92+
string $payloadClass
93+
): void {
94+
$jwt = $this->model->create($headers, $unprotected, $content);
95+
96+
$payload = $jwt->getPayload();
97+
$this->assertEquals($content, $payload->getContent());
98+
$this->assertInstanceOf($payloadClass, $payload);
99+
if ($payload instanceof ClaimsPayloadInterface) {
100+
$actualClaims = [];
101+
foreach ($payload->getClaims() as $claim) {
102+
$actualClaims[$claim->getName()] = $claim->getValue();
103+
}
104+
$this->assertEquals(json_decode($content, true), $actualClaims);
105+
}
106+
107+
$actualHeaders = array_map([$this, 'extractHeader'], $jwt->getProtectedHeaders());
108+
$this->assertEquals($headers, $actualHeaders);
109+
}
110+
111+
private function extractHeader(HeaderInterface $header): array
112+
{
113+
$values = [];
114+
foreach ($header->getParameters() as $parameter) {
115+
$values[$parameter->getName()] = $parameter->getValue();
116+
}
117+
118+
return $values;
119+
}
120+
}

0 commit comments

Comments
 (0)