diff --git a/src/Middlewares/SecurityFieldMiddleware.php b/src/Middlewares/SecurityFieldMiddleware.php index 920071c02d..d6cdea541c 100644 --- a/src/Middlewares/SecurityFieldMiddleware.php +++ b/src/Middlewares/SecurityFieldMiddleware.php @@ -83,7 +83,12 @@ public function process( $injectSource = $queryFieldDescriptor->isInjectSource(); $queryFieldDescriptor = $queryFieldDescriptor->withResolver(function (object|null $source, ...$args) use ($originalResolver, $securityAnnotations, $resolver, $failWith, $parameters, $queryFieldDescriptor, $injectSource) { - $variables = $this->getVariables($args, $parameters, $originalResolver->executionSource($source), $injectSource); + $variables = $this->getVariables( + $args, + $parameters, + $injectSource ? $source : $originalResolver->executionSource($source), + $injectSource, + ); foreach ($securityAnnotations as $annotation) { try { diff --git a/src/Middlewares/SecurityInputFieldMiddleware.php b/src/Middlewares/SecurityInputFieldMiddleware.php index 8c9a5632ce..ab1da69d5a 100644 --- a/src/Middlewares/SecurityInputFieldMiddleware.php +++ b/src/Middlewares/SecurityInputFieldMiddleware.php @@ -51,7 +51,12 @@ public function process(InputFieldDescriptor $inputFieldDescriptor, InputFieldHa $injectSource = $inputFieldDescriptor->isInjectSource(); $inputFieldDescriptor = $inputFieldDescriptor->withResolver(function (object|null $source, ...$args) use ($originalResolver, $securityAnnotations, $resolver, $parameters, $inputFieldDescriptor, $injectSource) { - $variables = $this->getVariables($args, $parameters, $originalResolver->executionSource($source), $injectSource); + $variables = $this->getVariables( + $args, + $parameters, + $injectSource ? $source : $originalResolver->executionSource($source), + $injectSource, + ); foreach ($securityAnnotations as $annotation) { try { diff --git a/tests/Fixtures/Integration/Types/ExtendedContactType.php b/tests/Fixtures/Integration/Types/ExtendedContactType.php index 10d69db74f..46ae8281dc 100644 --- a/tests/Fixtures/Integration/Types/ExtendedContactType.php +++ b/tests/Fixtures/Integration/Types/ExtendedContactType.php @@ -48,4 +48,15 @@ public function extendedSecretName(Contact $contact): string|null { return $contact->getName(); } + + /** + * Regression: in #[Security] on an #[ExtendType] field, `this` must be + * the source object, not the resolver instance. + */ + #[Field] + #[Security("user && this.getName() == 'Joe'", failWith: null)] + public function sourceAwareSecretName(Contact $contact): string|null + { + return $contact->getName(); + } } diff --git a/tests/Integration/EndToEndTest.php b/tests/Integration/EndToEndTest.php index 040f2efdca..31eba39313 100644 --- a/tests/Integration/EndToEndTest.php +++ b/tests/Integration/EndToEndTest.php @@ -1360,6 +1360,7 @@ public function testEndToEndSecurityOnExtendTypeField(): void contacts { name extendedSecretName + sourceAwareSecretName } } '; @@ -1368,6 +1369,7 @@ public function testEndToEndSecurityOnExtendTypeField(): void $data = $this->getSuccessResult($result); $this->assertSame('Joe', $data['contacts'][0]['name']); $this->assertNull($data['contacts'][0]['extendedSecretName']); + $this->assertNull($data['contacts'][0]['sourceAwareSecretName']); // Logged-in user with bar=42 → the Security expression passes and the field returns the name. $container = $this->createContainer([ @@ -1394,6 +1396,7 @@ public function getUser(): object|null $result = GraphQL::executeQuery($schema, $queryString); $data = $this->getSuccessResult($result); $this->assertSame('Joe', $data['contacts'][0]['extendedSecretName']); + $this->assertSame('Joe', $data['contacts'][0]['sourceAwareSecretName']); } public function testEndToEndUnions(): void