Skip to content

Commit

Permalink
Improve OrganizationVoter (#915)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmarchois authored Aug 8, 2024
1 parent 9c63f39 commit 70124b2
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 22 deletions.
25 changes: 25 additions & 0 deletions src/Domain/User/Specification/CanUserEditOrganization.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace App\Domain\User\Specification;

use App\Domain\User\Enum\OrganizationRolesEnum;
use App\Domain\User\Organization;
use App\Infrastructure\Security\SymfonyUser;

class CanUserEditOrganization
{
public function isSatisfiedBy(Organization $organization, SymfonyUser $user): bool
{
foreach ($user->getOrganizationUsers() as $organizationUser) {
if ($organizationUser->uuid !== $organization->getUuid()) {
continue;
}

return \in_array(OrganizationRolesEnum::ROLE_ORGA_ADMIN->value, $organizationUser->roles);
}

return false;
}
}
16 changes: 16 additions & 0 deletions src/Domain/User/Specification/CanUserViewOrganization.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace App\Domain\User\Specification;

use App\Domain\User\Organization;
use App\Infrastructure\Security\SymfonyUser;

class CanUserViewOrganization
{
public function isSatisfiedBy(Organization $organization, SymfonyUser $user): bool
{
return \in_array($organization->getUuid(), $user->getOrganizationUuids());
}
}
31 changes: 10 additions & 21 deletions src/Infrastructure/Security/Voter/OrganizationVoter.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

namespace App\Infrastructure\Security\Voter;

use App\Domain\User\Enum\OrganizationRolesEnum;
use App\Domain\User\Organization;
use App\Domain\User\Specification\CanUserEditOrganization;
use App\Domain\User\Specification\CanUserViewOrganization;
use App\Infrastructure\Security\SymfonyUser;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
Expand All @@ -15,6 +16,12 @@ final class OrganizationVoter extends Voter
public const VIEW = 'view';
public const EDIT = 'edit';

public function __construct(
private readonly CanUserEditOrganization $canUserEditOrganization,
private readonly CanUserViewOrganization $canUserViewOrganization,
) {
}

protected function supports(string $attribute, mixed $subject): bool
{
if (!\in_array($attribute, [self::VIEW, self::EDIT])) {
Expand All @@ -40,27 +47,9 @@ protected function voteOnAttribute(string $attribute, mixed $subject, TokenInter
$organization = $subject;

return match ($attribute) {
self::VIEW => $this->canView($organization, $user),
self::EDIT => $this->canEdit($organization, $user),
self::VIEW => $this->canUserViewOrganization->isSatisfiedBy($organization, $user),
self::EDIT => $this->canUserEditOrganization->isSatisfiedBy($organization, $user),
default => throw new \LogicException('This code should not be reached!')
};
}

private function canView(Organization $organization, SymfonyUser $user): bool
{
return \in_array($organization->getUuid(), $user->getOrganizationUuids());
}

private function canEdit(Organization $organization, SymfonyUser $user): bool
{
foreach ($user->getOrganizationUsers() as $organizationUser) {
if ($organizationUser->uuid !== $organization->getUuid()) {
continue;
}

return \in_array(OrganizationRolesEnum::ROLE_ORGA_ADMIN->value, $organizationUser->roles);
}

return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Domain\User\Specification;

use App\Application\User\View\OrganizationView;
use App\Domain\User\Enum\OrganizationRolesEnum;
use App\Domain\User\Organization;
use App\Domain\User\Specification\CanUserEditOrganization;
use App\Infrastructure\Security\SymfonyUser;
use PHPUnit\Framework\TestCase;

final class CanUserEditOrganizationTest extends TestCase
{
public function testCanEdit(): void
{
$organization = $this->createMock(Organization::class);
$organization
->expects(self::once())
->method('getUuid')
->willReturn('c1790745-b915-4fb5-96e7-79b104092a55');

$symfonyUser = $this->createMock(SymfonyUser::class);
$symfonyUser
->expects(self::once())
->method('getOrganizationUsers')
->willReturn([
new OrganizationView('c1790745-b915-4fb5-96e7-79b104092a55', 'Dialog', [OrganizationRolesEnum::ROLE_ORGA_ADMIN->value]),
]);

$pattern = new CanUserEditOrganization();
$this->assertTrue($pattern->isSatisfiedBy($organization, $symfonyUser));
}

public function testCannotEdit(): void
{
$organization = $this->createMock(Organization::class);
$organization
->expects(self::once())
->method('getUuid')
->willReturn('c1790745-b915-4fb5-96e7-79b104092a55');

$symfonyUser = $this->createMock(SymfonyUser::class);
$symfonyUser
->expects(self::once())
->method('getOrganizationUsers')
->willReturn([
new OrganizationView('c1790745-b915-4fb5-96e7-79b104092a55', 'Dialog', [OrganizationRolesEnum::ROLE_ORGA_CONTRIBUTOR->value]),
]);

$pattern = new CanUserEditOrganization();
$this->assertFalse($pattern->isSatisfiedBy($organization, $symfonyUser));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public function testCanPublish(): void
$this->assertTrue($pattern->isSatisfiedBy($regulationOrderRecord, $symfonyUser));
}

public function testCantPublish(): void
public function testCannotPublish(): void
{
$organization = $this->createMock(Organization::class);
$organization
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace App\Tests\Unit\Domain\User\Specification;

use App\Domain\User\Organization;
use App\Domain\User\Specification\CanUserViewOrganization;
use App\Infrastructure\Security\SymfonyUser;
use PHPUnit\Framework\TestCase;

final class CanUserViewOrganizationTest extends TestCase
{
public function testCanView(): void
{
$organization = $this->createMock(Organization::class);
$organization
->expects(self::once())
->method('getUuid')
->willReturn('c1790745-b915-4fb5-96e7-79b104092a55');

$symfonyUser = $this->createMock(SymfonyUser::class);
$symfonyUser
->expects(self::once())
->method('getOrganizationUuids')
->willReturn(['c1790745-b915-4fb5-96e7-79b104092a55']);

$pattern = new CanUserViewOrganization();
$this->assertTrue($pattern->isSatisfiedBy($organization, $symfonyUser));
}

public function testCannotView(): void
{
$organization = $this->createMock(Organization::class);
$organization
->expects(self::once())
->method('getUuid')
->willReturn('28aaef2a-e9d1-4189-9ead-17e866a8726f');

$symfonyUser = $this->createMock(SymfonyUser::class);
$symfonyUser
->expects(self::once())
->method('getOrganizationUuids')
->willReturn(['c1790745-b915-4fb5-96e7-79b104092a55']);

$pattern = new CanUserViewOrganization();
$this->assertFalse($pattern->isSatisfiedBy($organization, $symfonyUser));
}
}

0 comments on commit 70124b2

Please sign in to comment.