Skip to content

Commit 3d33017

Browse files
codingkarthikhasura-bot
authored andcommitted
[RFC] disable query/subscription root fields
PR-URL: hasura/graphql-engine-mono#4449 GitOrigin-RevId: f7f4e60250b4c7375a973d0e5770ac134217767a
1 parent b369462 commit 3d33017

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
original issue: https://github.com/hasura/graphql-engine/pull/4110
2+
3+
## Allow disabling query root fields
4+
5+
Currently when a select permission is defined for a role on a table, we
6+
automatically generate 3 fields for the table (`<table>`, `<table_by_pk>`,
7+
`<table_aggregate>`) in `query_root` and likewise in `subscription_root`. This
8+
should be customisable to allow some of the patterns as discussed below.
9+
10+
### Motivation
11+
12+
#### 1. Allow selecting data only through relationships (issues: [207](https://github.com/hasura/graphql-engine/issues/207), [696](https://github.com/hasura/graphql-engine/issues/696), [3742](https://github.com/hasura/graphql-engine/issues/3742)).
13+
14+
Let's say you have a slack like application with the following schema:
15+
16+
| table | columns | relationships |
17+
|-------|---------|---------------|
18+
| workspace | id, name | members(array, to workspace_membership) |
19+
| workspace_membership | workspace_id, user_id |
20+
| channel | id, name, workspace_id | workspace(object, to workspace) |
21+
| message | id, content, user_id, channel_id | channel(object, to channel) |
22+
23+
The permissions for a `user` role would be something along these lines:
24+
25+
| table | permissions |
26+
|-------|-------------|
27+
| workspace | `{"members": {"user_id": "x-hasura-user-id"}}` |
28+
| channel | `{"workspace": {"members": {"user_id": "x-hasura-user-id"}}}` |
29+
| message | `{"channel": {"workspace": {"members": {"user_id": "x-hasura-user-id"}}}}` |
30+
31+
Now let's say we would like to introduce a new table called `message_reaction`
32+
which has columns (message_id, reaciton_name, user_id). The permission on
33+
`message_reaction` table would be as follows:
34+
35+
```json
36+
{"message": {"channel": {"workspace": {"members": {"user_id": "x-hasura-user-id"}}}}}
37+
```
38+
39+
As we go down the chain, our permissions gets more and more nested, refering to
40+
the permissions of the parent tables and beyond a point can get quite
41+
cumbersome. Let's say in our application we **never** need to access
42+
`message_reactions` table directly and is always accessed through `reactions`
43+
relationship on `message` table. Can the permission be simplified?
44+
45+
Yes! *If we can disable all of the `message_reaction` table's top level
46+
fields*, the select filter on `message_reactions` table can be simplified to
47+
`{}` and as `message` table has the correct permissions, the relationship
48+
`reactions` is restricted to what can be accessed through `message` table.
49+
50+
The pattern where certain data can only be accessible through relationships
51+
seems to be known as 'Aggregate' pattern under [Domain-Driven
52+
Design](https://martinfowler.com/bliki/DDD_Aggregate.html).
53+
54+
#### 2. As an additional access control mechanism
55+
56+
Let's say you want to allow a client to fetch data from a table only if the
57+
client knows the primary key of a row in that table. In this case regardless of
58+
the permission on the table, only `<table>_by_pk` should be exposed in
59+
`query_root`.
60+
61+
## Allow disabling subscription fields
62+
63+
Currently we do not provide a fine grained control on subscriptions that are exposed - if a select permission is defined on a table, the live queries on that table are exposed through `subscription_root`. (Note: the discussion of `query_root` customisability also applies to `subscription_root`).
64+
65+
### Proposed solution
66+
67+
Introduce optional `query_root_fields` and `subscription_root_fields` in select permission which takes a list of `field`s that should be exposed in `query_root` (where `field` is one of `select`/`select_by_pk`/`select_aggregate`) and `subscription_root` (`query_root` fields + `select_stream`) respectively. When these fields are absent, all the values are enabled. The current behaviour is for backwards compatibility.
68+
Note: The Relay field `<table>_connection` will be enabled if `select` is given in `query_root_fields` else it will be disabled.
69+
70+
### Metadata API behaviour
71+
72+
For incremental metadata API (`create_select_permission`), throw validation error when:
73+
a. A role doesn't have access to the primary key column(s) and `select_by_pk` is added.
74+
b. When `select_stream` is added when streaming subscriptions is not enabled in the graphql-engine.
75+
c. When `select_aggregate` is added without `allow_aggregations` set to `true`.
76+
77+
For `replace_metadata` API, throw validation error in the above cases when `allow_inconsistent_metadata: false` else mark invalid permissions as inconsistent objects.
78+
79+
### Future work
80+
81+
1. Extend this feature for mutations and remote schemas.

0 commit comments

Comments
 (0)