Skip to content

Security checking on mapped resources #7880

@Fabrn

Description

@Fabrn

Description

Hello,

As development standards regarding ApiPlatform are evolving towards DTOs defined as resources and mapped to entities using stateOptions + #[Map], I ran into a use case where I need to check access in order to allow the endpoint to work. However, using the object variable into the security expression will use the resource itself, not the entity which it comes from.

Example

#[
    ApiResouce(
        uri_template: '/entites/{id}/my_resource'
        uriVariables: ['id' => Link(fromClass: Entity::class)],
        stateOptions: new Options(entityClass: Entity::class),
    ),
    Get(
        security: 'is_granted("READ", object)',
    ),
]
#[Map(source: Entity::class)]
class MappedResource
{
}

Suggestion

Since I'm not sure that replacing object with the actual entity is a great idea, shouldn't we add a mapped_object variable to the expression's context so we can easily access the actual entity. This value is currenly available using the attribute named mapped_data in the current Request. It would be quite the same as previous_object. Note that it is currently possible to use it by using the Request from the request variable of the expression's context :

security: 'is_granted("READ", request?.attributes?.get("mapped_data"))',

However it's quite verbose for something that should be used quite often. I think this is a valid use case since Voters will often vote on entities, also it allows users to funnel all of their DTOs to the same Voters for the entity.

Let me know what you think, and have a good day 😄


Edit

Note that I came accross a use case where we have a DTO representing a subresource of an entity (ManyToOne relationship). In that case, the identifier will be the one of the main resource, but the DTO will be mapped to the subresource like so :

#[
    ApiResouce(
        uri_template: '/entites/{id}/sub_resources'
        uriVariables: ['id' => Link(fromClass: Entity::class)],
        stateOptions: new Options(entityClass: Entity::class),
    ),
]
#[Map(source: SubResource::class)]
class MappedResource
{
}

In that case, the entity is not resolved, so we couldn't even access it using mapped_data. Maybe there is a confusion between stateOptions and mapping.

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