Skip to content

[Security] Using IsGranted in combination with MapRequestPayload passes incorrect subject to voter #50964

Closed
@DjordyKoert

Description

@DjordyKoert

Symfony version(s) affected

6.3.0

Description

Using the IsGranted attribute in combination with the MapRequestPayload results in the wrong subject being passed into the voters.

What I expected was that my voter would receive a DTO class (ExampleDTO) or atleast the class-string when the supports method gets called by symfony for the $subject argument, instead what I got was $subject of type MapRequestPayload which is the attribute of the controller argument.

How to reproduce

Controller route

...
    #[Route(
        '/api/v1/example',
        name: 'exampleMethod',
        methods: ['POST']
    )]
    #[IsGranted(
        'DTO',
        subject: 'exampleDTO',
    )]
    public function exampleMethod(#[MapRequestPayload] ExampleDTO $exampleDTO): Response {
...

DTO

#[DTO('example:permission')]
class ExampleDTO
{
    public function __construct(
        private int $id,
    ) {
    }

    public function getId(): int
    {
        return $this->id;
    }
}

Customer DTO Attribute

#[Attribute(Attribute::TARGET_CLASS)]
final readonly class DTO
{
    /**
     * @param string[] | string $permissions
     */
    public function __construct(
        private array | string $permissions
    ) {
    }

    /**
     * @return string[] | string
     */
    public function getPermissions(): array | string
    {
        return $this->permissions;
    }
}

Custom DTO voter

final class DTOVoter extends Voter
{
    /**
     * @param class-string|object $subject
     */
    protected function supports(string $attribute, mixed $subject): bool
    {
        // TODO: Check if attribute is of type DTO
        $class = new ReflectionClass($subject);

        /**
         * @var DTO[] $attributes
         */
        $attributes = $class->getAttributes(DTO::class, ReflectionAttribute::IS_INSTANCEOF);

        dump($subject);

        if ($attributes === []) {
            return false;
        }

       return true;
    }
...

Possible Solution

if ($arguments[$subjectRef] instanceof MapRequestPayload) {
    return $arguments[$subjectRef]->metadata->getType();
}

return $arguments[$subjectRef];

Additional Context

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions