如何根据用户角色和请求方法设置规范化组 [英] How to set normalization groups based on user's role and request method

查看:82
本文介绍了如何根据用户角色和请求方法设置规范化组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个沙箱应用程序作为Api平台实践,并且我要解决以下问题:
让我们考虑以下针对用户实体的REST端点:
免责声明在代码示例中还有一些属性,但是整个概念适用于该属性

集合获取(又名/api/users)-仅适用于管理员用户(所有可用属性,也许我们排除了哈希密码)

POST -每个人都应该有权访问以下属性:用户名,电子邮件,plainPassword(在有人询问时不要保留)

PATCH/PUT -在这里变得非常棘手:我希望拥有ROLE_ADMIN的用户可以访问用户名,电子邮件和plainPassword字段.而那些所有者只能更改plainPassword

删除-只有ROLE_ADMIN和所有者可以删除

我将从资源配置开始

I am creating a sandbox app as Api-platform practice and I have the following problem to address:
Let's consider following REST endpoint for user entity:
DISCLAIMER in the code examples there are a little more attributes but the whole concept applies regarding that

Collection-get(aka. /api/users) - only available for admin users(all attributes available, maybe we exclude hashed password)

POST - everyone should have access to following attributes: username, email, plainPassword(not persisted just in case someone asks)

PATCH/PUT - here it becomes quite tricky: I want those with ROLE_ADMIN to have access to username, email, plainPassword fields. And those who are the owners to only be able to alter plainPassword

DELETE - only ROLE_ADMIN and owners can delete

I will start with the resource config

resources:
    App\Entity\User:
        # attributes:
        #     normalization_context:
        #         groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
        #     denormalization_context:
        #         groups: ['read', 'put', 'patch', 'post', 'get', 'collection:get']
        collectionOperations:
            get:
                security: 'is_granted("ROLE_ADMIN")'
                normalization_context: { groups: ['collection:get'] }
            post: 
                normalization_context: { groups: ['admin:post', 'post'] }
        itemOperations:
            get:
                normalization_context: { groups: ['admin:get', 'get'] }
                security: 'is_granted("ROLE_ADMIN") or object == user'
            put:
                normalization_context: { groups: ['admin:put', 'put'] }
                security: 'is_granted("ROLE_ADMIN") or object == user'
            patch:
                normalization_context: { groups: ['admin:patch', 'patch'] }
                security: 'is_granted("ROLE_ADMIN") or object == user'
            delete:
                security: 'is_granted("ROLE_ADMIN") or object == user'

这是序列化程序配置

App\Entity\User:
    attributes:
        username:
            groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
        email:
            groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
        firstName:
            groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
        lastName:
            groups: ['post', 'admin:put', 'admin:patch', 'collection:get', 'get']
        plainPassword:
            groups: ['post', patch]
        createdAt:
            groups: ['get', 'collection:get']
        lastLoginDate:
            groups: ['get', 'collection:get']
        updatedAt:
            groups: ['collection:get']

这是上下文组构建器(如API平台文档中所述注册为服务

Here is the context group builder(Registered as service as it's stated in API-platform doc

<?php

namespace App\Serializer;

use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

final class AdminContextBuilder implements SerializerContextBuilderInterface
{
    private $decorated;
    private $authorizationChecker;

    public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->decorated = $decorated;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
    {
        $context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);

        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
            switch($request->getMethod()) {
                case 'GET':
                    $context['groups'][] = 'admin:get';
                    break;
                case 'POST':
                    $context['groups'][] = 'admin:post';
                case 'PUT':
                    $context['groups'][] = 'admin:put';
                case 'PATCH':
                    $context['groups'][] = 'admin:patch';
            }
        }

        return $context;
    }
}

问题在于,即使我仅以ROLE_USER身份登录,我仍然能够更改用户名字段,该字段应根据admin:patch规范化组进行锁定.我对api平台还很陌生,我不太了解为什么这不起作用,但是我想上下文生成器会出现问题.感谢您的帮助,如果我在此期间提出一些建议,我将及时更新问题

The issue is that even if I'm logged as a user with only ROLE_USER I am still able to alter username field which should be locked according to the admin:patch normalization group. I am pretty new to the api-platform and I can't quite understand why this does not work but I guess there will be an issue with the context builder. Thanks for your help I'll keep the question updated if I come up with something in the meantime

