nuwave/lighthouse

`@can` with `injectArgs: true` doesn't inject args passed as variables

Closed this issue · 3 comments

If you use injectArgs on @can, it doesn't pass in arguments that are populated via variables:

Model:

namespace App\Models;
use Illuminate\Database\Eloquent\Model;

class Test extends Model {
    protected $table = 'test';
    protected $fillable = ['foo']
}

Policy:

class TestPolicy
{
    public function create(User $user, array $injectedArgs): bool
    {
        return $injectedArgs['input']['foo'] === "bar";
    }
}

graphQL schema:

Input TestInput {
    foo: String!
}

type LeaveMutations {
    test(input: TestInput): TestPayload! @can(ability: "create", model: "Test", injectArgs: true)
}

graphQL Query:

mutation test ($input: TestInput) {
   input: $input
}

{ "input": { foo: "bar" } }

Result:

Internal server error
"Undefined array key "input"", file: /var/app/current/app/Policies/TestPolicy.php

When dumping out $injectedArgs, it returns an empty array, when the expected is

["input" => ["foo": "bar"]]

From my understanding, this makes it impossible to use variables and @can with injectedArgs
Lighthouse Version 6.22.0

There is a test for this, perhaps you can add a failing test case for your scenario?

public function testInjectArgsPassesClientArgumentToPolicy(): void
{
$this->be(new User());
$this->mockResolver(fn (): User => $this->resolveUser());
$this->schema = /** @lang GraphQL */ '
type Query {
user(foo: String): User!
@can(ability: "injectArgs", injectArgs: true)
@mock
}
type User {
name: String
}
';
$this->graphQL(/** @lang GraphQL */ '
{
user(foo: "bar") {
name
}
}
')->assertJson([
'data' => [
'user' => [
'name' => 'foo',
],
],
]);
}

Thanks for the quick response, that test doesn't cover the case, as it doesn't use variables to pass in the arguments.

Once I am back in the office on Monday I'll attempt to put together something.

But generally speaking, it looks something like the following, for a query using Type rather than a mutation using Input:

     public function testInjectArgsPassesClientArgumentToPolicy(): void 
     { 
         $this->be(new User()); 
  
         $this->mockResolver(fn (): User => $this->resolveUser()); 
  
         $this->schema = /** @lang GraphQL */ ' 
         type Query { 
             user(foo: String): User! 
                 @can(ability: "injectArgs", injectArgs: true) 
                 @mock 
         } 
  
         type User { 
             name: String 
         } 

         type Foo { # Here we define the type to use as a variable.
             foo: String
         }
         '; 
  
         $this->graphQL(/** @lang GraphQL */ ' 
        query getUser($foo: Foo) {  # Here we set the query operation and tell it use to our Foo type as a variable.
             user(foo: $foo) { 
                 name 
             } 
         } 
         ', ['foo' => 'bar'] # Here we pass the variables into the graphQL helper
        )->assertJson([ 
             'data' => [ 
                 'user' => [ 
                     'name' => 'foo', 
                 ], 
             ], 
         ]); 
     } 

The actual test is:

    public function testInjectArgsPassesClientVariablesToPolicy(): void
    {
        $this->be(new User());

        $this->mockResolver(fn (): User => $this->resolveUser());

        $this->schema = /** @lang GraphQL */ '
         type Query {
             user(foo: String): User!
                 @can(ability: "injectArgs", injectArgs: true)
                 @mock
         }

         type User {
             name: String
         }
         ';

        $this->graphQL(/** @lang GraphQL */ '
        query getUser($foo: String) { 
             user(foo: $foo) {
                 name
             }
         }
         ', ['foo' => 'bar']
        )->assertJson([
            'data' => [
                'user' => [
                    'name' => 'foo',
                ],
            ],
        ]);
    }

and what would you know, it passes.

OK (1 test, 2 assertions)

Because I am an idiot. and I am using @spread on the Input so the outer associate key doesn't exist.

Sorry for wasting your time.