Skip to content
Open
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 phpstan.neon.dist
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 7
level: 9
paths:
- ./src
- ./test
27 changes: 22 additions & 5 deletions src/PluginSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,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
Expand Down Expand Up @@ -153,12 +154,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.');
}

Expand Down
2 changes: 1 addition & 1 deletion src/RemoteCall/DeleteInstanceTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ protected function exitRemoteCall(): void
exit;
}

private function deleteInstance(string $instanceId, RemoteCallInterface $remoteCallHandler): void
private function deleteInstance(string $instanceId, RemoteCallInterface $remoteCallHandler): never
{
if ($remoteCallHandler instanceof DeleteInstanceCallHandlerInterface) {
$result = $remoteCallHandler->deleteInstance($instanceId);
Expand Down
6 changes: 4 additions & 2 deletions src/SSOData/ClaimAccessTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,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;
Expand Down
53 changes: 35 additions & 18 deletions src/SSOData/SSODataTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,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;
}

/**
Expand All @@ -42,7 +43,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;
}

/**
Expand All @@ -52,7 +54,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;
}

/**
Expand All @@ -64,7 +67,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;
}

/**
Expand All @@ -74,7 +78,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;
}

/**
Expand All @@ -84,7 +89,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;
}

/**
Expand All @@ -97,7 +103,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;
}

/**
Expand All @@ -107,7 +114,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;
}

/**
Expand All @@ -117,7 +125,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;
}

/**
Expand All @@ -127,7 +136,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;
}

/**
Expand All @@ -137,7 +147,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;
}

/**
Expand All @@ -147,20 +158,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;
}

/**
Expand All @@ -172,7 +185,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;
}

/**
Expand All @@ -184,7 +198,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;
}

/**
Expand All @@ -194,7 +209,8 @@ public function getThemeBackgroundColor(): ?string
*/
public function getLocale(): string
{
return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LOCALE);
$val = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_LOCALE);
return is_string($val) ? $val : '';
}

/**
Expand All @@ -204,6 +220,7 @@ public function getLocale(): string
*/
public function getTags(): ?array
{
return $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_TAGS);
$val = $this->getClaimSafe(SSODataClaimsInterface::CLAIM_USER_TAGS);
return is_array($val) ? $val : null;
}
}
25 changes: 16 additions & 9 deletions src/SSOData/SharedDataTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,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;
}

/**
Expand All @@ -69,7 +70,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;
}

/**
Expand All @@ -79,7 +81,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;
}

/**
Expand All @@ -89,7 +92,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;
}

/**
Expand All @@ -99,7 +103,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;
}

/**
Expand All @@ -109,21 +114,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 a "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;
}

/**
Expand Down
44 changes: 37 additions & 7 deletions src/SSOTokenGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,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
Expand Down
Loading