Skip to content

Commit 25c5f22

Browse files
authored
fix(graphql): security after resolver (#6444)
fixes #6427
1 parent c086dfe commit 25c5f22

File tree

10 files changed

+140
-0
lines changed

10 files changed

+140
-0
lines changed

features/graphql/query.feature

+13
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,16 @@ Feature: GraphQL query support
664664
And the header "Content-Type" should be equal to "application/json"
665665
And the JSON node "data.dummyDifferentGraphQlSerializationGroup.name" should be equal to "Name #1"
666666
And the JSON node "data.dummyDifferentGraphQlSerializationGroup.title" should be equal to "Title #1"
667+
668+
Scenario: Call security after resolver
669+
When I send the following GraphQL request:
670+
"""
671+
{
672+
getSecurityAfterResolver(id: "/security_after_resolvers/1") {
673+
name
674+
}
675+
}
676+
"""
677+
Then the response status code should be 200
678+
And the header "Content-Type" should be equal to "application/json"
679+
And the JSON node "data.getSecurityAfterResolver.name" should be equal to "test"

src/Metadata/GraphQl/Operation.php

+28
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ public function __construct(
4141
protected ?array $extraArgs = null,
4242
protected ?array $links = null,
4343
protected ?bool $validateAfterResolver = null,
44+
protected ?string $securityAfterResolver = null,
45+
protected ?string $securityMessageAfterResolver = null,
4446

4547
?string $shortName = null,
4648
?string $class = null,
@@ -209,4 +211,30 @@ public function withValidateAfterResolver(bool $validateAfterResolver = true): s
209211

210212
return $self;
211213
}
214+
215+
public function getSecurityAfterResolver(): ?string
216+
{
217+
return $this->securityAfterResolver;
218+
}
219+
220+
public function withSecurityAfterResolver(string $securityAfterResolver): self
221+
{
222+
$self = clone $this;
223+
$self->securityAfterResolver = $securityAfterResolver;
224+
225+
return $self;
226+
}
227+
228+
public function getSecurityMessageAfterResolver(): ?string
229+
{
230+
return $this->securityMessageAfterResolver;
231+
}
232+
233+
public function withSecurityMessageAfterResolver(string $securityMessageAfterResolver): self
234+
{
235+
$self = clone $this;
236+
$self->securityMessageAfterResolver = $securityMessageAfterResolver;
237+
238+
return $self;
239+
}
212240
}

