diff --git a/src/Codebooks/MetadataPolicyOperatorsEnum.php b/src/Codebooks/MetadataPolicyOperatorsEnum.php index 8ea816b..9d16fe6 100644 --- a/src/Codebooks/MetadataPolicyOperatorsEnum.php +++ b/src/Codebooks/MetadataPolicyOperatorsEnum.php @@ -299,6 +299,7 @@ public static function validateGeneralParameterOperationRules(array $parameterOp } /** + * @param array $parameterOperations * @throws \SimpleSAML\OpenID\Exceptions\MetadataPolicyException */ public static function validateSpecificParameterOperationRules(array $parameterOperations): void @@ -316,13 +317,13 @@ public static function validateSpecificParameterOperationRules(array $parameterO // No special resolving rules for operator 'value', continue with 'add'. if ($metadataPolicyOperatorEnum === MetadataPolicyOperatorsEnum::Add) { - /** @var array $operatorValue We ensured this is array. */ + /** @var array $operatorValue We ensured this is array. */ // If add is combined with subset_of, the values of add MUST be a subset of the values of // subset_of. if ( in_array(MetadataPolicyOperatorsEnum::SubsetOf->value, $parameterOperatorKeys, true) ) { - /** @var array $superset We ensured this is array. */ + /** @var array $superset We ensured this is array. */ $superset = $parameterOperations[ MetadataPolicyOperatorsEnum::SubsetOf->value ]; @@ -346,7 +347,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO true, ) ) { - /** @var array $subset We ensured this is array. */ + /** @var array $subset We ensured this is array. */ $subset = $parameterOperations[ MetadataPolicyOperatorsEnum::SupersetOf->value ]; @@ -366,7 +367,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO if ( in_array(MetadataPolicyOperatorsEnum::OneOf->value, $parameterOperatorKeys, true) ) { - /** @var array $superset We ensured this is array. */ + /** @var array $superset We ensured this is array. */ $superset = $parameterOperations[ MetadataPolicyOperatorsEnum::OneOf->value ]; @@ -386,7 +387,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO if ( in_array(MetadataPolicyOperatorsEnum::SubsetOf->value, $parameterOperatorKeys, true) ) { - /** @var array $superset We ensured this is array. */ + /** @var array $superset We ensured this is array. */ $superset = $parameterOperations[ MetadataPolicyOperatorsEnum::SubsetOf->value ]; @@ -410,7 +411,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO true, ) ) { - /** @var array $subset We ensured this is array. */ + /** @var array $subset We ensured this is array. */ $subset = $parameterOperations[ MetadataPolicyOperatorsEnum::SupersetOf->value ]; @@ -440,7 +441,7 @@ public static function validateSpecificParameterOperationRules(array $parameterO true, ) ) { - /** @var array $subset We ensured this is array. */ + /** @var array $subset We ensured this is array. */ $subset = $parameterOperations[ MetadataPolicyOperatorsEnum::SupersetOf->value ]; diff --git a/src/Federation/EntityStatement.php b/src/Federation/EntityStatement.php index 3b8d48f..c7ecabc 100644 --- a/src/Federation/EntityStatement.php +++ b/src/Federation/EntityStatement.php @@ -81,7 +81,7 @@ public function getExpirationTime(): int /** * @throws \SimpleSAML\OpenID\Exceptions\JwsException - * @return array[] + * @return array{keys:array>} * @psalm-suppress MixedReturnTypeCoercion */ public function getJwks(): array @@ -98,6 +98,12 @@ public function getJwks(): array throw new JwsException('Invalid JWKS encountered: ' . var_export($jwks, true)); } + $jwks[ClaimsEnum::Keys->value] = array_map( + $this->helpers->arr()->ensureStringKeys(...), + $jwks[ClaimsEnum::Keys->value], + ); + + /** @var array{keys:array>} $jwks */ return $jwks; } @@ -153,6 +159,7 @@ public function getAuthorityHints(): ?array } /** + * @return ?array * @throws \SimpleSAML\OpenID\Exceptions\JwsException * @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException */ @@ -172,12 +179,13 @@ public function getMetadata(): ?array throw new EntityStatementException('Invalid Metadata claim.'); } - return $metadata; + return $this->helpers->arr()->ensureStringKeys($metadata); } /** * @throws \SimpleSAML\OpenID\Exceptions\JwsException * @throws \SimpleSAML\OpenID\Exceptions\EntityStatementException + * @phpstan-ignore missingType.iterableValue (We will ensure proper format in policy resolver.) */ public function getMetadataPolicy(): ?array { @@ -273,6 +281,7 @@ public function isConfiguration(): bool /** * @throws \SimpleSAML\OpenID\Exceptions\JwsException + * @phpstan-ignore missingType.iterableValue (Format is validated later.) */ public function verifyWithKeySet(?array $jwks = null, int $signatureIndex = 0): void { diff --git a/src/Federation/EntityStatement/TrustMarkClaim.php b/src/Federation/EntityStatement/TrustMarkClaim.php index 4172390..c41eddf 100644 --- a/src/Federation/EntityStatement/TrustMarkClaim.php +++ b/src/Federation/EntityStatement/TrustMarkClaim.php @@ -59,6 +59,9 @@ public function getTrustMark(): TrustMark return $this->trustMark; } + /** + * @return array + */ public function getOtherClaims(): array { return $this->otherClaims; diff --git a/src/Federation/MetadataPolicyApplicator.php b/src/Federation/MetadataPolicyApplicator.php index 74877f7..19c4f5a 100644 --- a/src/Federation/MetadataPolicyApplicator.php +++ b/src/Federation/MetadataPolicyApplicator.php @@ -17,7 +17,9 @@ public function __construct( } /** - * @param array $resolvedMetadataPolicy Resolved (validated) metadata policy. + * @param array> $resolvedMetadataPolicy Resolved (validated) metadata policy. + * @param array $metadata + * @return array Metadata with applied policies. * @throws \SimpleSAML\OpenID\Exceptions\MetadataPolicyException * @throws \SimpleSAML\OpenID\Exceptions\OpenIdException */ @@ -25,10 +27,6 @@ public function for( array $resolvedMetadataPolicy, array $metadata, ): array { - /** - * @var string $policyParameterName - * @var array $policyOperations - */ foreach ($resolvedMetadataPolicy as $policyParameterName => $policyOperations) { foreach (MetadataPolicyOperatorsEnum::cases() as $metadataPolicyOperatorEnum) { if (!array_key_exists($metadataPolicyOperatorEnum->value, $policyOperations)) { @@ -36,7 +34,7 @@ public function for( } /** @psalm-suppress MixedAssignment */ $operatorValue = $policyOperations[$metadataPolicyOperatorEnum->value]; - /** @psalm-suppress MixedAssignment */ + /** @psalm-suppress MixedAssignment, MixedArgumentTypeCoercion */ $metadataParameterValueBeforePolicy = $this->resolveParameterValueBeforePolicy( $metadata, $policyParameterName, @@ -99,7 +97,7 @@ public function for( $policyParameterName, ); - /** @var array $operatorValue */ + /** @var array $operatorValue Set bc of phpstan */ if (!in_array($metadataParameterValueBeforePolicy, $operatorValue, true)) { throw new MetadataPolicyException( sprintf( @@ -152,7 +150,7 @@ public function for( $policyParameterName, ); - /** @var array $operatorValue */ + /** @var array $operatorValue Set bc of phpstan */ if ( !$metadataPolicyOperatorEnum->isValueSupersetOf( $metadataParameterValueBeforePolicy, @@ -190,9 +188,13 @@ public function for( } } + /** @var array $metadata */ return $metadata; } + /** + * @param array $metadata + */ protected function resolveParameterValueBeforePolicy(array $metadata, string $parameter): mixed { /** @psalm-suppress MixedAssignment */ diff --git a/src/Federation/MetadataPolicyResolver.php b/src/Federation/MetadataPolicyResolver.php index c771765..6a6f72c 100644 --- a/src/Federation/MetadataPolicyResolver.php +++ b/src/Federation/MetadataPolicyResolver.php @@ -20,6 +20,7 @@ public function __construct( * @return array>> * @throws \SimpleSAML\OpenID\Exceptions\MetadataPolicyException * @psalm-suppress MixedAssignment + * @phpstan-ignore missingType.iterableValue (We validate it here) */ public function ensureFormat(array $metadataPolicies): array { @@ -55,7 +56,7 @@ public function ensureFormat(array $metadataPolicies): array /** * @param array>>> $metadataPolicies * @param string[] $criticalMetadataPolicyOperators - * + * @return array> * @throws \SimpleSAML\OpenID\Exceptions\MetadataPolicyException * @throws \SimpleSAML\OpenID\Exceptions\OpenIdException */ @@ -72,6 +73,7 @@ public function for( /** @psalm-suppress MixedAssignment We'll check if $nextPolicy is array type. */ if ( (!array_key_exists($entityTypeEnum->value, $metadataPolicy)) || + /** @phpstan-ignore booleanNot.alwaysFalse (Let's check for validity here.) */ (!is_array($nextPolicy = $metadataPolicy[$entityTypeEnum->value])) ) { continue; diff --git a/src/Federation/RequestObject.php b/src/Federation/RequestObject.php index 5373571..078b5e1 100644 --- a/src/Federation/RequestObject.php +++ b/src/Federation/RequestObject.php @@ -66,20 +66,22 @@ public function getExpirationTime(): int } /** + * @return ?string[] * @throws \SimpleSAML\OpenID\Exceptions\JwsException * @throws \SimpleSAML\OpenID\Exceptions\RequestObjectException */ public function getTrustChain(): ?array { + $claimKey = ClaimsEnum::TrustChain->value; /** @psalm-suppress MixedAssignment */ - $trustChain = $this->getPayloadClaim(ClaimsEnum::TrustChain->value) ?? null; + $trustChain = $this->getPayloadClaim($claimKey) ?? null; if (is_null($trustChain)) { return null; } if (is_array($trustChain)) { - return $trustChain; + return $this->ensureNonEmptyStrings($trustChain, $claimKey); } throw new RequestObjectException( diff --git a/src/Federation/TrustChain.php b/src/Federation/TrustChain.php index 49191cd..7f914b1 100644 --- a/src/Federation/TrustChain.php +++ b/src/Federation/TrustChain.php @@ -43,14 +43,14 @@ class TrustChain implements JsonSerializable /** * Resolved metadata policy per entity type. * - * @var array[] + * @var array>> */ protected array $resolvedMetadataPolicy = []; /** * Resolved metadata (after applying resolved policy) per entity type. * - * @var array + * @var array> */ protected array $resolvedMetadata = []; @@ -122,6 +122,7 @@ public function getResolvedTrustAnchor(): EntityStatement } /** + * @return ?array * @throws \SimpleSAML\OpenID\Exceptions\TrustChainException * @throws \SimpleSAML\OpenID\Exceptions\JwsException * @throws \SimpleSAML\OpenID\Exceptions\OpenIdException @@ -433,17 +434,22 @@ protected function resolveMetadataFor(EntityTypesEnum $entityTypeEnum): void // to it. /** @psalm-suppress RiskyTruthyFalsyComparison */ if (empty($this->resolvedMetadataPolicy[$entityTypeEnum->value])) { + /** @var array $leafMetadataEntityType */ $this->resolvedMetadata[$entityTypeEnum->value] = $leafMetadataEntityType; return; } // Policy application to leaf metadata. + /** @var array $leafMetadataEntityType */ $this->resolvedMetadata[$entityTypeEnum->value] = $this->metadataPolicyApplicator->for( $this->resolvedMetadataPolicy[$entityTypeEnum->value], $leafMetadataEntityType, ); } + /** + * @return \SimpleSAML\OpenID\Federation\EntityStatement[] + */ public function getEntities(): array { return $this->entities; diff --git a/src/Federation/TrustChainResolver.php b/src/Federation/TrustChainResolver.php index 3fa21b8..828beff 100644 --- a/src/Federation/TrustChainResolver.php +++ b/src/Federation/TrustChainResolver.php @@ -288,6 +288,7 @@ public function for(string $entityId, array $validTrustAnchorIds): TrustChainBag /** * @throws \SimpleSAML\OpenID\Exceptions\TrustChainException + * @phpstan-ignore missingType.iterableValue (We validate it here) */ protected function validateStart(string $entityId, array $validTrustAnchorIds): void { diff --git a/src/Helpers/Arr.php b/src/Helpers/Arr.php index 9031050..95a5700 100644 --- a/src/Helpers/Arr.php +++ b/src/Helpers/Arr.php @@ -8,6 +8,9 @@ class Arr { + /** + * @phpstan-ignore missingType.iterableValue (We can handle mixed type) + */ public function ensureArrayDepth(array &$array, int|string ...$keys): void { if (count($keys) > 99) { @@ -30,6 +33,7 @@ public function ensureArrayDepth(array &$array, int|string ...$keys): void /** * @return array + * @phpstan-ignore missingType.iterableValue (We can handle mixed type) */ public function ensureStringKeys(array $array): array { diff --git a/src/Helpers/Url.php b/src/Helpers/Url.php index 4eec3f1..370341f 100644 --- a/src/Helpers/Url.php +++ b/src/Helpers/Url.php @@ -14,6 +14,7 @@ public function isValid(string $url): bool /** * Add (new) params to URL while preserving existing ones (if any). + * @param array $params */ public function withParams(string $url, array $params): string { diff --git a/src/Jwks/Factories/JwksFactory.php b/src/Jwks/Factories/JwksFactory.php index d075649..8cd2b95 100644 --- a/src/Jwks/Factories/JwksFactory.php +++ b/src/Jwks/Factories/JwksFactory.php @@ -9,6 +9,9 @@ class JwksFactory { + /** + * @phpstan-ignore missingType.iterableValue (JWKS array is validated later) + */ public function fromKeyData(array $jwks): JwksDecorator { return new JwksDecorator(JWKSet::createFromKeyData($jwks)); diff --git a/src/Jwks/JwksDecorator.php b/src/Jwks/JwksDecorator.php index 4297440..ea00d65 100644 --- a/src/Jwks/JwksDecorator.php +++ b/src/Jwks/JwksDecorator.php @@ -20,6 +20,10 @@ public function jwks(): JWKSet return $this->jwks; } + /** + * @return array{keys:array>} + * @psalm-suppress MixedReturnTypeCoercion, MixedReturnTypeCoercion + */ public function jsonSerialize(): array { return [ diff --git a/src/Jwks/JwksFetcher.php b/src/Jwks/JwksFetcher.php index 7696631..924b9ea 100644 --- a/src/Jwks/JwksFetcher.php +++ b/src/Jwks/JwksFetcher.php @@ -31,7 +31,7 @@ public function __construct( } /** - * @return array{keys:array } + * @return array{keys:array>} * @throws \SimpleSAML\OpenID\Exceptions\JwksException */ protected function decodeJwksJson(string $jwksJson): array @@ -63,8 +63,12 @@ protected function decodeJwksJson(string $jwksJson): array throw new JwksException($message); } - $jwks[ClaimsEnum::Keys->value] = $this->helpers->arr()->ensureStringKeys($jwks[ClaimsEnum::Keys->value]); - /** @var array{keys:array} $jwks */ + $jwks[ClaimsEnum::Keys->value] = array_map( + $this->helpers->arr()->ensureStringKeys(...), + $jwks[ClaimsEnum::Keys->value], + ); + + /** @var array{keys:array>} $jwks */ return $jwks; }