Closed
Description
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