Add REMOTE_USER env var with four modes: - session (default): sends session id, backward-compatible - static: sends a fixed string (REMOTE_USER_STATIC) - mapped: looks up session id in REMOTE_USER_MAP - none: omits the header entirely New RemoteUserMode enum, ConfigBag parsing/validation, and StringTrait::authSuccessResponse resolves the header value based on the configured mode. AcceptListener now receives ConfigBag as a constructor dependency. Addresses design consideration 1.2 (Remote-User header value is user-controlled) from DESIGN_CONSIDERATIONS.md. 241 tests pass, 0 cs-fixer violations.
133 lines
3.4 KiB
PHP
133 lines
3.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Tests\Unit;
|
|
|
|
use App\ConfigBag;
|
|
use App\Utilities;
|
|
use PHPUnit\Framework\TestCase;
|
|
use Psr\Cache\CacheItemInterface;
|
|
use Psr\Cache\CacheItemPoolInterface;
|
|
use Psr\Clock\ClockInterface;
|
|
|
|
final class ConfigBagTest extends TestCase
|
|
{
|
|
private function createUtilities(?string $totp = null): Utilities
|
|
{
|
|
$clock = $this->createStub(ClockInterface::class);
|
|
$cache = $this->createStub(CacheItemPoolInterface::class);
|
|
|
|
if ($totp !== null) {
|
|
$item = $this->createStub(CacheItemInterface::class);
|
|
$item->method('isHit')->willReturn(true);
|
|
$item->method('get')->willReturn($totp);
|
|
$cache->method('hasItem')->willReturn(true);
|
|
$cache->method('getItem')->willReturn($item);
|
|
} else {
|
|
$cache->method('hasItem')->willReturn(false);
|
|
}
|
|
|
|
return new Utilities($clock, $cache);
|
|
}
|
|
|
|
public function testGettersWithExplicitValues(): void
|
|
{
|
|
$clock = $this->createStub(ClockInterface::class);
|
|
$utilities = $this->createUtilities();
|
|
|
|
$config = new ConfigBag(
|
|
$utilities,
|
|
$clock,
|
|
3600,
|
|
'otpauth://totp/test',
|
|
1800,
|
|
true,
|
|
'Error!',
|
|
'Teapot!',
|
|
'Too Many!',
|
|
'session',
|
|
'authenticated',
|
|
'',
|
|
);
|
|
|
|
self::assertSame($clock, $config->clock());
|
|
self::assertSame(3600, $config->cookieTtl());
|
|
self::assertSame('otpauth://totp/test', $config->totpUri());
|
|
self::assertSame(1800, $config->ipTtl());
|
|
self::assertTrue($config->teapot());
|
|
self::assertSame('Error!', $config->errorMessage());
|
|
self::assertSame('Teapot!', $config->teapotTitle());
|
|
self::assertSame('Too Many!', $config->tooManyTitle());
|
|
}
|
|
|
|
public function testTotpUriFallsBackToUtilitiesWhenEmpty(): void
|
|
{
|
|
$clock = $this->createStub(ClockInterface::class);
|
|
$utilities = $this->createUtilities('fallback-totp');
|
|
|
|
$config = new ConfigBag(
|
|
$utilities,
|
|
$clock,
|
|
3600,
|
|
'',
|
|
1800,
|
|
false,
|
|
'Error',
|
|
'Teapot',
|
|
'Too Many',
|
|
'session',
|
|
'authenticated',
|
|
'',
|
|
);
|
|
|
|
self::assertSame('fallback-totp', $config->totpUri());
|
|
}
|
|
|
|
public function testIpTtlFallsBackToNullWhenZero(): void
|
|
{
|
|
$clock = $this->createStub(ClockInterface::class);
|
|
$utilities = $this->createUtilities();
|
|
|
|
$config = new ConfigBag(
|
|
$utilities,
|
|
$clock,
|
|
3600,
|
|
'otpauth://totp/test',
|
|
0,
|
|
false,
|
|
'Error',
|
|
'Teapot',
|
|
'Too Many',
|
|
'session',
|
|
'authenticated',
|
|
'',
|
|
);
|
|
|
|
self::assertNull($config->ipTtl());
|
|
}
|
|
|
|
public function testIpTtlFallsBackToNullWhenNull(): void
|
|
{
|
|
$clock = $this->createStub(ClockInterface::class);
|
|
$utilities = $this->createUtilities();
|
|
|
|
$config = new ConfigBag(
|
|
$utilities,
|
|
$clock,
|
|
3600,
|
|
'otpauth://totp/test',
|
|
null,
|
|
false,
|
|
'Error',
|
|
'Teapot',
|
|
'Too Many',
|
|
'session',
|
|
'authenticated',
|
|
'',
|
|
);
|
|
|
|
self::assertNull($config->ipTtl());
|
|
}
|
|
}
|