* SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * See the LICENSE file in the project root for the full license text. */ declare(strict_types=1); namespace App\Tests\Auth; use App\Auth\BootstrapAdmin; use App\Tests\TestCase; /** * Lock-in tests for R01-N03 — the OIDC bootstrap matcher. The matcher * is the only thing standing between an unconfigured deploy and the * historical "first user wins" land-grab. */ final class BootstrapAdminTest extends TestCase { /** @var array */ private array $envBackup = []; /** @var string[] */ private array $envKeys = [ 'BOOTSTRAP_ADMIN_OID', 'BOOTSTRAP_ADMIN_EMAIL', ]; protected function setUp(): void { parent::setUp(); foreach ($this->envKeys as $k) { $this->envBackup[$k] = getenv($k); putenv($k); } } protected function tearDown(): void { foreach ($this->envKeys as $k) { $prev = $this->envBackup[$k] ?? false; if ($prev === false) { putenv($k); } else { putenv("{$k}={$prev}"); } } parent::tearDown(); } public function testNotConfiguredByDefault(): void { self::assertFalse(BootstrapAdmin::isConfigured()); self::assertFalse( BootstrapAdmin::matches('any-oid', 'any@example.com'), 'unconfigured matcher must never promote anyone' ); } public function testMatchesByOid(): void { putenv('BOOTSTRAP_ADMIN_OID=00000000-0000-0000-0000-000000000001'); self::assertTrue(BootstrapAdmin::isConfigured()); self::assertTrue(BootstrapAdmin::matches( '00000000-0000-0000-0000-000000000001', 'unrelated@example.com' )); self::assertFalse(BootstrapAdmin::matches( '00000000-0000-0000-0000-000000000002', 'unrelated@example.com' )); } public function testMatchesByEmail(): void { putenv('BOOTSTRAP_ADMIN_EMAIL=admin@example.com'); self::assertTrue(BootstrapAdmin::isConfigured()); self::assertTrue(BootstrapAdmin::matches( 'oid-does-not-matter', 'admin@example.com' )); self::assertFalse(BootstrapAdmin::matches( 'oid-does-not-matter', 'attacker@example.com' )); } public function testEitherChannelMatchesWhenBothConfigured(): void { putenv('BOOTSTRAP_ADMIN_OID=oid-1'); putenv('BOOTSTRAP_ADMIN_EMAIL=admin@example.com'); self::assertTrue(BootstrapAdmin::matches('oid-1', 'someone-else@x')); self::assertTrue(BootstrapAdmin::matches('oid-99', 'admin@example.com')); self::assertFalse(BootstrapAdmin::matches('oid-99', 'someone-else@x')); } public function testMatchIsCaseInsensitiveAndTrimmed(): void { putenv('BOOTSTRAP_ADMIN_EMAIL=Admin@Example.COM'); self::assertTrue(BootstrapAdmin::matches('', ' admin@example.com ')); putenv('BOOTSTRAP_ADMIN_EMAIL'); putenv('BOOTSTRAP_ADMIN_OID=ABCDEF-1234'); self::assertTrue(BootstrapAdmin::matches(' abcdef-1234 ', '')); } public function testBlankIncomingFieldsNeverMatch(): void { // Blank email in env shouldn't match a blank claim — config is // OID-only here, so an empty email coming back from Entra must // not opportunistically match the absent BOOTSTRAP_ADMIN_EMAIL. putenv('BOOTSTRAP_ADMIN_OID=oid-1'); self::assertFalse(BootstrapAdmin::matches('', '')); self::assertFalse(BootstrapAdmin::matches('', 'bob@x')); } public function testWhitespaceOnlyEnvTreatedAsUnset(): void { putenv('BOOTSTRAP_ADMIN_OID= '); putenv('BOOTSTRAP_ADMIN_EMAIL= '); self::assertFalse(BootstrapAdmin::isConfigured()); self::assertFalse(BootstrapAdmin::matches('anything', 'anything@x')); } public function testOidValueOverEmailIsPreferred(): void { // Both are set; if the user's oid matches we promote regardless of // a (presumably stale) BOOTSTRAP_ADMIN_EMAIL. Sanity check that // either-channel-wins still holds. putenv('BOOTSTRAP_ADMIN_OID=primary-oid'); putenv('BOOTSTRAP_ADMIN_EMAIL=admin@example.com'); self::assertTrue(BootstrapAdmin::matches('primary-oid', 'noone@x')); } }