Skip to content

Commit 79e62dc

Browse files
authored
Merge pull request #48 from itk-dev/test/mutation-security
test: verify provider key lookup, claims contract and redirect route args
2 parents 52eee94 + 51ffcdb commit 79e62dc

5 files changed

Lines changed: 50 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1818

1919
### Changed
2020

21+
- Dev: strengthened Security tests based on mutation testing findings —
22+
the redirect-route parameters are asserted to reach the router when
23+
building a provider redirect URI, `validateClaims` is asserted to look
24+
up the exact provider key from the session and to merge
25+
`open_id_connect_provider` into the returned claims, and a request
26+
without any `loginToken` parameter is asserted to be rejected as
27+
unauthorized. No effect on the published package.
2128
- Dev: strengthened CLI login flow tests based on mutation testing
2229
findings — redeeming an unknown token is asserted to throw
2330
`TokenNotFoundException` specifically, both cache entries (token and

tests/Security/CliLoginTokenAuthenticatorTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@ public function testAuthenticateWithEmptyToken(): void
6161
$this->authenticator->authenticate($request);
6262
}
6363

64+
public function testAuthenticateWithMissingToken(): void
65+
{
66+
// No loginToken query parameter at all: the null from query->get()
67+
// must be coerced to '' and rejected as "no token provided", not
68+
// passed on to the login helper.
69+
$request = new Request();
70+
71+
$this->expectException(CustomUserMessageAuthenticationException::class);
72+
$this->expectExceptionMessage('No login token provided');
73+
74+
$this->authenticator->authenticate($request);
75+
}
76+
6477
public function testAuthenticateWithInvalidToken(): void
6578
{
6679
$this->stubCliLoginHelper

tests/Security/OpenIdConfigurationProviderManagerTest.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPUnit\Framework\MockObject\Stub;
1010
use PHPUnit\Framework\TestCase;
1111
use Symfony\Component\Cache\Adapter\ArrayAdapter;
12+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
1213
use Symfony\Component\Routing\RouterInterface;
1314

1415
class OpenIdConfigurationProviderManagerTest extends TestCase
@@ -77,9 +78,14 @@ public function testGetProviderThrowsOnInvalidKey(): void
7778

7879
public function testGetProviderWithRedirectRoute(): void
7980
{
80-
$this->stubRouter
81+
// Expect the exact arguments so dropping the route parameters (or the
82+
// route itself) when building the redirect URI fails the test.
83+
$mockRouter = $this->createMock(RouterInterface::class);
84+
$mockRouter->expects($this->once())
8185
->method('generate')
86+
->with('my_route', ['param' => 'value'], UrlGeneratorInterface::ABSOLUTE_URL)
8287
->willReturn('https://app.com/callback');
88+
$this->stubRouter = $mockRouter;
8389

8490
$manager = $this->createManager([
8591
'test' => $this->getBaseProviderConfig() + [

tests/Security/OpenIdLoginAuthenticatorTest.php

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,26 @@ public function testValidateClaimsSuccess(): void
114114
$claims->name = 'Test Tester';
115115
$stubProvider->method('validateIdToken')->willReturn($claims);
116116

117-
$this->stubProviderManager->method('getProvider')->willReturn($stubProvider);
117+
// Expect the exact provider key from the session, so a lookup with a
118+
// mangled key fails the test instead of silently matching any key.
119+
$mockProviderManager = $this->createMock(OpenIdConfigurationProviderManager::class);
120+
$mockProviderManager->expects($this->once())
121+
->method('getProvider')
122+
->with('test_provider_1')
123+
->willReturn($stubProvider);
124+
$authenticator = new TestAuthenticator($mockProviderManager);
118125

119126
$request = new Request(query: ['state' => 'test_state', 'code' => 'test_code']);
120127
$this->setSessionOnRequest($request);
121128

122-
$passport = $this->authenticator->authenticate($request);
129+
$passport = $authenticator->authenticate($request);
123130

124131
$this->assertSame('test@test.com', $passport->getUser()->getUserIdentifier());
132+
133+
// The claims contract: the IdP claims plus the provider key that
134+
// authenticated the user.
135+
$this->assertSame('Test Tester', $authenticator->lastClaims['name'] ?? null);
136+
$this->assertSame('test_provider_1', $authenticator->lastClaims['open_id_connect_provider'] ?? null);
125137
}
126138

127139
private function setSessionOnRequest(Request $request, ?string $nonce = 'test_nonce'): void

tests/Security/TestAuthenticator.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,18 @@
1313

1414
class TestAuthenticator extends OpenIdLoginAuthenticator
1515
{
16+
/**
17+
* Claims returned by the last validateClaims() call, exposed so tests
18+
* can assert on the full claims array (validateClaims is protected).
19+
*
20+
* @var array<string, string>
21+
*/
22+
public array $lastClaims = [];
23+
1624
public function authenticate(Request $request): Passport
1725
{
1826
$claims = $this->validateClaims($request);
27+
$this->lastClaims = $claims;
1928

2029
return new SelfValidatingPassport(
2130
new UserBadge(

0 commit comments

Comments
 (0)