diff --git a/README.md b/README.md index d9bfe1b4d..c8b807f8d 100644 --- a/README.md +++ b/README.md @@ -761,7 +761,7 @@ Expression | Description | Scope **request** | Refers to the current request. | Request **token** | Refers to the token which is currently in the security token storage. | Token **user** | Refers to the user which is currently in the security token storage. | Valid Token -**object** | Refers to the value of the field for which access is being requested. For array `object` will be each item of the array. For Relay connection `object` will be the node of each connection edges. | only available for `config.fields.*.access` with query operation type. +**object** | Refers to the value of the field for which access is being requested. For array `object` will be each item of the array. For Relay connection `object` will be the node of each connection edges. | only available for `config.fields.*.access` with query operation or mutation payload type. **value** | Resolver value | only available in resolve context **args** | Resolver args array | only available in resolve context **info** | Resolver GraphQL\Type\Definition\ResolveInfo Object | only available in resolve context diff --git a/Resolver/Config/FieldsConfigSolution.php b/Resolver/Config/FieldsConfigSolution.php index 6657270ce..bd0b281b5 100644 --- a/Resolver/Config/FieldsConfigSolution.php +++ b/Resolver/Config/FieldsConfigSolution.php @@ -187,7 +187,8 @@ private function resolveAccessAndWrapResolveCallback($expression, callable $reso $info = $values['info']; - if ($info instanceof ResolveInfo && $info->operation->operation === 'mutation') { + // operation is mutation and is mutation field + if ($info instanceof ResolveInfo && $info->operation->operation === 'mutation' && $info->parentType === $info->schema->getMutationType()) { $checkAccess = $this->checkAccessCallback($expression, $values); $result = $checkAccess(null, $values) ? call_user_func_array($resolveCallback, $args) : null; } else { diff --git a/Tests/Functional/Security/AccessTest.php b/Tests/Functional/Security/AccessTest.php index 7d6516f7b..8fc54d22d 100644 --- a/Tests/Functional/Security/AccessTest.php +++ b/Tests/Functional/Security/AccessTest.php @@ -11,6 +11,7 @@ namespace Overblog\GraphQLBundle\Tests\Functional\Security; +use Overblog\GraphQLBundle\Tests\Functional\app\Mutation\SimpleMutationWithThunkFieldsMutation; use Overblog\GraphQLBundle\Tests\Functional\TestCase; class AccessTest extends TestCase @@ -37,6 +38,15 @@ class AccessTest extends TestCase } } } +EOF; + + private $simpleMutationWithThunkQuery = <<assertResponse($this->userIsEnabledQuery, $expected, static::USER_ADMIN); } + public function testMutationAllowedUser() + { + $result = 123; + + $expected = [ + 'data' => [ + 'simpleMutationWithThunkFields' => [ + 'result' => $result, + 'clientMutationId' => 'bac', + ], + ], + ]; + + $this->assertResponse(sprintf($this->simpleMutationWithThunkQuery, $result), $expected, static::USER_ADMIN); + $this->assertTrue(SimpleMutationWithThunkFieldsMutation::hasMutate(true)); + } + + public function testMutationAllowedButNoRightsToDisplayPayload() + { + $expected = [ + 'data' => [ + 'simpleMutationWithThunkFields' => [ + 'result' => null, + 'clientMutationId' => 'bac', + ], + ], + 'errors' => [ + [ + 'message' => 'Access denied to this field.', + 'locations' => [ + [ + 'line' => 3, + 'column' => 5, + ], + ], + ], + ], + ]; + + $this->assertResponse(sprintf($this->simpleMutationWithThunkQuery, 321), $expected, static::USER_ADMIN); + $this->assertTrue(SimpleMutationWithThunkFieldsMutation::hasMutate(true)); + } + + public function testMutationNotAllowedUser() + { + $expected = [ + 'data' => [ + 'simpleMutationWithThunkFields' => null, + ], + 'errors' => [ + [ + 'message' => 'Access denied to this field.', + 'locations' => [ + [ + 'line' => 2, + 'column' => 3, + ], + ], + ], + ], + ]; + + $this->assertResponse(sprintf($this->simpleMutationWithThunkQuery, 123), $expected, static::USER_RYAN); + $this->assertFalse(SimpleMutationWithThunkFieldsMutation::hasMutate(true)); + } + private function expectedFailedUserRoles() { return [ diff --git a/Tests/Functional/app/Mutation/SimpleMutationWithThunkFieldsMutation.php b/Tests/Functional/app/Mutation/SimpleMutationWithThunkFieldsMutation.php index 9eb0b3cd8..0ea6d17b2 100644 --- a/Tests/Functional/app/Mutation/SimpleMutationWithThunkFieldsMutation.php +++ b/Tests/Functional/app/Mutation/SimpleMutationWithThunkFieldsMutation.php @@ -13,8 +13,28 @@ class SimpleMutationWithThunkFieldsMutation { + private static $hasMutate = false; + + public static function hasMutate($reset = false) + { + $hasMutate = self::$hasMutate; + + if ($reset) { + static::resetHasMutate(); + } + + return $hasMutate; + } + + public static function resetHasMutate() + { + self::$hasMutate = false; + } + public function mutate($value) { + self::$hasMutate = true; + return ['result' => $value['inputData']]; } } diff --git a/Tests/Functional/app/config/access/config.yml b/Tests/Functional/app/config/access/config.yml index fbad5760b..c83e39e07 100644 --- a/Tests/Functional/app/config/access/config.yml +++ b/Tests/Functional/app/config/access/config.yml @@ -1,12 +1,13 @@ imports: - { resource: ../config.yml } - { resource: ../connection/services.yml } + - { resource: ../mutation/services.yml } overblog_graphql: definitions: schema: - query: Query - mutation: ~ + query: RootQuery + mutation: RootMutation mappings: types: - diff --git a/Tests/Functional/app/config/access/mapping/access.types.yml b/Tests/Functional/app/config/access/mapping/access.types.yml index 824ad8a77..ab5b0988a 100644 --- a/Tests/Functional/app/config/access/mapping/access.types.yml +++ b/Tests/Functional/app/config/access/mapping/access.types.yml @@ -1,4 +1,12 @@ -Query: +RootQuery: + type: object + config: + fields: + user: + type: User + resolve: '@=resolver("query")' + +Mutation: type: object config: fields: @@ -40,3 +48,28 @@ friendConnection: totalCount: type: Int resolve: '@=resolver("connection")' + +#Mutation +RootMutation: + type: object + config: + fields: + simpleMutationWithThunkFields: + builder: Mutation + access: "@=hasRole('ROLE_ADMIN')" + builderConfig: + inputType: simpleMutationWithThunkFieldsInput + payloadType: simpleMutationWithThunkFieldsPayload + mutateAndGetPayload: "@=mutation('simple_mutation_with_thunk_fields', [value])" + +simpleMutationWithThunkFieldsInput: + type: relay-mutation-input + config: + fields: + inputData : { type: "Int" } + +simpleMutationWithThunkFieldsPayload: + type: relay-mutation-payload + config: + fields: + result: { type: "Int", access: "@=object === 123" } diff --git a/Tests/Functional/app/config/mutation/config.yml b/Tests/Functional/app/config/mutation/config.yml index 6be620378..4f1968607 100644 --- a/Tests/Functional/app/config/mutation/config.yml +++ b/Tests/Functional/app/config/mutation/config.yml @@ -1,11 +1,6 @@ imports: - { resource: ../config.yml } - -services: - overblog_graphql.test.mutation: - class: Overblog\GraphQLBundle\Tests\Functional\app\Mutation\SimpleMutationWithThunkFieldsMutation - tags: - - { name: "overblog_graphql.mutation", alias: "simple_mutation_with_thunk_fields", method: "mutate" } + - { resource: services.yml } overblog_graphql: definitions: diff --git a/Tests/Functional/app/config/mutation/services.yml b/Tests/Functional/app/config/mutation/services.yml new file mode 100644 index 000000000..ff3842eda --- /dev/null +++ b/Tests/Functional/app/config/mutation/services.yml @@ -0,0 +1,5 @@ +services: + overblog_graphql.test.mutation: + class: Overblog\GraphQLBundle\Tests\Functional\app\Mutation\SimpleMutationWithThunkFieldsMutation + tags: + - { name: "overblog_graphql.mutation", alias: "simple_mutation_with_thunk_fields", method: "mutate" } diff --git a/composer.json b/composer.json index a12d57544..df7eaf59c 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "symfony/expression-language": "^2.7|^3.0", "symfony/options-resolver": "^2.7|^3.0", "doctrine/doctrine-cache-bundle": "^1.2", - "webonyx/graphql-php": "^0.6.0", + "webonyx/graphql-php": "0.6.0", "symfony/property-access": "^2.7|^3.0" }, "suggest": {