【问题标题】:API Platform GraphQL Security in RelationshipsAPI 平台 GraphQL 关系中的安全性
【发布时间】:2021-02-19 20:19:53
【问题描述】:

我的问题是通过 GraphQL 消费时实体的安全实现,查询是完美的,它们返回必要的数据,但是当生成查询并且多对一关系仅限于具有角色“ROLE_ADMIN”,即使用户具有角色“IS_AUTHENTICATED_ANONYMOUSLY”,查询也会返回数据

如何添加安全层,使受保护关系中的数据无法获取?

产品查询必须在没有任何角色的情况下查看。

附加信息

GraphQL 查询用户管理员

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

GraphQL 查询用户管理结果OK

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

GraphQL 查询产品

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

GraphQL 查询产品结果FAILED

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

实体产品配置

<?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;

实体用户管理配置

<?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;

请帮忙!!!!

【问题讨论】:

    标签: symfony security graphql entity-relationship api-platform.com


    【解决方案1】:

    security 属性允许定义执行给定查询所需的角色或权限。但是,它没有定义用户可以通过该查询访问哪个关系。为此,您可以使用dynamic serialization groups

    基本上,使用特定序列化组标记需要在响应中返回特殊角色的属性,例如@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;
        }
    }
    

    在主版本(API 平台 2.6)中,the security attribute is now also available for properties。不幸的是,此功能目前仅在 REST 子系统中可用,但我们将非常高兴地合并一个 Pull Request,将对该属性的支持添加到 GraphQL 子系统中。

    【讨论】:

      【解决方案2】:

      尝试对您的实体使用排除政策。您将需要使用 JMS 序列化程序包

      例子:

      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;
      
          ...
      }
      

      这只会在用户拥有 ROLE_ADMIN 时公开$user 属性。 在您公开数据的控制器/服务中,您应该将用户角色添加到序列化上下文中:

      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;
      

      这样您应该能够根据用户的角色排除或公开一些属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-27
        • 1970-01-01
        • 2018-01-10
        • 1970-01-01
        • 2021-11-06
        • 2021-01-18
        • 1970-01-01
        相关资源
        最近更新 更多