|
| 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