From 65151be053ccef12329fd531e60e1a693456c9db Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Fri, 22 Dec 2023 14:28:22 -0600 Subject: [PATCH 1/2] function returning a table type, table type must be selectable. document broken behavior --- .../function_return_row_is_selectable.out | 131 ++++++++++++++++++ .../sql/function_return_row_is_selectable.sql | 82 +++++++++++ 2 files changed, 213 insertions(+) create mode 100644 test/expected/function_return_row_is_selectable.out create mode 100644 test/sql/function_return_row_is_selectable.sql diff --git a/test/expected/function_return_row_is_selectable.out b/test/expected/function_return_row_is_selectable.out new file mode 100644 index 00000000..3f8f441f --- /dev/null +++ b/test/expected/function_return_row_is_selectable.out @@ -0,0 +1,131 @@ +begin; + create table account( + id serial primary key, + email varchar(255) not null + ); + create function returns_account() + returns account language sql stable + as $$ select id, email from account; $$; + insert into account(email) + values + ('aardvark@x.com'); + create role anon; + grant usage on schema graphql to anon; + grant select on account to anon; + savepoint a; + set local role anon; + -- Should be visible + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + __typename + } + } + $$) + ); + jsonb_pretty +------------------------------------- + { + + "data": { + + "__type": { + + "__typename": "Account"+ + } + + } + + } +(1 row) + + -- Should show an entrypoint on Query for returnAccount + select jsonb_pretty( + graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + } + } + } + } + $$) + ); + jsonb_pretty +----------------------------------------------------- + { + + "data": { + + "__schema": { + + "queryType": { + + "fields": [ + + { + + "name": "accountCollection"+ + }, + + { + + "name": "node" + + }, + + { + + "name": "returnsAccount" + + } + + ] + + } + + } + + } + + } +(1 row) + + rollback to a; + revoke select on account from anon; + set local role anon; + -- We should no longer see "Account" types after revoking access + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + __typename + } + } + $$) + ); + jsonb_pretty +------------------------ + { + + "data": { + + "__type": null+ + } + + } +(1 row) + + -- We should no longer see returnAccount since it references an unknown return type "Account" + select jsonb_pretty( + graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + } + } + } + } + $$) + ); + jsonb_pretty +-------------------------------------------------- + { + + "data": { + + "__schema": { + + "queryType": { + + "fields": [ + + { + + "name": "node" + + }, + + { + + "name": "returnsAccount"+ + } + + ] + + } + + } + + } + + } +(1 row) + +rollback; diff --git a/test/sql/function_return_row_is_selectable.sql b/test/sql/function_return_row_is_selectable.sql new file mode 100644 index 00000000..9828e35f --- /dev/null +++ b/test/sql/function_return_row_is_selectable.sql @@ -0,0 +1,82 @@ +begin; + + create table account( + id serial primary key, + email varchar(255) not null + ); + + create function returns_account() + returns account language sql stable + as $$ select id, email from account; $$; + + insert into account(email) + values + ('aardvark@x.com'); + + + create role anon; + grant usage on schema graphql to anon; + grant select on account to anon; + + savepoint a; + + set local role anon; + + -- Should be visible + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + __typename + } + } + $$) + ); + + -- Should show an entrypoint on Query for returnAccount + select jsonb_pretty( + graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + } + } + } + } + $$) + ); + + rollback to a; + + revoke select on account from anon; + set local role anon; + + -- We should no longer see "Account" types after revoking access + select jsonb_pretty( + graphql.resolve($$ + { + __type(name: "Account") { + __typename + } + } + $$) + ); + + -- We should no longer see returnAccount since it references an unknown return type "Account" + select jsonb_pretty( + graphql.resolve($$ + query IntrospectionQuery { + __schema { + queryType { + fields { + name + } + } + } + } + $$) + ); + +rollback; From 7df883865d51c96af762208bbcfb9d287b8d06dd Mon Sep 17 00:00:00 2001 From: Oliver Rice Date: Fri, 22 Dec 2023 14:30:17 -0600 Subject: [PATCH 2/2] require returned row is selectable in function --- src/graphql.rs | 12 ++++++- .../function_return_row_is_selectable.out | 31 +++++++++---------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/graphql.rs b/src/graphql.rs index f2a4e394..4c24db4f 100644 --- a/src/graphql.rs +++ b/src/graphql.rs @@ -1284,10 +1284,20 @@ fn function_fields(schema: &Arc<__Schema>, volatilities: &[FunctionVolatility]) return None; } } - gql_args.extend(connection_args); } + // If the return type is a table type, it must be selectable + if !match &return_type { + __Type::Node(table_type) => table_type.table.permissions.is_selectable, + __Type::Connection(table_type) => { + table_type.table.permissions.is_selectable + } + _ => true, + } { + return None; + } + Some(__Field { name_: schema.graphql_function_field_name(func), type_: __Type::FuncCallResponse(FuncCallResponseType { diff --git a/test/expected/function_return_row_is_selectable.out b/test/expected/function_return_row_is_selectable.out index 3f8f441f..b803fe3f 100644 --- a/test/expected/function_return_row_is_selectable.out +++ b/test/expected/function_return_row_is_selectable.out @@ -108,23 +108,20 @@ begin; } $$) ); - jsonb_pretty --------------------------------------------------- - { + - "data": { + - "__schema": { + - "queryType": { + - "fields": [ + - { + - "name": "node" + - }, + - { + - "name": "returnsAccount"+ - } + - ] + - } + - } + - } + + jsonb_pretty +---------------------------------------- + { + + "data": { + + "__schema": { + + "queryType": { + + "fields": [ + + { + + "name": "node"+ + } + + ] + + } + + } + + } + } (1 row)