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

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

问题描述

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

Collection-get(aka./api/users) - 仅适用于管理员用户(所有属性都可用,也许我们排除了散列密码)

POST - 每个人都应该有权访问以下属性:用户名、电子邮件、普通密码(不保留以防万一)

PATCH/PUT - 这里变得非常棘手:我希望那些具有 ROLE_ADMIN 的人可以访问用户名、电子邮件、普通密码字段.而那些是只能更改普通密码的所有者

删除 - 只有 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:
    AppEntityUser:
        # 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'

这是序列化器配置

AppEntityUser:
    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 AppSerializer;

use SymfonyComponentHttpFoundationRequest;
use ApiPlatformCoreSerializerSerializerContextBuilderInterface;
use SymfonyComponentSecurityCoreAuthorizationAuthorizationCheckerInterface;

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:
    AppEntityUser:
        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

AppEntityUser:
    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 AppSerializer;

use SymfonyComponentHttpFoundationRequest;
use ApiPlatformCoreSerializerSerializerContextBuilderInterface;
use SymfonyComponentSecurityCoreAuthorizationAuthorizationCheckerInterface;

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天全站免登陆