Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
// naming: true,
instanceOf: true,
earlyReturn: true,
strictBooleans: true,
// strictBooleans: true,
// carbon: true,
rectorPreset: true,
phpunitCodeQuality: true,
Expand Down
1 change: 1 addition & 0 deletions src/Codebooks/ClaimsEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ enum ClaimsEnum: string
case Type = 'type';
case TrustChain = 'trust_chain';
case TrustMark = 'trust_mark';
case TrustMarkIssuers = 'trust_mark_issuers';
case TrustMarkOwners = 'trust_mark_owners';
case TrustMarkType = 'trust_mark_type';
case TrustMarks = 'trust_marks';
Expand Down
64 changes: 64 additions & 0 deletions src/Federation/Claims/TrustMarkIssuersClaimBag.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\OpenID\Federation\Claims;

use JsonSerializable;

class TrustMarkIssuersClaimBag implements JsonSerializable
{
/** @var array<non-empty-string,\SimpleSAML\OpenID\Federation\Claims\TrustMarkIssuersClaimValue> */
protected array $trustMarkIssuersClaimValues = [];


public function __construct(TrustMarkIssuersClaimValue ...$trustMarkIssuersClaimValues)
{
$this->add(...$trustMarkIssuersClaimValues);
}


public function add(TrustMarkIssuersClaimValue ...$trustMarkIssuersClaimValues): void
{
foreach ($trustMarkIssuersClaimValues as $trustMarkIssuersClaimValue) {
$this->trustMarkIssuersClaimValues[$trustMarkIssuersClaimValue->getTrustMarkType()] =
$trustMarkIssuersClaimValue;
}
}


public function has(string $trustMarkType): bool
{
return isset($this->trustMarkIssuersClaimValues[$trustMarkType]);
}


public function get(string $trustMarkType): ?TrustMarkIssuersClaimValue
{
return $this->trustMarkIssuersClaimValues[$trustMarkType] ?? null;
}


/**
* @return \SimpleSAML\OpenID\Federation\Claims\TrustMarkIssuersClaimValue[]
*/
public function getAll(): array
{
return $this->trustMarkIssuersClaimValues;
}


/**
* @return array<non-empty-string,array<non-empty-string>>
*/
public function jsonSerialize(): array
{
return array_combine(
array_keys($this->trustMarkIssuersClaimValues),
array_map(
fn(TrustMarkIssuersClaimValue $tMICValue): array => $tMICValue->getTrustMarkIssuers(),
$this->trustMarkIssuersClaimValues,
),
);
}
}
49 changes: 49 additions & 0 deletions src/Federation/Claims/TrustMarkIssuersClaimValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace SimpleSAML\OpenID\Federation\Claims;

use JsonSerializable;

class TrustMarkIssuersClaimValue implements JsonSerializable
{
/**
* @param non-empty-string $trustMarkType
* @param array<non-empty-string> $trustMarkIssuers
*/
public function __construct(
protected readonly string $trustMarkType,
protected readonly array $trustMarkIssuers,
) {
}


/**
* @return non-empty-string
*/
public function getTrustMarkType(): string
{
return $this->trustMarkType;
}


/**
* @return array<non-empty-string>
*/
public function getTrustMarkIssuers(): array
{
return $this->trustMarkIssuers;
}


/**
* @return array<non-empty-string,array<non-empty-string>>
*/
public function jsonSerialize(): array
{
return [
$this->trustMarkType => $this->getTrustMarkIssuers(),
];
}
}
21 changes: 21 additions & 0 deletions src/Federation/EntityStatement.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use SimpleSAML\OpenID\Codebooks\EntityTypesEnum;
use SimpleSAML\OpenID\Codebooks\JwtTypesEnum;
use SimpleSAML\OpenID\Exceptions\EntityStatementException;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkIssuersClaimBag;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkOwnersClaimBag;
use SimpleSAML\OpenID\Federation\Claims\TrustMarksClaimBag;
use SimpleSAML\OpenID\Jws\ParsedJws;
Expand Down Expand Up @@ -234,6 +235,25 @@ public function getTrustMarkOwners(): ?TrustMarkOwnersClaimBag
}


