From d0fc91c6158addb5e4e2138c691efb558a2c34cc Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Mon, 25 Nov 2024 14:36:34 -0800 Subject: [PATCH 1/3] Bind auth scope loaders to scopes object --- .../plugin-scope-auth/src/request-cache.ts | 3 +- .../tests/__snapshots__/index.test.ts.snap | 1 + .../tests/example/builder.ts | 18 ++++-- .../tests/example/schema/index.ts | 6 ++ .../tests/field-auth-scopes.test.ts | 56 +++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/packages/plugin-scope-auth/src/request-cache.ts b/packages/plugin-scope-auth/src/request-cache.ts index c6035545d..e0000995c 100644 --- a/packages/plugin-scope-auth/src/request-cache.ts +++ b/packages/plugin-scope-auth/src/request-cache.ts @@ -159,7 +159,7 @@ export default class RequestCache { const key = this.cacheKey ? this.cacheKey(arg) : arg; if (!cache.has(key)) { - const loader = scopes[name]; + let loader = scopes[name]; if (typeof loader !== 'function') { throw new PothosValidationError( @@ -167,6 +167,7 @@ export default class RequestCache { ); } + loader = loader.bind(scopes); let result: MaybePromise; if (this.treatErrorsAsUnauthorized) { diff --git a/packages/plugin-scope-auth/tests/__snapshots__/index.test.ts.snap b/packages/plugin-scope-auth/tests/__snapshots__/index.test.ts.snap index 56f92a8aa..c1d1e42dd 100644 --- a/packages/plugin-scope-auth/tests/__snapshots__/index.test.ts.snap +++ b/packages/plugin-scope-auth/tests/__snapshots__/index.test.ts.snap @@ -169,6 +169,7 @@ type Query { forAsyncPermission: String forAsyncPermissionFn(permission: String): String forBooleanFn(result: Boolean!): String + forBoundPermission: String forSyncPermission: String forSyncPermissionFn(permission: String): String grantedFromRoot: String diff --git a/packages/plugin-scope-auth/tests/example/builder.ts b/packages/plugin-scope-auth/tests/example/builder.ts index ebd2711b7..62f8b9508 100644 --- a/packages/plugin-scope-auth/tests/example/builder.ts +++ b/packages/plugin-scope-auth/tests/example/builder.ts @@ -11,17 +11,20 @@ interface Context { count?: (name: string) => void; } +interface AuthScopes { + loggedIn: boolean; + admin: boolean; + syncPermission: string; + asyncPermission: string; + boundPermission: boolean; +} + const builder = new SchemaBuilder<{ Context: Context; Interfaces: { StringInterface: {}; }; - AuthScopes: { - loggedIn: boolean; - admin: boolean; - syncPermission: string; - asyncPermission: string; - }; + AuthScopes: AuthScopes; AuthContexts: { loggedIn: Context & { user: User; isLoggedIn: true }; admin: Context & { user: User; isAdmin: true }; @@ -59,6 +62,9 @@ const builder = new SchemaBuilder<{ return await !!context.user?.permissions.includes(perm); }, + boundPermission(this: AuthScopes) { + return this.admin; + } }; }, }, diff --git a/packages/plugin-scope-auth/tests/example/schema/index.ts b/packages/plugin-scope-auth/tests/example/schema/index.ts index cbb3e9e2e..a5f73817d 100644 --- a/packages/plugin-scope-auth/tests/example/schema/index.ts +++ b/packages/plugin-scope-auth/tests/example/schema/index.ts @@ -441,6 +441,12 @@ builder.queryType({ }, resolve: () => 'ok', }), + forBoundPermission: t.string({ + authScopes: { + boundPermission: true, + }, + resolve: () => 'ok', + }), forAll: t.string({ authScopes: { $all: { diff --git a/packages/plugin-scope-auth/tests/field-auth-scopes.test.ts b/packages/plugin-scope-auth/tests/field-auth-scopes.test.ts index ca818646f..f616fb6ca 100644 --- a/packages/plugin-scope-auth/tests/field-auth-scopes.test.ts +++ b/packages/plugin-scope-auth/tests/field-auth-scopes.test.ts @@ -198,6 +198,62 @@ describe('queries for field authScopes with', () => { `); }); + it('field scope with bound loader', async () => { + const query = gql` + query { + forBoundPermission + } + `; + + const result = await execute({ + schema: exampleSchema, + document: query, + contextValue: { + user: new User({ + 'x-user-id': '1', + 'x-roles': 'admin', + }), + }, + }); + + expect(result).toMatchInlineSnapshot(` + { + "data": { + "forBoundPermission": "ok", + }, + } + `); + }); + + it('field scope with bound loader (unauthorized)', async () => { + const query = gql` + query { + forBoundPermission + } + `; + + const result = await execute({ + schema: exampleSchema, + document: query, + contextValue: { + user: new User({ + 'x-user-id': '1', + }), + }, + }); + + expect(result).toMatchInlineSnapshot(` + { + "data": { + "forBoundPermission": null, + }, + "errors": [ + [GraphQLError: Not authorized to resolve Query.forBoundPermission], + ], + } + `); + }); + it('field with $any (sync)', async () => { const query = gql` query { From 174c7b0368ffbe2f427e5282c4a30ecf52ea32a0 Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Mon, 25 Nov 2024 16:30:16 -0800 Subject: [PATCH 2/3] Add changeset --- .changeset/nervous-shoes-grin.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nervous-shoes-grin.md diff --git a/.changeset/nervous-shoes-grin.md b/.changeset/nervous-shoes-grin.md new file mode 100644 index 000000000..30c993a45 --- /dev/null +++ b/.changeset/nervous-shoes-grin.md @@ -0,0 +1,5 @@ +--- +"@pothos/plugin-scope-auth": patch +--- + +Bind `authScopes` loaders to returned provider From 68a9e934522baf6369a0c5c3113d461be3e20cc6 Mon Sep 17 00:00:00 2001 From: Ryan Delaney Date: Mon, 25 Nov 2024 16:31:14 -0800 Subject: [PATCH 3/3] Add trailing comma --- packages/plugin-scope-auth/tests/example/builder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/plugin-scope-auth/tests/example/builder.ts b/packages/plugin-scope-auth/tests/example/builder.ts index 62f8b9508..2259d0a7e 100644 --- a/packages/plugin-scope-auth/tests/example/builder.ts +++ b/packages/plugin-scope-auth/tests/example/builder.ts @@ -64,7 +64,7 @@ const builder = new SchemaBuilder<{ }, boundPermission(this: AuthScopes) { return this.admin; - } + }, }; }, },