diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 0e1768e..7867ae9 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,5 +1,5 @@ parameters: - level: 7 + level: 9 paths: - ./src - ./test diff --git a/src/PluginSession.php b/src/PluginSession.php index 549d377..2bb27c7 100644 --- a/src/PluginSession.php +++ b/src/PluginSession.php @@ -84,7 +84,8 @@ public function __construct( // delete the instance if the special sub is in the token data // exits the request if ($sso && $remoteCallHandler && $sso->isDeleteInstanceCall()) { - $this->deleteInstance($sso->getInstanceId(), $remoteCallHandler); + $instanceId = $sso->getInstanceId() ?: throw new SSOException('Instance id is required for deleteInstance'); + $this->deleteInstance($instanceId, $remoteCallHandler); } // starts the session @@ -151,12 +152,28 @@ private function updateSSOInformation(string $jwt, string $appSecret, int $leewa */ private function validateParams(): ?string { - $pid = $_REQUEST[self::QUERY_PARAM_PID] ?? null; - $jwt = $_REQUEST[self::QUERY_PARAM_JWT] ?? null; - $sid = $_REQUEST[self::QUERY_PARAM_SID] ?? null; + $rawPid = $_REQUEST[self::QUERY_PARAM_PID] ?? null; + $rawJwt = $_REQUEST[self::QUERY_PARAM_JWT] ?? null; + $rawSid = $_REQUEST[self::QUERY_PARAM_SID] ?? null; + + // Normalize values to string|null while avoiding casting arrays/objects to string + $pid = null; + if (is_string($rawPid)) { + $pid = $rawPid; + } + + $jwt = null; + if (is_string($rawJwt)) { + $jwt = $rawJwt; + } + + $sid = null; + if (is_string($rawSid)) { + $sid = $rawSid; + } // lets hint to bad class usage, as these cases should never happen. - if ($pid && $jwt) { + if ($pid !== null && $jwt !== null) { throw new SSOAuthenticationException('Tried to initialize the session with both PID and JWT provided.'); } diff --git a/src/RemoteCall/DeleteInstanceTrait.php b/src/RemoteCall/DeleteInstanceTrait.php index 7806884..ac7eb96 100644 --- a/src/RemoteCall/DeleteInstanceTrait.php +++ b/src/RemoteCall/DeleteInstanceTrait.php @@ -11,12 +11,20 @@ trait DeleteInstanceTrait * * if a remote call was not handled by the user we die hard here */ - protected function exitRemoteCall(): void + protected function exitRemoteCall(): never { error_log("Warning: The exit procedure for a remote call was not properly handled."); exit; } + /** + * Handle the delete-instance remote call and terminate the request. + * + * @param string $instanceId + * @param RemoteCallInterface $remoteCallHandler + * + * @return never + */ private function deleteInstance(string $instanceId, RemoteCallInterface $remoteCallHandler): void { if ($remoteCallHandler instanceof DeleteInstanceCallHandlerInterface) { diff --git a/src/SSOData/ClaimAccessTrait.php b/src/SSOData/ClaimAccessTrait.php index 55efee1..fedc18b 100644 --- a/src/SSOData/ClaimAccessTrait.php +++ b/src/SSOData/ClaimAccessTrait.php @@ -55,9 +55,11 @@ abstract protected function getAllClaims(): array; */ protected function getClaimSafe(string $name) { - if ($this->hasClaim($name)) { - return $this->getClaim($name); + $value = $this->getClaim($name); + + // Return the value as-is. Type safety is handled by individual getters. + return $value; } return null; diff --git a/src/SSOData/SSODataTrait.php b/src/SSOData/SSODataTrait.php index 74dcdcd..89fb3fa 100644 --- a/src/SSOData/SSODataTrait.php +++ b/src/SSOData/SSODataTrait.php @@ -30,7 +30,8 @@ trait SSODataTrait */ public function getBranchId(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_BRANCH_ID); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_BRANCH_ID); + return is_string($value) ? $value : null; } /** @@ -40,7 +41,8 @@ public function getBranchId(): ?string */ public function getBranchSlug(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_BRANCH_SLUG); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_BRANCH_SLUG); + return is_string($value) ? $value : null; } /** @@ -50,7 +52,8 @@ public function getBranchSlug(): ?string */ public function getSessionId(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_SESSION_ID); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_SESSION_ID); + return is_string($value) ? $value : null; } /** @@ -62,7 +65,8 @@ public function getSessionId(): ?string */ public function getInstanceId(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_INSTANCE_ID); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_INSTANCE_ID); + return is_string($value) ? $value : null; } /** @@ -72,7 +76,8 @@ public function getInstanceId(): ?string */ public function getInstanceName(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_INSTANCE_NAME); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_INSTANCE_NAME); + return is_string($value) ? $value : null; } /** @@ -82,7 +87,8 @@ public function getInstanceName(): ?string */ public function getUserId(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_ID); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_ID); + return is_string($value) ? $value : null; } /** @@ -95,7 +101,8 @@ public function getUserId(): ?string */ public function getUserExternalId(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_EXTERNAL_ID); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_EXTERNAL_ID); + return is_string($value) ? $value : null; } /** @@ -105,7 +112,8 @@ public function getUserExternalId(): ?string */ public function getUserUsername(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_USERNAME); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_USERNAME); + return is_string($value) ? $value : null; } /** @@ -115,7 +123,8 @@ public function getUserUsername(): ?string */ public function getUserPrimaryEmailAddress(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_PRIMARY_EMAIL_ADDRESS); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_PRIMARY_EMAIL_ADDRESS); + return is_string($value) ? $value : null; } /** @@ -125,7 +134,8 @@ public function getUserPrimaryEmailAddress(): ?string */ public function getFullName(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_FULL_NAME); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_FULL_NAME); + return is_string($value) ? $value : null; } /** @@ -135,7 +145,8 @@ public function getFullName(): ?string */ public function getFirstName(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_FIRST_NAME); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_FIRST_NAME); + return is_string($value) ? $value : null; } /** @@ -145,20 +156,22 @@ public function getFirstName(): ?string */ public function getLastName(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LAST_NAME); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LAST_NAME); + return is_string($value) ? $value : null; } /** * Get the type of the token. * - * The type of the accessing entity can be either a “user” or a “token”. + * The type of the accessing entity can be either a "user" or a "token". * * @return null|string */ public function getType(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_ENTITY_TYPE); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_ENTITY_TYPE); + return is_string($value) ? $value : null; } /** @@ -170,7 +183,8 @@ public function getType(): ?string */ public function getThemeTextColor(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_THEME_TEXT_COLOR); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_THEME_TEXT_COLOR); + return is_string($value) ? $value : null; } /** @@ -182,7 +196,8 @@ public function getThemeTextColor(): ?string */ public function getThemeBackgroundColor(): ?string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_THEME_BACKGROUND_COLOR); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_THEME_BACKGROUND_COLOR); + return is_string($value) ? $value : null; } /** @@ -192,7 +207,8 @@ public function getThemeBackgroundColor(): ?string */ public function getLocale(): string { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LOCALE); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LOCALE); + return is_string($value) ? $value : ''; } /** @@ -202,6 +218,7 @@ public function getLocale(): string */ public function getTags(): ?array { - return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_TAGS); + $value = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_TAGS); + return is_array($value) ? $value : null; } } diff --git a/src/SSOData/SharedDataTrait.php b/src/SSOData/SharedDataTrait.php index 224e675..9bf8739 100644 --- a/src/SSOData/SharedDataTrait.php +++ b/src/SSOData/SharedDataTrait.php @@ -57,7 +57,8 @@ public function getAudience(): ?string */ public function getExpireAtTime(): ?DateTimeImmutable { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_EXPIRE_AT); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_EXPIRE_AT); + return $value instanceof DateTimeImmutable ? $value : null; } /** @@ -67,7 +68,8 @@ public function getExpireAtTime(): ?DateTimeImmutable */ public function getNotBeforeTime(): ?DateTimeImmutable { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_NOT_BEFORE); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_NOT_BEFORE); + return $value instanceof DateTimeImmutable ? $value : null; } /** @@ -77,7 +79,8 @@ public function getNotBeforeTime(): ?DateTimeImmutable */ public function getIssuedAtTime(): ?DateTimeImmutable { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_ISSUED_AT); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_ISSUED_AT); + return $value instanceof DateTimeImmutable ? $value : null; } /** @@ -87,7 +90,8 @@ public function getIssuedAtTime(): ?DateTimeImmutable */ public function getIssuer(): ?string { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_ISSUER); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_ISSUER); + return is_string($value) ? $value : null; } /** @@ -97,7 +101,8 @@ public function getIssuer(): ?string */ public function getId(): ?string { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_JWT_ID); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_JWT_ID); + return is_string($value) ? $value : null; } /** @@ -107,21 +112,23 @@ public function getId(): ?string */ public function getSubject(): ?string { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_SUBJECT); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_SUBJECT); + return is_string($value) ? $value : null; } /** * Get the role of the accessing user. * - * If this is set to “editor”, the requesting user may manage the contents + * If this is set to "editor", the requesting user may manage the contents * of the plugin instance, i.e. she has administration rights. - * The type of the accessing entity can be either a “user” or a “editor”. + * The type of the accessing entity can be either a "user" or an "editor". * * @return null|string */ public function getRole(): ?string { - return $this->getClaimSafe(SharedClaimsInterface::CLAIM_USER_ROLE); + $value = $this->getClaimSafe(SharedClaimsInterface::CLAIM_USER_ROLE); + return is_string($value) ? $value : null; } /** diff --git a/src/SSOTokenGenerator.php b/src/SSOTokenGenerator.php index 2bb1aef..aef2f8b 100644 --- a/src/SSOTokenGenerator.php +++ b/src/SSOTokenGenerator.php @@ -52,22 +52,52 @@ public static function createSignedTokenFromData(string $privateKey, array $toke private static function buildToken(Configuration $config, array $tokenData): Token { $builder = $config->builder(); + // Validate and coerce required registered claims to the expected types + $audience = $tokenData[SSOData\SharedClaimsInterface::CLAIM_AUDIENCE] ?? ''; + if (!is_string($audience) || $audience === '') { + throw new \InvalidArgumentException('aud claim must be a non-empty string for token generation'); + } + + $issuedAt = $tokenData[SSOData\SharedClaimsInterface::CLAIM_ISSUED_AT] ?? null; + if (!($issuedAt instanceof \DateTimeImmutable)) { + throw new \InvalidArgumentException('iat claim must be a DateTimeImmutable for token generation'); + } + + $notBefore = $tokenData[SSOData\SharedClaimsInterface::CLAIM_NOT_BEFORE] ?? null; + if (!($notBefore instanceof \DateTimeImmutable)) { + throw new \InvalidArgumentException('nbf claim must be a DateTimeImmutable for token generation'); + } + + $expiresAt = $tokenData[SSOData\SharedClaimsInterface::CLAIM_EXPIRE_AT] ?? null; + if (!($expiresAt instanceof \DateTimeImmutable)) { + throw new \InvalidArgumentException('exp claim must be a DateTimeImmutable for token generation'); + } + $token = $builder - ->permittedFor($tokenData[SSOData\SharedClaimsInterface::CLAIM_AUDIENCE]) - ->issuedAt($tokenData[SSOData\SharedClaimsInterface::CLAIM_ISSUED_AT]) - ->canOnlyBeUsedAfter($tokenData[SSOData\SharedClaimsInterface::CLAIM_NOT_BEFORE]) - ->expiresAt($tokenData[SSOData\SharedClaimsInterface::CLAIM_EXPIRE_AT]); + ->permittedFor($audience) + ->issuedAt($issuedAt) + ->canOnlyBeUsedAfter($notBefore) + ->expiresAt($expiresAt); if (isset($tokenData[SSOData\SharedClaimsInterface::CLAIM_ISSUER])) { - $token = $token->issuedBy($tokenData[SSOData\SharedClaimsInterface::CLAIM_ISSUER]); + $issuer = $tokenData[SSOData\SharedClaimsInterface::CLAIM_ISSUER]; + if (is_string($issuer) && $issuer !== '') { + $token = $token->issuedBy($issuer); + } } if (isset($tokenData[SSOData\SSODataClaimsInterface::CLAIM_USER_ID])) { - $token = $token->relatedTo($tokenData[SSOData\SSODataClaimsInterface::CLAIM_USER_ID]); + $subject = $tokenData[SSOData\SSODataClaimsInterface::CLAIM_USER_ID]; + if (is_string($subject) && $subject !== '') { + $token = $token->relatedTo($subject); + } } if (isset($tokenData[SSOData\SharedClaimsInterface::CLAIM_JWT_ID])) { - $token = $token->identifiedBy($tokenData[SSOData\SharedClaimsInterface::CLAIM_JWT_ID]); + $jwtId = $tokenData[SSOData\SharedClaimsInterface::CLAIM_JWT_ID]; + if (is_string($jwtId) && $jwtId !== '') { + $token = $token->identifiedBy($jwtId); + } } // Remove all set keys as they throw an exception when used with withClaim diff --git a/src/SessionHandling/SessionHandlerTrait.php b/src/SessionHandling/SessionHandlerTrait.php index 90dfe60..cdadfa6 100644 --- a/src/SessionHandling/SessionHandlerTrait.php +++ b/src/SessionHandling/SessionHandlerTrait.php @@ -64,7 +64,10 @@ protected function closeSession(): void */ public function hasSessionVar(string $key, ?string $parentKey = null): bool { - return isset($_SESSION[$this->pluginInstanceId][$parentKey ?? self::$KEY_DATA][$key]); + $parent = $parentKey ?? self::$KEY_DATA; + $bucket = $this->getSessionBucket($parent); + + return $bucket !== null && isset($bucket[$key]); } /** @@ -77,7 +80,10 @@ public function hasSessionVar(string $key, ?string $parentKey = null): bool */ public function getSessionVar(string $key, ?string $parentKey = null) { - return $_SESSION[$this->pluginInstanceId][$parentKey ?? self::$KEY_DATA][$key] ?? null; + $parent = $parentKey ?? self::$KEY_DATA; + $bucket = $this->getSessionBucket($parent); + + return $bucket[$key] ?? null; } /** @@ -89,40 +95,96 @@ public function getSessionVar(string $key, ?string $parentKey = null) */ public function getSessionData(?string $parentKey = null): array { - return $_SESSION[$this->pluginInstanceId][$parentKey ?? self::$KEY_DATA] ?? []; + $parent = $parentKey ?? self::$KEY_DATA; + + return $this->getSessionBucket($parent) ?? []; } /** * Set all session variables. * - * @param mixed $data - * @param string|null $parentKey - * - */ - /** * @param array $data + * @param string|null $parentKey */ public function setSessionData(array $data, ?string $parentKey = null): void { - $_SESSION[$this->pluginInstanceId][$parentKey ?? self::$KEY_DATA] = $data; + $instance = $this->getValidInstance(); + if ($instance === null) { + return; + } + + $parent = $parentKey ?? self::$KEY_DATA; + $sessionInstance = $this->getSessionInstance($instance); + $sessionInstance[$parent] = $data; + $_SESSION[$instance] = $sessionInstance; } /** * Set a session variable. * - * @param mixed $key + * @param string $key * @param mixed $val * @param string|null $parentKey */ + public function setSessionVar(string $key, mixed $val, ?string $parentKey = null): void + { + $instance = $this->getValidInstance(); + if ($instance === null) { + return; + } + + $parent = $parentKey ?? self::$KEY_DATA; + $sessionInstance = $this->getSessionInstance($instance); + /** @var array $bucket */ + $bucket = $this->getSessionBucket($parent, default: []) ?? []; + $bucket[$key] = $val; + $sessionInstance[$parent] = $bucket; + $_SESSION[$instance] = $sessionInstance; + } + /** - * @param string $key - * @param mixed $val + * Return the validated plugin instance ID, or null if unset/empty. */ - public function setSessionVar(string $key, mixed $val, ?string $parentKey = null): void + private function getValidInstance(): ?string { - $_SESSION[$this->pluginInstanceId][$parentKey ?? self::$KEY_DATA][$key] = $val; + $instance = $this->pluginInstanceId; + return ($instance !== null && $instance !== '') ? $instance : null; } + /** + * Return $_SESSION[$instance] as a typed array, or $default if instance is + * null/empty or the session entry is missing/invalid. + * + * @param array $default + * @return array + */ + private function getSessionInstance(?string $instance, array $default = []): array + { + if ($instance === null || $instance === '') { + return $default; + } + + /** @var array $result */ + $result = isset($_SESSION[$instance]) && is_array($_SESSION[$instance]) + ? $_SESSION[$instance] + : $default; + return $result; + } + + /** + * Return the session bucket array for the given parent key, + * or $default if the session structure is missing or invalid. + * + * @param array|null $default + * @return array|null + */ + private function getSessionBucket(string $parentKey, ?array $default = null): ?array + { + $sessionInstance = $this->getSessionInstance($this->getValidInstance(), $default ?? []); + $bucket = $sessionInstance[$parentKey] ?? null; + /** @var array|null */ + return is_array($bucket) ? $bucket : $default; + } /** * Destroy the session with the given id diff --git a/test/PluginSessionTest.php b/test/PluginSessionTest.php index 85853b1..dc851e3 100644 --- a/test/PluginSessionTest.php +++ b/test/PluginSessionTest.php @@ -57,7 +57,15 @@ public function setUp(): void $this->tokenData = SSOTestData::getTokenData(); $this->token = SSOTokenGenerator::createSignedTokenFromData($this->privateKey, $this->tokenData); - $this->pluginInstanceId = $this->tokenData[SSODataClaimsInterface::CLAIM_INSTANCE_ID]; + $instanceId = $this->tokenData[SSODataClaimsInterface::CLAIM_INSTANCE_ID] ?? ''; + + if (is_string($instanceId)) { + $this->pluginInstanceId = $instanceId; + } elseif (is_numeric($instanceId)) { + $this->pluginInstanceId = (string) $instanceId; + } else { + $this->pluginInstanceId = ''; + } } @@ -115,7 +123,7 @@ public function testConstructorWorksAsExpected(): void ->with($this->pluginId); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, $this->pluginId, $this->publicKey); $this->setupEnvironment($this->pluginInstanceId, null, false); @@ -143,7 +151,7 @@ public function testConstructorRejectsSpoofedPID(): void $this->expectException(SSOException::class); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, $this->pluginId, $this->publicKey); } @@ -168,7 +176,7 @@ public function testConstructorRefuseEmptyPluginId(): void $this->expectExceptionMessage('Empty plugin ID.'); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, '', $this->publicKey); } @@ -193,7 +201,7 @@ public function testConstructorRefuseEmptySecret(): void $this->expectExceptionMessage('Parameter appSecret for SSOToken is empty.'); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, $this->pluginId, ''); } @@ -218,7 +226,7 @@ public function testConstructorRefuseEmptyEnv(): void $this->expectExceptionMessage('Missing PID or JWT query parameter in Request.'); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, $this->pluginId, $this->publicKey); } @@ -243,7 +251,7 @@ public function testConstructorRefuseHavingBothJwtAndPid(): void $this->expectExceptionMessage('Tried to initialize the session with both PID and JWT provided.'); $reflectedClass = new ReflectionClass($this->classname); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, $this->pluginId, $this->publicKey); } @@ -392,7 +400,8 @@ public function testDeleteSuccessfulCallInterface(): void ->method('deleteInstance'); $handler->expects($this->once()) - ->method('exitSuccess'); + ->method('exitSuccess') + ->willThrowException(new \Exception('exitSuccess called')); $handler->expects($this->never()) ->method('exitFailure'); @@ -404,6 +413,10 @@ public function testDeleteSuccessfulCallInterface(): void ->onlyMethods(['openSession', 'closeSession', 'exitRemoteCall']) ->getMock(); + // Expect the exitSuccess to be called (via exception) + $this->expectException(\Exception::class); + $this->expectExceptionMessage('exitSuccess called'); + $session = new $Session($this->pluginId, $this->publicKey, null, 0, $handler); $this->assertInstanceOf($this->classname, $session); @@ -440,7 +453,8 @@ public function testDeleteFailedCallInterface(): void ->method('exitSuccess'); $handler->expects($this->once()) - ->method('exitFailure'); + ->method('exitFailure') + ->willThrowException(new \Exception('exitFailure called')); // session mock /** @var MockObject&PluginSession $Session */ @@ -449,6 +463,10 @@ public function testDeleteFailedCallInterface(): void ->onlyMethods(['openSession', 'closeSession', 'exitRemoteCall']) ->getMock(); + // Expect the exitFailure to be called (via exception) + $this->expectException(\Exception::class); + $this->expectExceptionMessage('exitFailure called'); + $session = new $Session($this->pluginId, $this->publicKey, null, 0, $handler); $this->assertInstanceOf($this->classname, $session); diff --git a/test/SSOTokenTest.php b/test/SSOTokenTest.php index b7368aa..93860f3 100644 --- a/test/SSOTokenTest.php +++ b/test/SSOTokenTest.php @@ -66,7 +66,7 @@ public function testConstructorRefuseEmptySecret(): void $this->expectExceptionMessage('Parameter appSecret for SSOToken is empty.'); $reflectedClass = new ReflectionClass(SSOToken::class); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, ' ', 'fake token'); } @@ -89,7 +89,7 @@ public function testConstructorRefuseEmptyToken(): void $this->expectExceptionMessage('Parameter tokenData for SSOToken is empty.'); $reflectedClass = new ReflectionClass(SSOToken::class); - $constructor = $reflectedClass->getConstructor(); + $constructor = $reflectedClass->getConstructor() ?: throw new \Exception('Constructor not found'); $constructor->invoke($mock, 'fake secret', ' '); } @@ -208,7 +208,8 @@ public function testAccessorsGiveCorrectValues(): void $data = $data->getTimestamp(); } - $data = is_array($data) ? print_r($data, true) : $data; + $data = is_array($data) ? print_r($data, true) + : (is_scalar($data) || is_null($data) ? (string) ($data ?? '') : '[complex_type]'); $this->assertEquals( $tokenData[$key],