src/Metadata/GraphQl/Query.php

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public function __construct(
2424
?array $args = null,
2525
?array $extraArgs = null,
2626
?array $links = null,
27+
?string $securityAfterResolver = null,
28+
?string $securityMessageAfterResolver = null,
2729

2830
?string $shortName = null,
2931
?string $class = null,
@@ -79,6 +81,8 @@ public function __construct(
7981
args: $args,
8082
extraArgs: $extraArgs,
8183
links: $links,
84+
securityAfterResolver: $securityAfterResolver,
85+
securityMessageAfterResolver: $securityMessageAfterResolver,
8286
shortName: $shortName,
8387
class: $class,
8488
paginationEnabled: $paginationEnabled,

src/Metadata/GraphQl/QueryCollection.php

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ public function __construct(
2525
?array $args = null,
2626
?array $extraArgs = null,
2727
?array $links = null,
28+
?string $securityAfterResolver = null,
29+
?string $securityMessageAfterResolver = null,
2830

2931
?string $shortName = null,
3032
?string $class = null,
@@ -80,6 +82,8 @@ public function __construct(
8082
args: $args,
8183
extraArgs: $extraArgs,
8284
links: $links,
85+
securityAfterResolver: $securityAfterResolver,
86+
securityMessageAfterResolver: $securityMessageAfterResolver,
8387
shortName: $shortName,
8488
class: $class,
8589
paginationEnabled: $paginationEnabled,

src/Metadata/GraphQl/Subscription.php

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ public function __construct(
2424
?array $args = null,
2525
?array $extraArgs = null,
2626
?array $links = null,
27+
?string $securityAfterResolver = null,
28+
?string $securityMessageAfterResolver = null,
2729

2830
?string $shortName = null,
2931
?string $class = null,
@@ -77,6 +79,8 @@ public function __construct(
7779
args: $args,
7880
extraArgs: $extraArgs,
7981
links: $links,
82+
securityAfterResolver: $securityAfterResolver,
83+
securityMessageAfterResolver: $securityMessageAfterResolver,
8084
shortName: $shortName,
8185
class: $class,
8286
paginationEnabled: $paginationEnabled,

src/Symfony/Bundle/Resources/config/graphql/security.xml

+6
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,11 @@
1919
<argument type="service" id="api_platform.security.resource_access_checker" />
2020
<argument>post_validate</argument>
2121
</service>
22+
23+
<service id="api_platform.graphql.state_provider.access_checker.after_resolver" class="ApiPlatform\Symfony\Security\State\AccessCheckerProvider" decorates="api_platform.graphql.state_provider" decoration-priority="170">
24+
<argument type="service" id="api_platform.graphql.state_provider.access_checker.after_resolver.inner" />
25+
<argument type="service" id="api_platform.security.resource_access_checker" />
26+
<argument>after_resolver</argument>
27+
</service>
2228
</services>
2329
</container>

src/Symfony/Security/State/AccessCheckerProvider.php

+9
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace ApiPlatform\Symfony\Security\State;
1515

16+
use ApiPlatform\Exception\RuntimeException;
1617
use ApiPlatform\Metadata\GraphQl\Operation as GraphQlOperation;
1718
use ApiPlatform\Metadata\GraphQl\QueryCollection;
1819
use ApiPlatform\Metadata\HttpOperation;
@@ -45,6 +46,14 @@ public function provide(Operation $operation, array $uriVariables = [], array $c
4546
$isGranted = $operation->getSecurityPostValidation();
4647
$message = $operation->getSecurityPostValidationMessage();
4748
break;
49+
case 'after_resolver':
50+
if (!$operation instanceof GraphQlOperation) {
51+
throw new RuntimeException('Not a graphql operation');
52+
}
53+
54+
$isGranted = $operation->getSecurityAfterResolver();
55+
$message = $operation->getSecurityMessageAfterResolver();
56+
// no break
4857
default:
4958
$isGranted = $operation->getSecurity();
5059
$message = $operation->getSecurityMessage();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6427;
15+
16+
use ApiPlatform\Metadata\ApiResource;
17+
use ApiPlatform\Metadata\GraphQl\Query;
18+
19+
#[ApiResource(
20+
provider: [self::class, 'provide'],
21+
graphQlOperations: [
22+
new Query(
23+
resolver: 'app.graphql.query_resolver.security_after_resolver',
24+
securityAfterResolver: "object.name == 'test'",
25+
name: 'get'
26+
),
27+
]
28+
)]
29+
class SecurityAfterResolver
30+
{
31+
public function __construct(public ?string $id, public ?string $name)
32+
{
33+
}
34+
35+
public static function provide()
36+
{
37+
return new self('1', '1');
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6427;
15+
16+
use ApiPlatform\GraphQl\Resolver\QueryItemResolverInterface;
17+
18+
final class SecurityAfterResolverResolver implements QueryItemResolverInterface
19+
{
20+
/**
21+
* @param object|null $item
22+
* @param mixed[] $context
23+
*/
24+
public function __invoke($item, array $context): SecurityAfterResolver
25+
{
26+
return new SecurityAfterResolver('1', 'test');
27+
}
28+
}

tests/Fixtures/app/config/config_common.yml

+5
Original file line numberDiff line numberDiff line change
@@ -464,3 +464,8 @@ services:
464464
class: 'ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6354\CreateActivityLogResolver'
465465
tags:
466466
- name: 'api_platform.graphql.resolver'
467+
468+
app.graphql.query_resolver.security_after_resolver:
469+
class: 'ApiPlatform\Tests\Fixtures\TestBundle\ApiResource\Issue6427\SecurityAfterResolverResolver'
470+
tags:
471+
- name: 'api_platform.graphql.resolver'

0 commit comments

Comments
 (0)