Issue
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"
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
query {
userAdmin(id: "api/user_admins/1") {
id
name
}
}
GraphQL Query User Admin Result OK
{
"errors": [
{
"message": "Sorry, but you don't have access",
"extensions": {
"category": "graphql"
}
]
}
GraphQL Query Product
query {
products {
edges {
node {
name
price
user {
name
}
}
}
}
}
GraphQL Query Product Result FAILED
{
"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 !!!!
Solution
The security
attribute allows to define roles or permissions necessary to execute a given query. 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:
<?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. 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.
Answered By - Kévin Dunglas
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.