简体   繁体   English

关系中的 API 平台 GraphQL 安全性

[英]API Platform GraphQL Security in Relationships

My problem is the security implementation of the entities when consumed through GraphQL, the queries are perfect, they return the necessary data, but the problem occurs when a query is generated and the many-to-one relationship is limited to having a role "ROLE_ADMIN", the query returns the data even when the user has the role "IS_AUTHENTICATED_ANONYMOUSLY"我的问题是实体在通过 GraphQL 消费时的安全实现,查询是完美的,它们返回必要的数据,但是当生成查询并且多对一关系仅限于具有角色“ROLE_ADMIN”时出现问题",即使用户具有“IS_AUTHENTICATED_ANONYMOUSLY”角色,查询也会返回数据

How to add the security layer so that data from protected relationships cannot be obtained?如何添加安全层,使受保护关系中的数据无法获取?

The product query must be seen without any role.必须看到没有任何作用的产品查询。

Additional Information附加信息

GraphQL Query User Admin GraphQL 查询用户管理

query {
    userAdmin(id: "api/user_admins/1") {
        id
        name
    }
}

GraphQL Query User Admin Result OK GraphQL 查询用户管理结果 OK

{
  "errors": [
    {
      "message": "Sorry, but you don't have access",
      "extensions": {
        "category": "graphql"
      }
  ]
}

GraphQL Query Product GraphQL 查询产品

query {
    products {
        edges {
            node {
                name
                price
                user {
                    name
                }
            }
        }
    }
}

GraphQL Query Product Result FAILED GraphQL 查询产品结果失败

{
  "data": {
    "products": {
      "edges": [
        {
          "node": {
            "name": "GERLACH-HAAG",
            "price": "175",
            "user": {
              "name": "Sidney Deane" /** this information should not be seen **/
            }
          }
        }
      ]
    }
  }
}

Entity Product Configuration实体产品配置

<?php

/**
 * @ApiResource(
 *     graphql={
 *          "item_query",
 *          "collection_query",
 *          "delete"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "create"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "update"={ "security" = "is_granted('ROLE_ADMIN')" }
 *     }
 * )
 * @ORM\Table(name="TBL_PRODUCTS")
 * @ORM\Entity(repositoryClass=ProductRepository::class)
 * @ORM\HasLifecycleCallbacks()
 */
class Product
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="bigint", name="ID")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, name="NAME")
     */
    private $name;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\UserAdmin")
     * @ORM\JoinColumn(name="USER", referencedColumnName="ID")
     */
    private $user;

Entity User Admin Configuration实体用户管理配置

<?php

/**
 * @ApiResource(
 *     graphql={
 *          "item_query"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "collection_query"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "delete"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "create"={ "security" = "is_granted('ROLE_ADMIN')" },
 *          "update"={ "security" = "is_granted('ROLE_ADMIN')" }
 *     }
 * )
 * @ORM\Table(name="TBL_USERS_ADMIN")
 * @ORM\Entity(repositoryClass=UserAdminRepository::class)
 * @ORM\HasLifecycleCallbacks()
 */
class UserAdmin implements UserInterface
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="bigint", name="ID")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=180, name="USERNAME")
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=180, name="PASSWORD")
     */
    private $password;

    /**
     * @ORM\Column(type="string", length=180, name="NAME")
     */
    private $name;

Please help !!!!请帮忙 !!!!

The security attribute allows to define roles or permissions necessary to execute a given query. security属性允许定义执行给定查询所需的角色或权限。 However, it doesn't define which relation available trough this query the user can access.但是,它没有定义用户可以通过该查询访问哪个关系。 To do so, you can use dynamic serialization groups .为此,您可以使用动态序列化组

Basically, mark the properties requiring a special role to be returned in the response with a specific serialization group, such as @Groups("admin") , then create a dynamic serialization context builder to add the special group if the connected user has this special role:基本上,使用特定的序列化组标记需要在响应中返回的特殊角色的属性,例如@Groups("admin") ,然后创建动态序列化上下文构建器以添加特殊组,如果连接的用户具有此特殊组角色:

<?php

namespace App\Serializer;

use ApiPlatform\Core\GraphQl\Serializer\SerializerContextBuilderInterface;
use App\Entity\Book;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;

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

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

    public function create(?string $resourceClass, string $operationName, array $resolverContext, bool $normalization): array
    {
        $context = $this->decorated->create($resourceClass, $operationName, $resolverContext, $normalization);
        $resourceClass = $context['resource_class'] ?? null;

        if ($resourceClass === Book::class && isset($context['groups']) && $this->authorizationChecker->isGranted('ROLE_ADMIN') && false === $normalization) {
            $context['groups'][] = 'admin';
        }

        return $context;
    }
}

In the master version (API Platform 2.6), the security attribute is now also available for properties .在主版本(API 平台 2.6)中, security属性现在也可用于 properties Unfortunately, this feature is currently available only in the REST subsystem, but we will be very pleased to merge a Pull Request adding support for this attribute to the GraphQL subsystem.不幸的是,此功能目前仅在 REST 子系统中可用,但我们很高兴将合并请求以将此属性添加到 GraphQL 子系统中。

Try using exclusion policies on your entities.尝试对您的实体使用排除策略。 You will need to use the JMS Serializer Bundle您将需要使用 JMS Serializer Bundle

Example:例子:

use JMS\Serializer\Annotation as Serializer;

/**
 * @ORM\Table(name="TBL_PRODUCTS")
 * @ORM\Entity(repositoryClass=ProductRepository::class)
 * @ORM\HasLifecycleCallbacks()
 * @Serializer\ExclusionPolicy("all")
 */

class Product
{
    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\UserAdmin")
     * @ORM\JoinColumn(name="USER", referencedColumnName="ID")
     * @Serializer\Groups({"ROLE_ADMIN"}) // only users with ROLE_ADMIN will see this
     * @Serializer\Expose()
     */
    private $user;
    
    /**
     * ...
     * @Serializer\Groups({"ROLE_SUPER_ADMIN"}) // only users with ROLE_SUPER_ADMIN will see this
     * @Serializer\Expose()
     */
    private $superSecretData;

    ...
}

This wil expose the $user property only when the user has the ROLE_ADMIN.仅当用户具有 ROLE_ADMIN 时才会公开$user属性。 In your controller/service where you expose your data, you should add the users role to the serialization context:在您公开数据的控制器/服务中,您应该将用户角色添加到序列化上下文中:

use JMS\Serializer\SerializerInterface;

...


        $entities = $repository->findAll();

        $responseData = $this->serializer->serialize(
            $entities,
            'json',
            SerializationContext::create()
                ->setGroups(['ROLE_USER']) // Add all roles you want
        );

        $response = new Response(
            $responseData,
            Response::HTTP_OK
        );

        $response->headers->set('Content-Type', 'application/json');

        return $response;

This way you should be able to exclude or expose some properties based on the users' role.通过这种方式,您应该能够根据用户的角色排除或公开某些属性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM