Skip to content

Commit 8ef11c9

Browse files
committed
WIP JwtVcJson
1 parent 55c90e7 commit 8ef11c9

16 files changed

Lines changed: 769 additions & 61 deletions

src/Codebooks/AtContextsEnum.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\OpenID\Codebooks;
6+
7+
enum AtContextsEnum: string
8+
{
9+
case W3Org2018CredentialsV1 = 'https://www.w3.org/2018/credentials/v1';
10+
}

src/Codebooks/ClaimsEnum.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
enum ClaimsEnum: string
88
{
9+
// @context
10+
case AtContext = '@context';
11+
// @type
12+
case AtType = '@type';
913
case AcrValuesSupported = 'acr_values_supported';
1014
// Algorithm
1115
case Alg = 'alg';
@@ -42,6 +46,7 @@ enum ClaimsEnum: string
4246
case CredentialResponseEncryption = 'credential_response_encryption';
4347
// CredentialSigningAlgorithmValuesSupported
4448
case CredentialSigningAlgValuesSupported = 'credential_signing_alg_values_supported';
49+
case CredentialSubject = 'credential_subject';
4550
case CryptographicBindingMethodsSupported = 'cryptographic_binding_methods_supported';
4651
case DeferredCredentialEndpoint = 'deferred_credential_endpoint';
4752
case Delegation = 'delegation';
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44

55
namespace SimpleSAML\OpenID\Exceptions;
66

7-
class JwtVcJsonException extends JwsException
7+
class VcDataModelException extends JwsException
88
{
99
}

src/Factories/ClaimFactory.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@
1010
use SimpleSAML\OpenID\Exceptions\JwksException;
1111
use SimpleSAML\OpenID\Federation\Factories\FederationClaimFactory;
1212
use SimpleSAML\OpenID\Helpers;
13+
use SimpleSAML\OpenID\VerifiableCredentials\VcDataModel\Factories\VcDataModelClaimFactory;
1314

1415
class ClaimFactory
1516
{
1617
protected FederationClaimFactory $federationClaimFactory;
1718

19+
protected VcDataModelClaimFactory $vcDataModelClaimFactory;
20+
1821
public function __construct(
1922
protected readonly Helpers $helpers,
2023
) {
@@ -28,6 +31,14 @@ public function forFederation(): FederationClaimFactory
2831
);
2932
}
3033

34+
public function forVcDataModel(): VcDataModelClaimFactory
35+
{
36+
return $this->vcDataModelClaimFactory ??= new VcDataModelClaimFactory(
37+
$this->helpers,
38+
$this,
39+
);
40+
}
41+
3142
/**
3243
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
3344
*/

src/Helpers/Arr.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,28 @@ public function getNestedValue(array $array, int|string ...$keys): mixed
5656

5757
return $this->getNestedValue($nestedArray, ...$keys);
5858
}
59+
60+
/**
61+
* @param mixed[] $array
62+
*/
63+
public function isAssociative(array $array): bool
64+
{
65+
// Has at least one string key or non-sequential numeric keys
66+
return array_keys($array) !== range(0, count($array) - 1);
67+
}
68+
69+
/**
70+
* Is array of arrays.
71+
* @param mixed[] $array
72+
*/
73+
public function isOfArrays(array $array): bool
74+
{
75+
foreach ($array as $value) {
76+
if (!is_array($value)) {
77+
return false;
78+
}
79+
}
80+
81+
return true;
82+
}
5983
}

src/Helpers/Type.php

Lines changed: 156 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ public function ensureString(mixed $value, ?string $context = null): string
2424
return (string)$value;
2525
}
2626

27-
$error = 'Unsafe string casting, aborting.';
28-
$error .= is_string($context) ? ' Context: ' . $context : '';
29-
$error .= ' Value was: ' . var_export($value, true);
27+
$error = $this->prepareErrorMessage(
28+
'Unsafe string casting, aborting.',
29+
$value,
30+
$context,
31+
);
3032

3133
throw new InvalidValueException($error);
3234
}
@@ -43,9 +45,11 @@ public function ensureNonEmptyString(mixed $value, ?string $context = null): str
4345
return $value;
4446
}
4547

46-
$error = 'Empty string value encountered, aborting.';
47-
$error .= is_string($context) ? ' Context: ' . $context : '';
48-
$error .= ' Value was: ' . var_export($value, true);
48+
$error = $this->prepareErrorMessage(
49+
'Empty string value encountered, aborting.',
50+
$value,
51+
$context,
52+
);
4953

5054
throw new InvalidValueException($error);
5155
}
@@ -73,9 +77,11 @@ public function ensureArray(mixed $value, ?string $context = null): array
7377
// Converts object properties to an array
7478
}
7579

76-
$error = 'Unsafe array casting, aborting.';
77-
$error .= is_string($context) ? 'Context: ' . $context : '';
78-
$error .= ' Value was: ' . var_export($value, true);
80+
$error = $this->prepareErrorMessage(
81+
'Unsafe array casting, aborting.',
82+
$value,
83+
$context,
84+
);
7985

