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
4 changes: 3 additions & 1 deletion .github/workflows/php-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ jobs:
- name: "Install PHP"
uses: "shivammathur/setup-php@v2"
with:
php-version: "latest"
# Use PHP 8.3 until psalm resolves issues with 8.4
# https://github.com/vimeo/psalm/issues/11107
php-version: "8.3"
coverage: "none"

- name: "Install dependencies (Composer)"
Expand Down
2 changes: 1 addition & 1 deletion src/Config/ConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public function getProduction(): bool;
public function getSameSiteStrict(): ?bool;
/** @return array<string, int|string>|null */
public function getError(): ?array;
/** @return array<string, string>|null */
/** @return array<string>|null */
public function getScope(): ?array;
/** @return array<string>|null */
public function getProviderHint(): ?array;
Expand Down
6 changes: 3 additions & 3 deletions src/Exception/CallbackException.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

class CallbackException extends Exception
{
/** @var array<string, string> */
/** @var array<string, mixed> */
private array $errorDetails;

/**
* @param array<string, string> $errorDetails
* @param array<string, mixed> $errorDetails
* @param string $message
* @param int $code
* @param Throwable|null $previous
Expand All @@ -27,7 +27,7 @@ public function __construct(
}

/**
* @return array<string, string>
* @return array<string, mixed>
*/
public function getErrorDetails(): array
{
Expand Down
3 changes: 2 additions & 1 deletion src/Handler/Callback.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ public function handleCallback(): string
* Uses the target URI from error details or a fallback error route. Updates the query
* string with error information. Throws an exception if no error URI is available.
*
* @param array<string, string> $error Error details including 'target_uri', 'error', and 'error_description'.
* @param array<string, mixed> $error Error details including 'target_uri', 'error', and 'error_description'.
* @param string $errorMessage A message describing the error.
* @param Throwable|null $previous Previous exception for chaining (optional).
*
Expand All @@ -272,6 +272,7 @@ public function handleCallback(): string
*/
private function sendErrorPage(array $error, string $errorMessage, ?Throwable $previous = null): string
{
/** @var string $error_uri */
$error_uri = $error['target_uri'] ?? $this->config->getRoutes()['error'] ?? null;
if ($error_uri) {
list($pathString, $queryString) = array_pad(explode('?', (string) $error_uri, 2), 2, '');
Expand Down
12 changes: 10 additions & 2 deletions src/Handler/Login.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,16 @@
}

$redirectURI = $this->config->getRedirectURI();
/** @var string $host */
$host = $this->helloRequest->fetchHeader('Host');

if (empty($redirectURI)) {
if (isset($this->redirectURIs[$host])) {
$redirectURI = $this->redirectURIs[$host];
} elseif (!empty($params['redirect_uri'])) {
$this->redirectURIs[$host] = $redirectURI = $params['redirect_uri'];
/** @var string $redirectURI */
$redirectURI = $params['redirect_uri'];
$this->redirectURIs[$host] = $redirectURI;

Check warning on line 94 in src/Handler/Login.php

View check run for this annotation

Codecov / codecov/patch

src/Handler/Login.php#L93-L94

Added lines #L93 - L94 were not covered by tests
error_log("Hello: RedirectURI for $host => $redirectURI");
} else {
error_log('Hello: Discovering API RedirectURI route ...');
Expand All @@ -97,9 +100,11 @@
}
}

/** @var string $providerHintString */
$providerHintString = $params['provider_hint'];
$providerHint = $providerHintString ? array_map('trim', explode(' ', $providerHintString)) : null;

/** @var string $scopeString */
$scopeString = $params['scope'];
$scope = $scopeString ? array_map('trim', explode(' ', $scopeString)) : null;

Expand All @@ -120,11 +125,14 @@

$authResponse = $this->getAuthHelper()->createAuthRequest($request);

/** @var string $targetUri */
$targetUri = $params['target_uri'];

$this->getOIDCManager()->saveOidc(OIDC::fromArray([
'nonce' => $authResponse['nonce'],
'code_verifier' => $authResponse['code_verifier'],
'redirect_uri' => $redirectURI,
'target_uri' => $params['target_uri'],
'target_uri' => $targetUri,
]));

return $authResponse['url'];
Expand Down
1 change: 1 addition & 0 deletions src/Handler/Logout.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ private function getAuthLib(): AuthLib
*/
public function generateLogoutUrl(): string
{
/** @var string|null $targetUri */
$targetUri = $this->helloRequest->fetch('target_uri');
$this->getAuthLib()->clearAuthCookie();
if ($this->config->getLogoutSync()) {
Expand Down
14 changes: 10 additions & 4 deletions src/HelloClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,16 @@ private function handleCallback()
return $this->helloResponse->redirect($this->getCallbackHandler()->handleCallback());
} catch (CallbackException $e) {
$errorDetails = $e->getErrorDetails();
/** @var string $error */
$error = $errorDetails['error'];
/** @var string $errorDescription */
$errorDescription = $errorDetails['error_description'];
/** @var string $targetUri */
$targetUri = $errorDetails['target_uri'];
return $this->helloResponse->render($this->pageRenderer->renderErrorPage(
$errorDetails['error'],
$errorDetails['error_description'],
$errorDetails['target_uri']
$error,
$errorDescription,
$targetUri
));
} catch (SameSiteCallbackException $e) {
return $this->helloResponse->render($this->pageRenderer->renderSameSitePage());
Expand All @@ -153,7 +159,7 @@ private function handleCallback()

/**
* @return mixed|string|void|null
* @throws CryptoFailedException | InvalidSecretException
* @throws CryptoFailedException | InvalidSecretException | Exception
*/
public function route()
{
Expand Down
16 changes: 8 additions & 8 deletions src/HelloRequest/HelloRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ class HelloRequest implements HelloRequestInterface
* Fetch a parameter by key from either GET or POST data.
*
* @param string $key The key of the parameter to fetch.
* @param string|null $default Default value if the key is not found.
* @return string|null The value of the parameter or default.
* @param mixed $default Default value if the key is not found.
* @return mixed The value of the parameter or default.
*/
public function fetch(string $key, ?string $default = null): ?string
public function fetch(string $key, $default = null)
{
// First check GET, then POST if not found.
return $_GET[$key] ?? $_POST[$key] ?? $default;
Expand All @@ -23,7 +23,7 @@ public function fetch(string $key, ?string $default = null): ?string
* Fetch multiple parameters by keys from either GET or POST data.
*
* @param array<string> $keys The keys of the parameters to fetch.
* @return array<string, string|null> An associative array of parameters.
* @return array<string, mixed> An associative array of parameters.
*/
public function fetchMultiple(array $keys): array
{
Expand All @@ -38,10 +38,10 @@ public function fetchMultiple(array $keys): array
* Fetch a header by key from the request headers.
*
* @param string $key The key of the header to fetch.
* @param string|null $default Default value if the key is not found.
* @return string|null The value of the header or default.
* @param mixed|null $default Default value if the key is not found.
* @return mixed|null The value of the header or default.
*/
public function fetchHeader(string $key, ?string $default = null): ?string
public function fetchHeader(string $key, $default = null)
{
$headers = $this->getAllHeaders();
$normalizedKey = strtolower($key);
Expand All @@ -56,7 +56,7 @@ public function fetchHeader(string $key, ?string $default = null): ?string
/**
* Retrieve all request headers.
*
* @return array<string, string> An associative array of all request headers.
* @return array<string, mixed> An associative array of all request headers.
*/
private function getAllHeaders(): array
{
Expand Down
14 changes: 7 additions & 7 deletions src/HelloRequest/HelloRequestInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,27 @@ interface HelloRequestInterface
* Fetch a parameter by key from either GET or POST data.
*
* @param string $key The key of the parameter to fetch.
* @param string|null $default Default value if the key is not found.
* @return string|null The value of the parameter or default.
* @param mixed $default Default value if the key is not found.
* @return mixed The value of the parameter or default.
*/
public function fetch(string $key, ?string $default = null): ?string;
public function fetch(string $key, $default = null);

/**
* Fetch multiple parameters by keys from either GET or POST data.
*
* @param array<string> $keys The keys of the parameters to fetch.
* @return array<string, string> An associative array of parameters.
* @return array<string, mixed> An associative array of parameters.
*/
public function fetchMultiple(array $keys): array;

/**
* Fetch a header by key from the request headers.
*
* @param string $key The key of the header to fetch.
* @param string|null $default Default value if the key is not found.
* @return string|null The value of the header or default.
* @param mixed $default Default value if the key is not found.
* @return mixed The value of the header or default.
*/
public function fetchHeader(string $key, ?string $default = null): ?string;
public function fetchHeader(string $key, $default = null);

/**
* Fetch a cookie value by name.
Expand Down
4 changes: 2 additions & 2 deletions src/Type/AuthUpdates.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@
return isset($this->additionalProperties[$offset]);
}

public function offsetGet($offset): ?string
public function offsetGet($offset): string

Check warning on line 61 in src/Type/AuthUpdates.php

View check run for this annotation

Codecov / codecov/patch

src/Type/AuthUpdates.php#L61

Added line #L61 was not covered by tests
{
return is_string($this->additionalProperties[$offset]) ? $this->additionalProperties[$offset] : null;
return is_string($this->additionalProperties[$offset]) ? $this->additionalProperties[$offset] : '';

Check warning on line 63 in src/Type/AuthUpdates.php

View check run for this annotation

Codecov / codecov/patch

src/Type/AuthUpdates.php#L63

Added line #L63 was not covered by tests
}

public function offsetSet($offset, $value): void
Expand Down
2 changes: 1 addition & 1 deletion src/Utils/QueryParamFetcher.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
class QueryParamFetcher
{
/**
* @param array<string, mixed> $keys
* @param array<int, string> $keys
* @return array<string, mixed>
*/
public static function fetch(array $keys): array
Expand Down
2 changes: 1 addition & 1 deletion tests/Config/HelloConfigBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class HelloConfigBuilderTest extends TestCase
{
public function testHelloConfigBuilderBuildsConfigCorrectly()
public function testHelloConfigBuilderBuildsConfigCorrectly(): void
{
// Arrange: Create the builder and set values
$builder = new HelloConfigBuilder();
Expand Down
12 changes: 10 additions & 2 deletions tests/Handler/AuthTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ public function testCanHandleAuth(): void
]);

$result = $this->auth->handleAuth();
/** @var string $actual_name */
$actual_name = isset($result->toArray()['authCookie']['name'])
? $result->toArray()['authCookie']['name']
: '';
/** @var string $actual_email */
$actual_email = isset($result->toArray()['authCookie']['name'])
? $result->toArray()['authCookie']['email']
: '';
$this->assertTrue($result->toArray()['isLoggedIn']);
$this->assertEquals($result->toArray()['authCookie']['name'], 'Dick Hardt');
$this->assertEquals($result->toArray()['authCookie']['email'], '[email protected]');
$this->assertEquals('Dick Hardt', $actual_name);
$this->assertEquals('[email protected]', $actual_email);
}
}
11 changes: 7 additions & 4 deletions tests/Handler/CallbackTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ protected function setUp(): void
$this->tokenFetcherMock->method('fetchToken')->willReturn('valid_token_id');
}

public function testHandleCallbackSuccessfulLogin()
public function testHandleCallbackSuccessfulLogin(): void
{
$_COOKIE['oidcName'] = $this->crypto->encrypt([
'code_verifier' => 'test_verifier',
Expand Down Expand Up @@ -68,7 +68,7 @@ public function testHandleCallbackSuccessfulLogin()
$this->assertEquals('http://api.example.com?uri=example.com&appName=MyApp&redirectURI=https%3A%2F%2Fexample.com%2Fcallback&targetURI=%2Fdashboard&wildcard_console=true', $result);
}

public function testHandleCallbackMissingCode()
public function testHandleCallbackMissingCode(): void
{
$_COOKIE['oidcName'] = $this->crypto->encrypt([
'code_verifier' => 'test_verifier',
Expand All @@ -86,10 +86,13 @@ public function testHandleCallbackMissingCode()
]);


$this->assertEquals('/dashboard?error=invalid_request&error_description=Missing+code+parameter', $this->callback->handleCallback());
$this->assertEquals(
'/dashboard?error=invalid_request&error_description=Missing+code+parameter',
$this->callback->handleCallback()
);
}

public function testHandleCallbackInvalidTokenAudience()
public function testHandleCallbackInvalidTokenAudience(): void
{
// Set up mock behaviors
$_GET = array_merge($_GET, [
Expand Down
15 changes: 8 additions & 7 deletions tests/Handler/LoginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protected function setUp(): void
);
}

public function testGenerateLoginUrlSuccess()
public function testGenerateLoginUrlSuccess(): void
{
// Setup mocks
$_GET = [
Expand Down Expand Up @@ -67,15 +67,16 @@ public function testGenerateLoginUrlSuccess()
);

$query = parse_url($url, PHP_URL_QUERY);
$this->assertIsString($query);
parse_str($query, $params);
$this->assertEquals($params['provider_hint'], 'google');
$this->assertEquals($params['scope'], 'openid profile');
$this->assertEquals($params['code_challenge_method'], 'S256');
$this->assertEquals($params['login_hint'], '[email protected]');
$this->assertEquals('google', $params['provider_hint']);
$this->assertEquals('openid profile', $params['scope']);
$this->assertEquals('S256', $params['code_challenge_method']);
$this->assertEquals('[email protected]', $params['login_hint']);
//$this->assertEquals('https://wallet.hello.coop/authorize?client_id=valid_client_id&redirect_uri=https%3A%2F%2Fexample.com%2Fcallback&scope=openid&response_type=code&response_mode=query&nonce=1234&prompt=consent&code_challenge=yyhvlwTA3oVJcnTpkLV70DjqXb794Ar5Sgth12qbRsM&code_challenge_method=S256&provider_hint=google&login_hint=user%40example.com&domain_hint=example.com', $url);
}

public function testGenerateLoginUrlMissingClientId()
public function testGenerateLoginUrlMissingClientId(): void
{
$_GET = [
'provider_hint' => 'google',
Expand All @@ -98,7 +99,7 @@ public function testGenerateLoginUrlMissingClientId()
$this->login->generateLoginUrl();
}

public function testGenerateLoginUrlMissingRedirectURI()
public function testGenerateLoginUrlMissingRedirectURI(): void
{
$_GET = [
'provider_hint' => 'google',
Expand Down
10 changes: 5 additions & 5 deletions tests/HelloClientTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ class HelloClientTest extends TestCase
{
use ServiceMocksTrait;

/** @var MockObject|PageRendererInterface */
/** @var MockObject & PageRendererInterface */
private $pageRendererMock;
/** @var MockObject|Callback */
/** @var MockObject & Callback */
private $callbackMock;
private HelloClient $client;

Expand All @@ -40,7 +40,7 @@ protected function setUp(): void
$this->pageRendererMock->method('renderErrorPage')->willReturn("error_page");
}

public function testRouteHandlesAuth()
public function testRouteHandlesAuth(): void
{
// Simulate $_GET parameters
$_GET = ['op' => 'auth'];
Expand All @@ -61,7 +61,7 @@ public function testRouteHandlesAuth()
$this->assertSame('auth_response', $result);
}

public function testRouteHandlesCallback()
public function testRouteHandlesCallback(): void
{
// Simulate $_GET parameters
$_GET = ['code' => 'callback_code'];
Expand All @@ -88,7 +88,7 @@ public function testRouteHandlesCallback()
$this->assertSame('/dashboard', $result);
}

public function testRouteHandlesCallbackException()
public function testRouteHandlesCallbackException(): void
{
// Simulate $_GET parameters
$_GET = ['code' => 'callback_code'];
Expand Down
Loading
Loading