public function getTrustMarkIssuers(): ?TrustMarkIssuersClaimBag
{
// trust_mark_issuers
// OPTIONAL. A Trust Anchor MAY use this claim to tell which combination of Trust Mark type identifiers and
// issuers are trusted by the federation. It is a JSON object with member names that are Trust Mark type
// identifiers, and each corresponding value being an array of Entity Identifiers that are trusted to
// represent the accreditation authority for Trust Marks with that identifier.

$claimKey = ClaimsEnum::TrustMarkIssuers->value;
$trustMarkIssuersClaimData = $this->getPayloadClaim($claimKey);

if (is_null($trustMarkIssuersClaimData)) {
return null;
}

return $this->claimFactory->forFederation()->buildTrustMarkIssuersClaimBagFrom($trustMarkIssuersClaimData);
}


/**
* @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
Expand Down Expand Up @@ -339,6 +359,7 @@ protected function validate(): void
$this->getMetadataPolicy(...),
$this->getTrustMarks(...),
$this->getTrustMarkOwners(...),
$this->getTrustMarkIssuers(...),
$this->getFederationFetchEndpoint(...),
$this->getFederationTrustMarkEndpoint(...),
);
Expand Down
46 changes: 46 additions & 0 deletions src/Federation/Factories/FederationClaimFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
use SimpleSAML\OpenID\Exceptions\TrustMarkException;
use SimpleSAML\OpenID\Factories\ClaimFactory;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkIssuersClaimBag;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkIssuersClaimValue;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkOwnersClaimBag;
use SimpleSAML\OpenID\Federation\Claims\TrustMarkOwnersClaimValue;
use SimpleSAML\OpenID\Federation\Claims\TrustMarksClaimBag;
Expand Down Expand Up @@ -152,4 +154,48 @@ public function buildTrustMarkOwnersClaimBag(
): TrustMarkOwnersClaimBag {
return new TrustMarkOwnersClaimBag(...$trustMarkOwnersClaimValues);
}


public function buildTrustMarkIssuersClaimBagFrom(mixed $trustMarkIssuersClaimData): TrustMarkIssuersClaimBag
{
$trustMarkIssuersClaimData = $this->helpers->type()->ensureArrayWithKeysAsNonEmptyStrings(
$trustMarkIssuersClaimData,
);

$trustMarkIssuersClaimValues = [];

foreach ($trustMarkIssuersClaimData as $trustMarkType => $trustMarkIssuersClaim) {
$trustMarkIssuersClaim = $this->helpers->type()->ensureArrayWithValuesAsNonEmptyStrings(
$trustMarkIssuersClaim,
);

$trustMarkIssuersClaimValues[] = $this->buildTrustMarkIssuersClaimValue(
$trustMarkType,
$trustMarkIssuersClaim,
);
}

return $this->buildTrustMarkIssuerClaimBag(...$trustMarkIssuersClaimValues);
}


public function buildTrustMarkIssuersClaimValue(
mixed $trustMarkType,
mixed $trustMarkIssuers,
): TrustMarkIssuersClaimValue {
$trustMarkType = $this->helpers->type()->ensureNonEmptyString($trustMarkType);
$trustMarkIssuers = $this->helpers->type()->ensureArrayWithValuesAsNonEmptyStrings($trustMarkIssuers);

return new TrustMarkIssuersClaimValue(
$trustMarkType,
$trustMarkIssuers,
);
}


public function buildTrustMarkIssuerClaimBag(
TrustMarkIssuersClaimValue ...$trustMarkIssuersClaimValues,
): TrustMarkIssuersClaimBag {
return new TrustMarkIssuersClaimBag(...$trustMarkIssuersClaimValues);
}
}
2 changes: 1 addition & 1 deletion src/Federation/Factories/TrustChainFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public function fromStatements(EntityStatement ...$statements): TrustChain
public function fromTokens(string ...$tokens): TrustChain
{
$statements = array_map(
fn(string $token): EntityStatement => $this->entityStatementFactory->fromToken($token),
$this->entityStatementFactory->fromToken(...),
$tokens,
);

Expand Down
88 changes: 87 additions & 1 deletion src/Federation/TrustMarkValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,9 @@ public function doForTrustMark(
);

$this->validateSubjectClaim($trustMark, $leafEntityConfiguration);
$this->validateTrustMarkIssuers($trustMark, $trustAnchorEntityConfiguration);

// If Trust Mark Issuer is the Trust Anchor itself, we don't have to resolve chain, as Trust Anchor is trusted
// If Trust Mark Issuer is the Trust Anchor itself, we don't have to resolve a chain, as Trust Anchor is trusted
// out-of-band. Otherwise, we have to resolve trust for Trust Mark Issuer.
$trustMarkIssuerEntityConfiguration =
$trustMark->getIssuer() === $trustAnchorEntityConfiguration->getIssuer() ?
Expand Down Expand Up @@ -516,6 +517,91 @@ public function validateSubjectClaim(
}


/**
* @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException
* @throws \SimpleSAML\OpenID\Exceptions\TrustMarkException
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
*/
public function validateTrustMarkIssuers(
TrustMark $trustMark,
EntityStatement $trustAnchorEntityConfiguration,
): void {
$this->logger?->debug('Validating Trust Mark Issuers.');

if (is_null($trustMarkIssuersClaimBag = $trustAnchorEntityConfiguration->getTrustMarkIssuers())) {
$this->logger?->debug(
sprintf(
'Trust Anchor %s does not define Trust Mark Issuers. Skipping validation.',
$trustAnchorEntityConfiguration->getIssuer(),
),
);
return;
}

$this->logger?->debug(
sprintf(
'Trust Anchor %s defines Trust Mark Issuers.',
$trustAnchorEntityConfiguration->getIssuer(),
),
['trustMarkIssuers' => $trustMarkIssuersClaimBag->jsonSerialize()],
);

$trustMarkIssuersClaimValue = $trustMarkIssuersClaimBag->get($trustMark->getTrustMarkType());

if (is_null($trustMarkIssuersClaimValue)) {
$this->logger?->debug(
sprintf(
'Trust Anchor %s does not define issuers of Trust Mark %s. Skipping validation.',
$trustAnchorEntityConfiguration->getIssuer(),
$trustMark->getTrustMarkType(),
),
);
return;
}

if ($trustMarkIssuersClaimValue->getTrustMarkIssuers() === []) {
$this->logger?->debug(
sprintf(
'Trust Anchor %s defines any issuers of Trust Mark %s. Skipping validation.',
$trustAnchorEntityConfiguration->getIssuer(),
$trustMark->getTrustMarkType(),
),
);
return;
}

$this->logger?->debug(
sprintf(
'Trust Anchor %s defines issuers %s of Trust Mark %s.',
$trustAnchorEntityConfiguration->getIssuer(),
implode(', ', $trustMarkIssuersClaimValue->getTrustMarkIssuers()),
$trustMark->getTrustMarkType(),
),
);

if (!in_array($trustMark->getIssuer(), $trustMarkIssuersClaimValue->getTrustMarkIssuers(), true)) {
$error = sprintf(
'Trust Mark %s is not issued by any of the Trust Mark Issuers %s defined by Trust Anchor %s.',
$trustMark->getTrustMarkType(),
implode(', ', $trustMarkIssuersClaimValue->getTrustMarkIssuers()),
$trustAnchorEntityConfiguration->getIssuer(),
);

$this->logger?->error($error);
throw new TrustMarkException($error);
}

$this->logger?->debug(
sprintf(
'Trust Mark %s is issued by one of the Trust Mark Issuers %s defined by Trust Anchor %s.',
$trustMark->getTrustMarkType(),
implode(', ', $trustMarkIssuersClaimValue->getTrustMarkIssuers()),
$trustAnchorEntityConfiguration->getIssuer(),
),
);
}


/**
* @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException
* @throws \SimpleSAML\OpenID\Exceptions\JwsException
Expand Down
2 changes: 1 addition & 1 deletion src/Jws/ParsedJws.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public function getPayload(): array
}

$payloadString = $this->jwsDecorator->jws()->getPayload();
if ($payloadString === null || $payloadString === '' || $payloadString === '0') {
if (in_array($payloadString, [null, '', '0'], true)) {
return $this->payload = [];
}

Expand Down
Loading