Skip to content

[Security] Document is persisted before voters result on securityPostDenormalize #8429

Description

@Julbert123

API Platform version(s) affected: 4.3.5

Description
When a resource is behind a voter on Patch operation and custom logic is called with a Voter using securityPostDenormalize, the incoming document is still persisted and therefore is written in the database if a flush comes after.

AgentRole.php

#[ApiResource(operations: [
    new Patch(
        securityPostDenormalize: "is_granted('ROLE_UPDATE', object)",
        extraProperties: ['throw_on_access_denied' => true],
    ),
])]

AgentRoleVoter.php

protected function supports(string $attribute, mixed $subject): bool
{
    $supportAttributes = $attribute == 'ROLE_UPDATE';
    $supportSubject = $subject instanceof AgentRole;

    return $supportAttributes && $supportSubject;
}

protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
    // some false logic
}

AgentRoleTest.php

use ResetDatabase;

public function testChangeAgentRoleToDev()
{
    $client = static::createClientWithCredentials();
    $dm = $this->getContainer()->get(DocumentManager::class);

    $agentRoles = AgentRoleFactory::createSequence([
        ['agentId' => '1', 'roles' => ['ROLE_USER']],
        ['agentId' => '2', 'roles' => ['ROLE_USER']]
    ]);

    $client->request('PATCH', '/agent_roles/1', [
        'headers' => ['Content-Type' => 'application/merge-patch+json'],
        'json' => [
            'roles' => ['ROLE_USER', 'ROLE_DEV']
        ]
    ]);

    $this->assertResponseStatusCodeSame(403); // OK

    // I would need to use $dm->clear() to flush previous operation without persisting
    // $dm->clear();

    $currentUserRole = $dm->getRepository(AgentRole::class)->findOneBy([
        'agentId' => 'user' // current logged user
    ]);
    $currentUserRole->setRoles(['ROLE_ADMIN']);
    $dm->persist($currentUserRole);
    $dm->flush(); // <-------- this persist also the request operation which wasn't persisted on request execution
}

Before flush:
Image

After:
Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions