2022-02-08 12:24:34 +03:00
|
|
|
-- | Postgres Translate Select
|
|
|
|
--
|
|
|
|
-- This module is a translation layer between IR and postgres-specific select queries.
|
2021-10-06 17:47:47 +03:00
|
|
|
--
|
|
|
|
-- There are three main types of selects (as distinguished from the IR):
|
|
|
|
--
|
|
|
|
-- * "simple" selects
|
|
|
|
--
|
|
|
|
-- * aggregate selects
|
|
|
|
--
|
|
|
|
-- * connection selects (used for relay)
|
|
|
|
--
|
|
|
|
-- Most exports from this module showcase this distinction. The "interesting" parts
|
|
|
|
-- of the call tree of these functions is similar:
|
|
|
|
--
|
|
|
|
-- * 'selectQuerySQL' -> 'mkSQLSelect' -> 'processAnnSimpleSelect' -> 'processSelectParams'/'processAnnFields'
|
|
|
|
--
|
|
|
|
-- * 'selectAggregateQuerySQL' -> 'mkAggregateSelect' -> 'processAnnAggregateSelect' -> 'processSelectParams'/'processAnnFields'
|
|
|
|
--
|
|
|
|
-- * 'connetionSelectQuerySQL' -> 'mkConnectionSelect' -> 'processConnectionSelection' -> 'processSelectParams'
|
|
|
|
--
|
|
|
|
--
|
|
|
|
-- Random thoughts that might help when diving deeper in this module:
|
|
|
|
--
|
|
|
|
-- * Extractors are a pair of an SQL expression and an alias; they get
|
|
|
|
-- translated like "[SELECT ...] <expr> as <alias>"
|
|
|
|
-- * a 'SelectSource' consists of a prefix, a source, a boolean conditional
|
|
|
|
-- expression, and info on whether sorting or slicing is done
|
|
|
|
-- (needed to handle the LIMIT optimisation)
|
|
|
|
-- * For details on creating the selection tree for relationships via
|
|
|
|
-- @MonadWriter JoinTree@, see 'withWriteJoinTree'
|
2020-10-29 19:58:13 +03:00
|
|
|
module Hasura.Backends.Postgres.Translate.Select
|
2021-09-24 01:56:37 +03:00
|
|
|
( selectQuerySQL,
|
2022-04-07 17:41:43 +03:00
|
|
|
selectStreamQuerySQL,
|
2021-09-24 01:56:37 +03:00
|
|
|
selectAggregateQuerySQL,
|
|
|
|
connectionSelectQuerySQL,
|
|
|
|
mkSQLSelect,
|
2022-04-07 17:41:43 +03:00
|
|
|
mkStreamSQLSelect,
|
2021-09-24 01:56:37 +03:00
|
|
|
mkAggregateSelect,
|
|
|
|
mkConnectionSelect,
|
|
|
|
PostgresAnnotatedFieldJSON,
|
|
|
|
)
|
|
|
|
where
|
|
|
|
|
2022-04-22 16:38:35 +03:00
|
|
|
import Hasura.Backends.Postgres.Translate.Select.Aggregate
|
|
|
|
( mkAggregateSelect,
|
|
|
|
selectAggregateQuerySQL,
|
|
|
|
)
|
|
|
|
import Hasura.Backends.Postgres.Translate.Select.AnnotatedFieldJSON
|
|
|
|
( PostgresAnnotatedFieldJSON,
|
|
|
|
)
|
|
|
|
import Hasura.Backends.Postgres.Translate.Select.Connection
|
|
|
|
( connectionSelectQuerySQL,
|
|
|
|
mkConnectionSelect,
|
|
|
|
)
|
|
|
|
import Hasura.Backends.Postgres.Translate.Select.Query
|
|
|
|
( mkSQLSelect,
|
|
|
|
selectQuerySQL,
|
|
|
|
)
|
|
|
|
import Hasura.Backends.Postgres.Translate.Select.Streaming
|
|
|
|
( mkStreamSQLSelect,
|
|
|
|
selectStreamQuerySQL,
|
2021-09-24 01:56:37 +03:00
|
|
|
)
|
2018-10-31 15:51:20 +03:00
|
|
|
|
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
|
|
|
{- Note: [SQL generation for inherited roles]
|
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
When a query is executed by an inherited role, each column may contain a predicate
|
2021-04-22 00:44:37 +03:00
|
|
|
(AnnColumnCaseBoolExp ('Postgres pgKind) SQLExp) along with it. The predicate is then
|
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
|
|
|
converted to a BoolExp, which will be used to check if the said column should
|
|
|
|
be nullified. For example,
|
|
|
|
|
|
|
|
Suppose there are two roles, role1 gives access only to the `addr` column with
|
|
|
|
row filter P1 and role2 gives access to both addr and phone column with row
|
|
|
|
filter P2. The `OR`ing of the predicates will have already been done while
|
|
|
|
the schema has been generated. The SQL generated will look like this:
|
|
|
|
|
|
|
|
select
|
|
|
|
(case when (P1 or P2) then addr else null end) as addr,
|
|
|
|
(case when P2 then phone else null end) as phone
|
|
|
|
from employee
|
|
|
|
where (P1 or P2)
|
|
|
|
|
|
|
|
-}
|