mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-09-01 20:43:49 +00:00
Update Workflow Permission Handling
Refactor the `WorkflowRelatedEntityPermissionHelper` to enhance permission checks for workflow-related entities. This includes updating methods, improving test coverage, and incorporating `MockClock` for date-sensitive operations.
This commit is contained in:
@@ -15,10 +15,11 @@ use Chill\DocStoreBundle\Entity\StoredObject;
|
||||
use Chill\DocStoreBundle\Repository\AssociatedEntityToStoredObjectInterface;
|
||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectRoleEnum;
|
||||
use Chill\DocStoreBundle\Security\Authorization\StoredObjectVoter\AbstractStoredObjectVoter;
|
||||
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
|
||||
use Chill\MainBundle\Entity\User;
|
||||
use Chill\MainBundle\Workflow\Helper\WorkflowRelatedEntityPermissionHelper;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
|
||||
use Prophecy\PhpUnit\ProphecyTrait;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
|
||||
use Symfony\Component\Security\Core\Security;
|
||||
|
||||
/**
|
||||
@@ -28,19 +29,14 @@ use Symfony\Component\Security\Core\Security;
|
||||
*/
|
||||
class AbstractStoredObjectVoterTest extends TestCase
|
||||
{
|
||||
private AssociatedEntityToStoredObjectInterface $repository;
|
||||
private Security $security;
|
||||
private WorkflowRelatedEntityPermissionHelper $workflowDocumentService;
|
||||
use ProphecyTrait;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->repository = $this->createMock(AssociatedEntityToStoredObjectInterface::class);
|
||||
$this->security = $this->createMock(Security::class);
|
||||
$this->workflowDocumentService = $this->createMock(WorkflowRelatedEntityPermissionHelper::class);
|
||||
}
|
||||
|
||||
private function buildStoredObjectVoter(bool $canBeAssociatedWithWorkflow, AssociatedEntityToStoredObjectInterface $repository, Security $security, ?WorkflowRelatedEntityPermissionHelper $workflowDocumentService = null): AbstractStoredObjectVoter
|
||||
{
|
||||
private function buildStoredObjectVoter(
|
||||
bool $canBeAssociatedWithWorkflow,
|
||||
AssociatedEntityToStoredObjectInterface $repository,
|
||||
Security $security,
|
||||
?WorkflowRelatedEntityPermissionHelper $workflowDocumentService = null,
|
||||
): AbstractStoredObjectVoter {
|
||||
// Anonymous class extending the abstract class
|
||||
return new class ($canBeAssociatedWithWorkflow, $repository, $security, $workflowDocumentService) extends AbstractStoredObjectVoter {
|
||||
public function __construct(
|
||||
@@ -74,95 +70,89 @@ class AbstractStoredObjectVoterTest extends TestCase
|
||||
};
|
||||
}
|
||||
|
||||
private function setupMockObjects(): array
|
||||
{
|
||||
$user = new User();
|
||||
$token = $this->createMock(TokenInterface::class);
|
||||
$subject = new StoredObject();
|
||||
$entity = new \stdClass();
|
||||
|
||||
return [$user, $token, $subject, $entity];
|
||||
}
|
||||
|
||||
private function setupMocksForVoteOnAttribute(User $user, TokenInterface $token, bool $isGrantedForEntity, object $entity, bool $workflowAllowed): void
|
||||
{
|
||||
// Set up token to return user
|
||||
$token->method('getUser')->willReturn($user);
|
||||
|
||||
// Mock the return of an AccompanyingCourseDocument by the repository
|
||||
$this->repository->method('findAssociatedEntityToStoredObject')->willReturn($entity);
|
||||
|
||||
// Mock scenario where user is allowed to see_details of the AccompanyingCourseDocument
|
||||
$this->security->method('isGranted')->willReturn($isGrantedForEntity);
|
||||
|
||||
// Mock case where user is blocked or not by workflow
|
||||
$this->workflowDocumentService->method('notBlockedByWorkflow')->willReturn($workflowAllowed);
|
||||
}
|
||||
|
||||
public function testSupportsOnAttribute(): void
|
||||
{
|
||||
[$user, $token, $subject, $entity] = $this->setupMockObjects();
|
||||
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(new \stdClass()), $this->prophesize(Security::class)->reveal(), null);
|
||||
|
||||
// Setup mocks for voteOnAttribute method
|
||||
$this->setupMocksForVoteOnAttribute($user, $token, true, $entity, true);
|
||||
$voter = $this->buildStoredObjectVoter(true, $this->repository, $this->security, $this->workflowDocumentService);
|
||||
self::assertTrue($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
|
||||
|
||||
self::assertTrue($voter->supports(StoredObjectRoleEnum::SEE, $subject));
|
||||
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(new User()), $this->prophesize(Security::class)->reveal(), null);
|
||||
|
||||
self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
|
||||
|
||||
$voter = $this->buildStoredObjectVoter(false, new DummyRepository(null), $this->prophesize(Security::class)->reveal(), null);
|
||||
|
||||
self::assertFalse($voter->supports(StoredObjectRoleEnum::SEE, new StoredObject()));
|
||||
}
|
||||
|
||||
public function testVoteOnAttributeAllowedAndWorkflowAllowed(): void
|
||||
{
|
||||
[$user, $token, $subject, $entity] = $this->setupMockObjects();
|
||||
/**
|
||||
* @dataProvider dataProviderVoteOnAttribute
|
||||
*/
|
||||
public function testVoteOnAttribute(
|
||||
StoredObjectRoleEnum $attribute,
|
||||
bool $expected,
|
||||
bool $canBeAssociatedWithWorkflow,
|
||||
bool $isGrantedRegularPermission,
|
||||
?string $isGrantedWorkflowPermissionRead,
|
||||
?string $isGrantedWorkflowPermissionWrite,
|
||||
string $message,
|
||||
): void {
|
||||
$storedObject = new StoredObject();
|
||||
$dummyRepository = new DummyRepository($related = new \stdClass());
|
||||
$token = new UsernamePasswordToken(new User(), 'dummy');
|
||||
|
||||
// Setup mocks for voteOnAttribute method
|
||||
$this->setupMocksForVoteOnAttribute($user, $token, true, $entity, true);
|
||||
$voter = $this->buildStoredObjectVoter(true, $this->repository, $this->security, $this->workflowDocumentService);
|
||||
$security = $this->prophesize(Security::class);
|
||||
$security->isGranted('SOME_ROLE', $related)->willReturn($isGrantedRegularPermission);
|
||||
|
||||
// The voteOnAttribute method should return True when workflow is allowed
|
||||
self::assertTrue($voter->voteOnAttribute(StoredObjectRoleEnum::SEE, $subject, $token));
|
||||
$workflowRelatedEntityPermissionHelper = $this->prophesize(WorkflowRelatedEntityPermissionHelper::class);
|
||||
if (null !== $isGrantedWorkflowPermissionRead) {
|
||||
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related)
|
||||
->willReturn($isGrantedWorkflowPermissionRead)->shouldBeCalled();
|
||||
} else {
|
||||
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForReadOperation($related)->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
if (null !== $isGrantedWorkflowPermissionWrite) {
|
||||
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($related)
|
||||
->willReturn($isGrantedWorkflowPermissionWrite)->shouldBeCalled();
|
||||
} else {
|
||||
$workflowRelatedEntityPermissionHelper->isAllowedByWorkflowForWriteOperation($related)->shouldNotBeCalled();
|
||||
}
|
||||
|
||||
$voter = $this->buildStoredObjectVoter($canBeAssociatedWithWorkflow, $dummyRepository, $security->reveal(), $workflowRelatedEntityPermissionHelper->reveal());
|
||||
self::assertEquals($expected, $voter->voteOnAttribute($attribute, $storedObject, $token), $message);
|
||||
}
|
||||
|
||||
public function testVoteOnAttributeNotAllowed(): void
|
||||
public static function dataProviderVoteOnAttribute(): iterable
|
||||
{
|
||||
[$user, $token, $subject, $entity] = $this->setupMockObjects();
|
||||
// not associated on a workflow
|
||||
yield [StoredObjectRoleEnum::SEE, true, false, true, null, null, 'not associated on a workflow, granted by regular access, must not rely on helper'];
|
||||
yield [StoredObjectRoleEnum::SEE, false, false, false, null, null, 'not associated on a workflow, denied by regular access, must not rely on helper'];
|
||||
|
||||
// Setup mocks for voteOnAttribute method where isGranted() returns false
|
||||
$this->setupMocksForVoteOnAttribute($user, $token, false, $entity, true);
|
||||
$voter = $this->buildStoredObjectVoter(true, $this->repository, $this->security, $this->workflowDocumentService);
|
||||
// associated on a workflow, read operation
|
||||
yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be granted'];
|
||||
yield [StoredObjectRoleEnum::SEE, true, true, true, WorkflowRelatedEntityPermissionHelper::ABSTAIN, null, 'associated on a workflow, read by regular, abstain by workflow, ask for read, should be granted'];
|
||||
yield [StoredObjectRoleEnum::SEE, false, true, true, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, null, 'associated on a workflow, read by regular, force grant by workflow, ask for read, should be denied'];
|
||||
yield [StoredObjectRoleEnum::SEE, true, true, false, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be granted'];
|
||||
yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelper::ABSTAIN, null, 'associated on a workflow, denied read by regular, abstain by workflow, ask for read, should be granted'];
|
||||
yield [StoredObjectRoleEnum::SEE, false, true, false, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, null, 'associated on a workflow, denied read by regular, force grant by workflow, ask for read, should be denied'];
|
||||
|
||||
// The voteOnAttribute method should return True when workflow is allowed
|
||||
self::assertFalse($voter->voteOnAttribute(StoredObjectRoleEnum::SEE, $subject, $token));
|
||||
}
|
||||
|
||||
public function testVoteOnAttributeAllowedWorkflowNotAllowed(): void
|
||||
{
|
||||
[$user, $token, $subject, $entity] = $this->setupMockObjects();
|
||||
|
||||
// Setup mocks for voteOnAttribute method
|
||||
$this->setupMocksForVoteOnAttribute($user, $token, true, $entity, false);
|
||||
$voter = $this->buildStoredObjectVoter(true, $this->repository, $this->security, $this->workflowDocumentService);
|
||||
|
||||
// Test voteOnAttribute method
|
||||
$attribute = StoredObjectRoleEnum::EDIT;
|
||||
$result = $voter->voteOnAttribute($attribute, $subject, $token);
|
||||
|
||||
// Assert that access is denied when workflow is not allowed
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
public function testVoteOnAttributeAllowedWorkflowAllowedToSeeDocument(): void
|
||||
{
|
||||
[$user, $token, $subject, $entity] = $this->setupMockObjects();
|
||||
|
||||
// Setup mocks for voteOnAttribute method
|
||||
$this->setupMocksForVoteOnAttribute($user, $token, true, $entity, false);
|
||||
$voter = $this->buildStoredObjectVoter(true, $this->repository, $this->security, $this->workflowDocumentService);
|
||||
|
||||
// Test voteOnAttribute method
|
||||
$attribute = StoredObjectRoleEnum::SEE;
|
||||
$result = $voter->voteOnAttribute($attribute, $subject, $token);
|
||||
|
||||
// Assert that access is denied when workflow is not allowed
|
||||
$this->assertTrue($result);
|
||||
// association on a workflow, write operation
|
||||
yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be granted'];
|
||||
yield [StoredObjectRoleEnum::EDIT, true, true, true, null, WorkflowRelatedEntityPermissionHelper::ABSTAIN, 'associated on a workflow, write by regular, abstain by workflow, ask for write, should be granted'];
|
||||
yield [StoredObjectRoleEnum::EDIT, false, true, true, null, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, 'associated on a workflow, write by regular, force grant by workflow, ask for write, should be denied'];
|
||||
yield [StoredObjectRoleEnum::EDIT, true, true, false, null, WorkflowRelatedEntityPermissionHelper::FORCE_GRANT, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be granted'];
|
||||
yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelper::ABSTAIN, 'associated on a workflow, denied write by regular, abstain by workflow, ask for write, should be granted'];
|
||||
yield [StoredObjectRoleEnum::EDIT, false, true, false, null, WorkflowRelatedEntityPermissionHelper::FORCE_DENIED, 'associated on a workflow, denied write by regular, force grant by workflow, ask for write, should be denied'];
|
||||
}
|
||||
}
|
||||
|
||||
class DummyRepository implements AssociatedEntityToStoredObjectInterface
|
||||
{
|
||||
public function __construct(private readonly ?object $relatedEntity) {}
|
||||
|
||||
public function findAssociatedEntityToStoredObject(StoredObject $storedObject): ?object
|
||||
{
|
||||
return $this->relatedEntity;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user