8086
throw new InvalidValueException($error);
8187
}
@@ -203,10 +209,148 @@ public function ensureInt(mixed $value, ?string $context = null): int
203209
return (int)$value;
204210
}
205211

206-
$error = 'Unsafe integer casting, aborting.';
207-
$error .= is_string($context) ? 'Context: ' . $context : '';
208-
$error .= ' Value was: ' . var_export($value, true);
212+
$error = $this->prepareErrorMessage(
213+
'Unsafe integer casting, aborting.',
214+
$value,
215+
$context,
216+
);
209217

210218
throw new InvalidValueException($error);
211219
}
220+
221+
/**
222+
* @return non-empty-string
223+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
224+
*/
225+
public function enforceRegex(
226+
mixed $value,
227+
string $pattern,
228+
?string $context = null,
229+
): string {
230+
$value = $this->ensureNonEmptyString($value, $context);
231+
232+
$error = $this->prepareErrorMessage(
233+
'Regex match failed, aborting.',
234+
$value,
235+
$context,
236+
);
237+
238+
preg_match($pattern, $value) || throw new InvalidValueException($error);
239+
240+
return $value;
241+
}
242+
243+
/**
244+
* @return non-empty-string
245+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
246+
*/
247+
public function enforceUri(
248+
mixed $value,
249+
?string $context = null,
250+
string $pattern = '/^[^:]+:\/\/?([^\s\/$.?#].[^\s]*)?$/',
251+
): string {
252+
try {
253+
$value = $this->enforceRegex($value, $pattern, $context);
254+
} catch (InvalidValueException) {
255+
$error = $this->prepareErrorMessage(
256+
'URI regex match failed, aborting.',
257+
$value,
258+
$context,
259+
);
260+
261+
throw new InvalidValueException($error);
262+
}
263+
264+
return $value;
265+
}
266+
267+
/**
268+
* @param mixed[] $array
269+
* @return array<mixed[]>
270+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
271+
*/
272+
public function enforceArrayOfArrays(array $array, ?string $context = null): array
273+
{
274+
foreach ($array as $value) {
275+
if (!is_array($value)) {
276+
$error = $this->prepareErrorMessage(
277+
'Non-array value encountered, aborting.',
278+
$array,
279+
$context,
280+
);
281+
282+
throw new InvalidValueException($error);
283+
}
284+
}
285+
286+
/** @var array<mixed[]> $array */
287+
return $array;
288+
}
289+
290+
/**
291+
* @param mixed[] $array
292+
* @return non-empty-array<mixed>
293+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
294+
*/
295+
public function enforceNonEmptyArray(array $array, ?string $context = null): array
296+
{
297+
if ($array === []) {
298+
$error = $this->prepareErrorMessage(
299+
'Empty array encountered, aborting.',
300+
$array,
301+
$context,
302+
);
303+
throw new InvalidValueException($error);
304+
}
305+
306+
return $array;
307+
}
308+
309+
/**
310+
* @param mixed[] $array
311+
* @return non-empty-array<non-empty-string>
312+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
313+
*/
314+
public function enforceNonEmptyArrayWithValuesAsNonEmptyStrings(array $array, ?string $context = null): array
315+
{
316+
$array = $this->ensureArrayWithValuesAsNonEmptyStrings($array, $context);
317+
$array = $this->enforceNonEmptyArray($array, $context);
318+
319+
/** @var non-empty-array<non-empty-string> $array */
320+
return $array;
321+
}
322+
323+
/**
324+
* @param mixed[] $array
325+
* @return non-empty-array<non-empty-array>
326+
* @throws \SimpleSAML\OpenID\Exceptions\InvalidValueException
327+
*/
328+
public function enforceNonEmptyArrayOfNonEmptyArrays(array $array, ?string $context = null): array
329+
{
330+
$array = $this->enforceNonEmptyArray($array, $context);
331+
332+
foreach ($array as $value) {
333+
if (!is_array($value)) {
334+
$error = $this->prepareErrorMessage(
335+
'Non-array value encountered, aborting.',
336+
$array,
337+
$context,
338+
);
339+
340+
throw new InvalidValueException($error);
341+
}
342+
343+
$this->enforceNonEmptyArray($value, $context);
344+
}
345+
346+
/** @var non-empty-array<non-empty-array> $array */
347+
return $array;
348+
}
349+
350+
protected function prepareErrorMessage(string $message, mixed $value, ?string $context = null): string
351+
{
352+
return $message .
353+
(is_string($context) ? ' Context: ' . $context : '') .
354+
' Value was: ' . var_export($value, true);
355+
}
212356
}

src/Jws/ParsedJws.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ public function getPayloadClaim(string $key): mixed
9595
return $this->getPayload()[$key] ?? null;
9696
}
9797

98+
public function getNestedPayloadClaim(int|string ...$keys): mixed
99+
{
100+
return $this->helpers->arr()->getNestedValue(
101+
$this->getPayload(),
102+
...$keys,
103+
);
104+
}
105+
98106
public function getToken(
99107
JwsSerializerEnum $jwsSerializerEnum = JwsSerializerEnum::Compact,
100108
?int $signatureIndex = null,

src/VerifiableCredentials/JwtVcJson.php

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)