推荐答案

在研究了文档并浏览了youtube,并尝试了上述用户资源后,我提出了解决方案.
让我们再次从配置开始:

After investigating the docs and browsing youtube and most of all experimenting with the aformentioned user resource I came up with the solution
Let's start with the configuration again:

resources:
    App\Entity\User:
        collectionOperations:
            get:
                security: 'is_granted("ROLE_ADMIN")'
                normalization_context: { groups: ['collection:get'] }
                denormalization_context: { groups: ['collection:get'] }
            post:
                normalization_context: { groups: ['post'] }
                denormalization_context: { groups: ['post'] }
        itemOperations:
            get:
                normalization_context: { groups: ['get'] }
                security: 'is_granted("ROLE_ADMIN") or object == user'
            patch:
                normalization_context: { groups: ['patch'] }
                denormalization_context: { groups: ['patch'] }
                security: 'is_granted("ROLE_ADMIN") or object == user'
            delete:
                security: 'is_granted("ROLE_ADMIN") or object == user'

起点之间的主要区别在于,永远不要在操作组中声明管理员操作,因为默认情况下会将它们添加到上下文中.

接下来的属性组中,我们定义了某些属性上所有可用的操作

The main difference between the starting point is that the admin actions should never be stated in the operation groups because they will be added by default to the context.

Next the property groups where we define all the operations available on certain property

App\Entity\User:
    attributes:
        id:
            groups: ['get', 'collection:get']
        username:
            groups: ['post', 'admin:patch', 'get', 'collection:get']
        email:
            groups: ['post', 'admin:patch', 'get', 'collection:get']
        plainPassword:
            groups: ['post', 'patch', 'collection:get']
        firstName:
            groups: ['post', 'patch', 'get', 'collection:get']
        lastName:
            groups: ['post', 'get', 'collection:get']
        createdAt:
            groups: ['get', 'collection:get']
        lastLoginDate:
            groups: ['get', 'collection:get']
        updatedAt:
            groups: ['collection:get']

这与问题中的问题完全相同,我们仅需配置的内容是哪些操作需要成为管理员",无论您编写博客,图书馆,商店还是任何需要,都可以根据您的需要进行更改API中每个角色的一些自定义操作.

最后是自定义上下文生成器

This is fairly the same from as in the question only thing we need to configure is which actions require to be the 'admin' this can be changed according to your needs whatever you program a blog, library, store or whatever and need some custom actions per role on your API.

At last is the custom context builder

<?php

namespace App\Serializer;

use Symfony\Component\HttpFoundation\Request;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

final class AdminContextBuilder implements SerializerContextBuilderInterface
{
    private $decorated;
    private $authorizationChecker;

    public function __construct(SerializerContextBuilderInterface $decorated, AuthorizationCheckerInterface $authorizationChecker)
    {
        $this->decorated = $decorated;
        $this->authorizationChecker = $authorizationChecker;
    }

    public function createFromRequest(Request $request, bool $normalization, ?array $extractedAttributes = null): array
    {
        $context = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);

        if ($this->authorizationChecker->isGranted('ROLE_ADMIN')) {
            $context['groups'][] = 'admin:patch';
            $context['groups'][] = 'admin:post';
            $context['groups'][] = 'admin:get';
        }

        return $context;
    }
}

这非常简单,基本上可以根据您的个人需求进行扩展,我们会检查当前用户是否为管理员,并为他提供a,b,c等组的属性.也可以根据每个实体进行指定(有关更多信息,请参见可以在API平台文档中找到 BookContextBuilder 相当简单

我很确定这是构建一些简单甚至复杂的API时任何人都需要的东西,其中角色将决定谁可以做什么.如果此答案对您有帮助,请务必对我的答案表示感谢.非常感谢!编码愉快!

This is fairly simple and can be extended on your personal needs basically we check if the current user is an admin give him the properties from groups a, b, c etc. This can also be specified per entity(more on that you can find in API platform doc BookContextBuilder fairly simple

I am pretty sure this is the bread and butter anyone will ever need when building some simple or even complex API where roles will determine who can do what. If this answer will help you pleas be sure to up my answer thanks a lot and happy coding!

这篇关于如何根据用户角色和请求方法设置规范化组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