forked from fa/breadcrumb-the-shire
Harden module runtime: audited listener failures and DI-first resolver
This commit is contained in:
104
tests/Service/Access/AccessPolicyFactoryModuleResolverTest.php
Normal file
104
tests/Service/Access/AccessPolicyFactoryModuleResolverTest.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Service\Access;
|
||||
|
||||
use MintyPHP\App\AppContainer;
|
||||
use MintyPHP\App\Module\ModuleClassResolver;
|
||||
use MintyPHP\App\Module\ModuleRegistry;
|
||||
use MintyPHP\Service\Access\AccessPolicyFactory;
|
||||
use MintyPHP\Service\Access\AuthorizationDecision;
|
||||
use MintyPHP\Service\Access\AuthorizationPolicyInterface;
|
||||
use MintyPHP\Service\Access\PermissionService;
|
||||
use MintyPHP\Service\Tenant\TenantScopeService;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class AccessPolicyFactoryModuleResolverTest extends TestCase
|
||||
{
|
||||
public function testModulePolicyWithMultipleDependenciesIsResolvedViaContainer(): void
|
||||
{
|
||||
$fixturesDir = sys_get_temp_dir() . '/access-policy-factory-' . uniqid('', true);
|
||||
$moduleDir = $fixturesDir . '/mod-policy';
|
||||
mkdir($moduleDir, 0777, true);
|
||||
|
||||
file_put_contents(
|
||||
$moduleDir . '/module.php',
|
||||
'<?php return ' . var_export([
|
||||
'id' => 'mod-policy',
|
||||
'authorization_policies' => [TestMultiDependencyPolicy::class],
|
||||
], true) . ';'
|
||||
);
|
||||
|
||||
try {
|
||||
$registry = ModuleRegistry::boot($fixturesDir, ['mod-policy']);
|
||||
|
||||
$permissionService = $this->createMock(PermissionService::class);
|
||||
$tenantScopeService = $this->createMock(TenantScopeService::class);
|
||||
$dependency = new TestPolicyDependency();
|
||||
|
||||
$container = new AppContainer();
|
||||
$container->set(
|
||||
TestMultiDependencyPolicy::class,
|
||||
static fn () => new TestMultiDependencyPolicy($permissionService, $dependency)
|
||||
);
|
||||
|
||||
$factory = new AccessPolicyFactory(
|
||||
$permissionService,
|
||||
$tenantScopeService,
|
||||
$registry,
|
||||
static fn (string $class): ?object => ModuleClassResolver::resolve($container, $class)
|
||||
);
|
||||
|
||||
$authorizationService = $factory->createAuthorizationService();
|
||||
$decision = $authorizationService->authorize(TestMultiDependencyPolicy::ABILITY, [
|
||||
'actor_user_id' => 7,
|
||||
]);
|
||||
|
||||
self::assertTrue($decision->isAllowed());
|
||||
self::assertSame('container', $decision->attribute('resolver'));
|
||||
} finally {
|
||||
@unlink($moduleDir . '/module.php');
|
||||
@rmdir($moduleDir);
|
||||
@rmdir($fixturesDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class TestPolicyDependency
|
||||
{
|
||||
public function value(): string
|
||||
{
|
||||
return 'container';
|
||||
}
|
||||
}
|
||||
|
||||
final class TestMultiDependencyPolicy implements AuthorizationPolicyInterface
|
||||
{
|
||||
public const ABILITY = 'module.multi.dep';
|
||||
|
||||
public function __construct(
|
||||
private readonly PermissionService $permissionService,
|
||||
private readonly TestPolicyDependency $dependency
|
||||
) {
|
||||
}
|
||||
|
||||
public function supports(string $ability): bool
|
||||
{
|
||||
return $ability === self::ABILITY;
|
||||
}
|
||||
|
||||
public function authorize(string $ability, array $context = []): AuthorizationDecision
|
||||
{
|
||||
if ($ability !== self::ABILITY) {
|
||||
return AuthorizationDecision::deny(500, 'authorization_ability_not_supported');
|
||||
}
|
||||
|
||||
if ((int) ($context['actor_user_id'] ?? 0) <= 0) {
|
||||
return AuthorizationDecision::deny(403, 'forbidden');
|
||||
}
|
||||
|
||||
return AuthorizationDecision::allow([
|
||||
'resolver' => $this->dependency->value(),
|
||||
'permission_service' => $this->permissionService::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user