2021-01-07 12:04:22 +03:00
|
|
|
-- | The RQL metadata query ('/v1/metadata')
|
2021-07-07 04:43:42 +03:00
|
|
|
|
|
|
|
{-# LANGUAGE ViewPatterns #-}
|
|
|
|
|
2021-01-07 12:04:22 +03:00
|
|
|
module Hasura.Server.API.Metadata where
|
|
|
|
|
2021-01-09 02:09:15 +03:00
|
|
|
import Hasura.Prelude
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
import qualified Data.Aeson.Types as A
|
2021-05-05 15:25:27 +03:00
|
|
|
import qualified Data.Environment as Env
|
2021-07-07 04:43:42 +03:00
|
|
|
import qualified Data.Text as T
|
|
|
|
import qualified Data.Text.Extended as T
|
2021-05-05 15:25:27 +03:00
|
|
|
import qualified Network.HTTP.Client.Extended as HTTP
|
2021-01-09 02:09:15 +03:00
|
|
|
|
2021-05-05 15:25:27 +03:00
|
|
|
import Control.Monad.Trans.Control (MonadBaseControl)
|
2021-01-07 12:04:22 +03:00
|
|
|
import Control.Monad.Unique
|
|
|
|
import Data.Aeson
|
|
|
|
import Data.Aeson.Casing
|
|
|
|
|
2021-05-05 15:25:27 +03:00
|
|
|
import qualified Hasura.Tracing as Tracing
|
2021-01-07 12:04:22 +03:00
|
|
|
|
2021-05-11 18:18:31 +03:00
|
|
|
import Hasura.Base.Error
|
2021-01-07 12:04:22 +03:00
|
|
|
import Hasura.EncJSON
|
|
|
|
import Hasura.Metadata.Class
|
|
|
|
import Hasura.RQL.DDL.Action
|
2021-02-11 20:54:25 +03:00
|
|
|
import Hasura.RQL.DDL.ApiLimit
|
2021-01-07 12:04:22 +03:00
|
|
|
import Hasura.RQL.DDL.ComputedField
|
|
|
|
import Hasura.RQL.DDL.CustomTypes
|
2021-01-29 04:02:34 +03:00
|
|
|
import Hasura.RQL.DDL.Endpoint
|
2021-01-07 12:04:22 +03:00
|
|
|
import Hasura.RQL.DDL.EventTrigger
|
2021-05-05 15:25:27 +03:00
|
|
|
import Hasura.RQL.DDL.GraphqlSchemaIntrospection
|
[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
|
|
|
import Hasura.RQL.DDL.InheritedRoles
|
2021-01-07 12:04:22 +03:00
|
|
|
import Hasura.RQL.DDL.Metadata
|
|
|
|
import Hasura.RQL.DDL.Permission
|
|
|
|
import Hasura.RQL.DDL.QueryCollection
|
|
|
|
import Hasura.RQL.DDL.Relationship
|
|
|
|
import Hasura.RQL.DDL.Relationship.Rename
|
|
|
|
import Hasura.RQL.DDL.RemoteRelationship
|
|
|
|
import Hasura.RQL.DDL.RemoteSchema
|
|
|
|
import Hasura.RQL.DDL.ScheduledTrigger
|
|
|
|
import Hasura.RQL.DDL.Schema
|
|
|
|
import Hasura.RQL.DDL.Schema.Source
|
|
|
|
import Hasura.RQL.Types
|
|
|
|
import Hasura.RQL.Types.Run
|
2021-07-07 04:43:42 +03:00
|
|
|
import Hasura.SQL.AnyBackend
|
|
|
|
import Hasura.SQL.Tag
|
|
|
|
import Hasura.Server.API.Backend
|
|
|
|
import Hasura.Server.API.Instances ()
|
2021-05-05 15:25:27 +03:00
|
|
|
import Hasura.Server.Types (InstanceId (..), MaintenanceMode (..))
|
|
|
|
import Hasura.Server.Utils (APIVersion (..))
|
|
|
|
import Hasura.Server.Version (HasVersion)
|
2021-01-07 12:04:22 +03:00
|
|
|
import Hasura.Session
|
|
|
|
|
|
|
|
|
2021-02-16 11:08:19 +03:00
|
|
|
data RQLMetadataV1
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Sources
|
|
|
|
= RMAddSource !(AnyBackend AddSource)
|
|
|
|
| RMDropSource DropSource
|
|
|
|
| RMRenameSource !RenameSource
|
|
|
|
|
|
|
|
-- Tables
|
|
|
|
| RMTrackTable !(AnyBackend TrackTableV2)
|
|
|
|
| RMUntrackTable !(AnyBackend UntrackTable)
|
|
|
|
| RMSetTableCustomization !(AnyBackend SetTableCustomization)
|
|
|
|
|
|
|
|
-- Tables (PG-specific)
|
|
|
|
| RMPgSetTableIsEnum !SetTableIsEnum
|
|
|
|
|
|
|
|
-- Tables permissions
|
|
|
|
| RMCreateInsertPermission !(AnyBackend (CreatePerm InsPerm))
|
|
|
|
| RMCreateSelectPermission !(AnyBackend (CreatePerm SelPerm))
|
|
|
|
| RMCreateUpdatePermission !(AnyBackend (CreatePerm UpdPerm))
|
|
|
|
| RMCreateDeletePermission !(AnyBackend (CreatePerm DelPerm))
|
|
|
|
| RMDropInsertPermission !(AnyBackend (DropPerm InsPerm))
|
|
|
|
| RMDropSelectPermission !(AnyBackend (DropPerm SelPerm))
|
|
|
|
| RMDropUpdatePermission !(AnyBackend (DropPerm UpdPerm))
|
|
|
|
| RMDropDeletePermission !(AnyBackend (DropPerm DelPerm))
|
|
|
|
| RMSetPermissionComment !(AnyBackend SetPermComment)
|
|
|
|
|
|
|
|
-- Tables relationships
|
|
|
|
| RMCreateObjectRelationship !(AnyBackend CreateObjRel)
|
|
|
|
| RMCreateArrayRelationship !(AnyBackend CreateArrRel)
|
|
|
|
| RMDropRelationship !(AnyBackend DropRel)
|
|
|
|
| RMSetRelationshipComment !(AnyBackend SetRelComment)
|
|
|
|
| RMRenameRelationship !(AnyBackend RenameRel)
|
|
|
|
|
|
|
|
-- Tables remote relationships
|
|
|
|
| RMCreateRemoteRelationship !(AnyBackend RemoteRelationship)
|
|
|
|
| RMUpdateRemoteRelationship !(AnyBackend RemoteRelationship)
|
|
|
|
| RMDeleteRemoteRelationship !(DeleteRemoteRelationship ('Postgres 'Vanilla))
|
|
|
|
|
|
|
|
-- Functions
|
|
|
|
| RMTrackFunction !(AnyBackend TrackFunctionV2)
|
|
|
|
| RMUntrackFunction !(AnyBackend UnTrackFunction)
|
|
|
|
|
|
|
|
-- Functions permissions
|
|
|
|
| RMCreateFunctionPermission !(AnyBackend CreateFunctionPermission)
|
|
|
|
| RMDropFunctionPermission !(AnyBackend DropFunctionPermission)
|
|
|
|
|
|
|
|
-- Computed fields (PG-specific)
|
|
|
|
| RMAddComputedField !(AddComputedField ('Postgres 'Vanilla))
|
|
|
|
| RMDropComputedField !(DropComputedField ('Postgres 'Vanilla))
|
|
|
|
|
|
|
|
-- Tables event triggers (PG-specific)
|
2021-04-22 00:44:37 +03:00
|
|
|
| RMPgCreateEventTrigger !(CreateEventTriggerQuery ('Postgres 'Vanilla))
|
|
|
|
| RMPgDeleteEventTrigger !(DeleteEventTriggerQuery ('Postgres 'Vanilla))
|
|
|
|
| RMPgRedeliverEvent !(RedeliverEventQuery ('Postgres 'Vanilla))
|
|
|
|
| RMPgInvokeEventTrigger !(InvokeEventTriggerQuery ('Postgres 'Vanilla))
|
2021-01-07 12:04:22 +03:00
|
|
|
|
|
|
|
-- Remote schemas
|
2021-07-07 04:43:42 +03:00
|
|
|
| RMAddRemoteSchema !AddRemoteSchemaQuery
|
|
|
|
| RMUpdateRemoteSchema !AddRemoteSchemaQuery
|
|
|
|
| RMRemoveRemoteSchema !RemoteSchemaNameQuery
|
|
|
|
| RMReloadRemoteSchema !RemoteSchemaNameQuery
|
2021-01-07 12:04:22 +03:00
|
|
|
| RMIntrospectRemoteSchema !RemoteSchemaNameQuery
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Remote schemas permissions
|
|
|
|
| RMAddRemoteSchemaPermissions !AddRemoteSchemaPermissions
|
2021-01-18 13:38:34 +03:00
|
|
|
| RMDropRemoteSchemaPermissions !DropRemoteSchemaPermissions
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Scheduled triggers
|
|
|
|
| RMCreateCronTrigger !CreateCronTrigger
|
|
|
|
| RMDeleteCronTrigger !ScheduledTriggerName
|
2021-01-07 12:04:22 +03:00
|
|
|
| RMCreateScheduledEvent !CreateScheduledEvent
|
|
|
|
| RMDeleteScheduledEvent !DeleteScheduledEvent
|
2021-07-07 04:43:42 +03:00
|
|
|
| RMGetScheduledEvents !GetScheduledEvents
|
|
|
|
| RMGetEventInvocations !GetEventInvocations
|
2021-01-07 12:04:22 +03:00
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Actions
|
|
|
|
| RMCreateAction !CreateAction
|
|
|
|
| RMDropAction !DropAction
|
|
|
|
| RMUpdateAction !UpdateAction
|
2021-01-07 12:04:22 +03:00
|
|
|
| RMCreateActionPermission !CreateActionPermission
|
2021-07-07 04:43:42 +03:00
|
|
|
| RMDropActionPermission !DropActionPermission
|
|
|
|
|
|
|
|
-- Query collections, allow list related
|
|
|
|
| RMCreateQueryCollection !CreateCollection
|
|
|
|
| RMDropQueryCollection !DropCollection
|
|
|
|
| RMAddQueryToCollection !AddQueryToCollection
|
|
|
|
| RMDropQueryFromCollection !DropQueryFromCollection
|
|
|
|
| RMAddCollectionToAllowlist !CollectionReq
|
|
|
|
| RMDropCollectionFromAllowlist !CollectionReq
|
2021-01-07 12:04:22 +03:00
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Rest endpoints
|
2021-01-29 04:02:34 +03:00
|
|
|
| RMCreateRestEndpoint !CreateEndpoint
|
2021-07-07 04:43:42 +03:00
|
|
|
| RMDropRestEndpoint !DropEndpoint
|
2021-01-29 04:02:34 +03:00
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Custom types
|
2021-01-07 12:04:22 +03:00
|
|
|
| RMSetCustomTypes !CustomTypes
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Api limits
|
2021-02-11 20:54:25 +03:00
|
|
|
| RMSetApiLimits !ApiLimit
|
|
|
|
| RMRemoveApiLimits
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Metrics config
|
2021-02-11 20:54:25 +03:00
|
|
|
| RMSetMetricsConfig !MetricsConfig
|
|
|
|
| RMRemoveMetricsConfig
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Inherited roles
|
|
|
|
| RMAddInheritedRole !AddInheritedRole
|
[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
|
|
|
| RMDropInheritedRole !DropInheritedRole
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Metadata management
|
|
|
|
| RMReplaceMetadata !ReplaceMetadata
|
|
|
|
| RMExportMetadata !ExportMetadata
|
|
|
|
| RMClearMetadata !ClearMetadata
|
|
|
|
| RMReloadMetadata !ReloadMetadata
|
|
|
|
| RMGetInconsistentMetadata !GetInconsistentMetadata
|
|
|
|
| RMDropInconsistentMetadata !DropInconsistentMetadata
|
|
|
|
|
|
|
|
-- Introspection options
|
2021-05-05 15:25:27 +03:00
|
|
|
| RMSetGraphqlSchemaIntrospectionOptions !SetGraphqlIntrospectionOptions
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
-- Debug
|
|
|
|
| RMDumpInternalState !DumpInternalState
|
|
|
|
| RMGetCatalogState !GetCatalogState
|
|
|
|
| RMSetCatalogState !SetCatalogState
|
|
|
|
|
|
|
|
-- Bulk metadata queries
|
2021-02-19 05:39:30 +03:00
|
|
|
| RMBulk [RQLMetadataRequest]
|
2021-02-14 09:07:52 +03:00
|
|
|
deriving (Eq)
|
2021-01-07 12:04:22 +03:00
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
instance FromJSON RQLMetadataV1 where
|
|
|
|
parseJSON = withObject "RQLMetadataV1" \o -> do
|
|
|
|
queryType <- o .: "type"
|
|
|
|
let
|
|
|
|
args :: forall a. FromJSON a => A.Parser a
|
|
|
|
args = o .: "args"
|
|
|
|
case queryType of
|
|
|
|
-- backend agnostic
|
|
|
|
"rename_source" -> RMRenameSource <$> args
|
|
|
|
|
|
|
|
"add_remote_schema" -> RMAddRemoteSchema <$> args
|
|
|
|
"update_remote_schema" -> RMUpdateRemoteSchema <$> args
|
|
|
|
"remove_remote_schema" -> RMRemoveRemoteSchema <$> args
|
|
|
|
"reload_remote_schema" -> RMReloadRemoteSchema <$> args
|
|
|
|
"introspect_remote_schema" -> RMIntrospectRemoteSchema <$> args
|
|
|
|
|
|
|
|
"add_remote_schema_permissions" -> RMAddRemoteSchemaPermissions <$> args
|
|
|
|
"drop_remote_schema_permissions" -> RMDropRemoteSchemaPermissions <$> args
|
|
|
|
|
|
|
|
"create_cron_trigger" -> RMCreateCronTrigger <$> args
|
|
|
|
"delete_cron_trigger" -> RMDeleteCronTrigger <$> args
|
|
|
|
"create_scheduled_event" -> RMCreateScheduledEvent <$> args
|
|
|
|
"delete_scheduled_event" -> RMDeleteScheduledEvent <$> args
|
|
|
|
"get_scheduled_events" -> RMGetScheduledEvents <$> args
|
|
|
|
"get_event_invocations" -> RMGetEventInvocations <$> args
|
|
|
|
|
|
|
|
"create_action" -> RMCreateAction <$> args
|
|
|
|
"drop_action" -> RMDropAction <$> args
|
|
|
|
"update_action" -> RMUpdateAction <$> args
|
|
|
|
"create_action_permission" -> RMCreateActionPermission <$> args
|
|
|
|
"drop_action_permission" -> RMDropActionPermission <$> args
|
|
|
|
|
|
|
|
"create_query_collection" -> RMCreateQueryCollection <$> args
|
|
|
|
"drop_query_collection" -> RMDropQueryCollection <$> args
|
|
|
|
"add_query_to_collection" -> RMAddQueryToCollection <$> args
|
|
|
|
"drop_query_from_collection" -> RMDropQueryFromCollection <$> args
|
|
|
|
"add_collection_to_allowlist" -> RMAddCollectionToAllowlist <$> args
|
|
|
|
"drop_collection_from_allowlist" -> RMDropCollectionFromAllowlist <$> args
|
|
|
|
|
|
|
|
"create_rest_endpoint" -> RMCreateRestEndpoint <$> args
|
|
|
|
"drop_rest_endpoint" -> RMDropRestEndpoint <$> args
|
|
|
|
|
|
|
|
"set_custom_types" -> RMSetCustomTypes <$> args
|
|
|
|
|
|
|
|
"set_api_limits" -> RMSetApiLimits <$> args
|
|
|
|
"remove_api_limits" -> pure RMRemoveApiLimits
|
|
|
|
"set_metrics_config" -> RMSetMetricsConfig <$> args
|
|
|
|
"remove_metrics_config" -> pure RMRemoveMetricsConfig
|
|
|
|
"add_inherited_role" -> RMAddInheritedRole <$> args
|
|
|
|
"drop_inherited_role" -> RMDropInheritedRole <$> args
|
|
|
|
|
|
|
|
"replace_metadata" -> RMReplaceMetadata <$> args
|
|
|
|
"export_metadata" -> RMExportMetadata <$> args
|
|
|
|
"clear_metadata" -> RMClearMetadata <$> args
|
|
|
|
"reload_metadata" -> RMReloadMetadata <$> args
|
|
|
|
"get_inconsistent_metadata" -> RMGetInconsistentMetadata <$> args
|
|
|
|
"drop_inconsistent_metadata" -> RMDropInconsistentMetadata <$> args
|
|
|
|
|
|
|
|
"dump_internal_state" -> RMDumpInternalState <$> args
|
|
|
|
"get_catalog_state" -> RMGetCatalogState <$> args
|
|
|
|
"set_catalog_state" -> RMSetCatalogState <$> args
|
|
|
|
|
|
|
|
"set_graphql_schema_introspection_options" -> RMSetGraphqlSchemaIntrospectionOptions <$> args
|
|
|
|
|
|
|
|
"bulk" -> RMBulk <$> args
|
|
|
|
|
|
|
|
-- backend specific
|
|
|
|
_ -> do
|
|
|
|
let (prefix, T.drop 1 -> cmd) = T.breakOn "_" queryType
|
|
|
|
backendType <- runAesonParser parseJSON (String prefix)
|
|
|
|
`onLeft` \_ -> fail (
|
|
|
|
"unknown metadata command \"" <> T.unpack queryType <>
|
|
|
|
"\"; \"" <> T.unpack prefix <> "\" was not recognized as a valid backend name"
|
|
|
|
)
|
|
|
|
dispatchAnyBackend @BackendAPI (liftTag backendType) \(_ :: BackendTag b) -> do
|
|
|
|
argValue <- args
|
|
|
|
command <- choice <$> sequenceA [p cmd argValue | p <- metadataV1CommandParsers @b]
|
|
|
|
onNothing command $ fail $
|
|
|
|
"unknown metadata command \"" <> T.unpack cmd <>
|
|
|
|
"\" for backend " <> T.unpack (T.toTxt backendType)
|
|
|
|
|
|
|
|
|
2021-02-16 11:08:19 +03:00
|
|
|
data RQLMetadataV2
|
|
|
|
= RMV2ReplaceMetadata !ReplaceMetadataV2
|
2021-07-07 04:43:42 +03:00
|
|
|
| RMV2ExportMetadata !ExportMetadata
|
|
|
|
deriving (Eq, Generic)
|
|
|
|
|
|
|
|
instance FromJSON RQLMetadataV2 where
|
|
|
|
parseJSON = genericParseJSON $
|
|
|
|
defaultOptions { constructorTagModifier = snakeCase . drop 4
|
|
|
|
, sumEncoding = TaggedObject "type" "args"
|
|
|
|
}
|
|
|
|
|
2021-02-16 11:08:19 +03:00
|
|
|
|
2021-02-19 05:39:30 +03:00
|
|
|
data RQLMetadataRequest
|
2021-02-16 11:08:19 +03:00
|
|
|
= RMV1 !RQLMetadataV1
|
|
|
|
| RMV2 !RQLMetadataV2
|
|
|
|
deriving (Eq)
|
|
|
|
|
2021-02-19 05:39:30 +03:00
|
|
|
instance FromJSON RQLMetadataRequest where
|
|
|
|
parseJSON = withObject "RQLMetadataRequest" $ \o -> do
|
2021-02-16 11:08:19 +03:00
|
|
|
version <- o .:? "version" .!= VIVersion1
|
|
|
|
let val = Object o
|
|
|
|
case version of
|
|
|
|
VIVersion1 -> RMV1 <$> parseJSON val
|
|
|
|
VIVersion2 -> RMV2 <$> parseJSON val
|
|
|
|
|
2021-02-19 05:39:30 +03:00
|
|
|
|
|
|
|
data RQLMetadata
|
|
|
|
= RQLMetadata
|
|
|
|
{ _rqlMetadataResourceVersion :: !(Maybe MetadataResourceVersion)
|
|
|
|
, _rqlMetadata :: !RQLMetadataRequest
|
|
|
|
} deriving (Eq)
|
|
|
|
|
|
|
|
instance FromJSON RQLMetadata where
|
|
|
|
parseJSON = withObject "RQLMetadata" $ \o -> do
|
|
|
|
_rqlMetadataResourceVersion <- o .:? "resource_version"
|
|
|
|
_rqlMetadata <- parseJSON $ Object o
|
|
|
|
pure RQLMetadata{..}
|
|
|
|
|
2021-01-07 12:04:22 +03:00
|
|
|
|
|
|
|
runMetadataQuery
|
|
|
|
:: ( HasVersion
|
|
|
|
, MonadIO m
|
|
|
|
, MonadBaseControl IO m
|
|
|
|
, Tracing.MonadTrace m
|
|
|
|
, MonadMetadataStorage m
|
|
|
|
, MonadResolveSource m
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
|
|
|
-> InstanceId
|
|
|
|
-> UserInfo
|
|
|
|
-> HTTP.Manager
|
2021-01-29 08:48:17 +03:00
|
|
|
-> ServerConfigCtx
|
2021-01-07 12:04:22 +03:00
|
|
|
-> RebuildableSchemaCache
|
|
|
|
-> RQLMetadata
|
|
|
|
-> m (EncJSON, RebuildableSchemaCache)
|
2021-02-19 05:39:30 +03:00
|
|
|
runMetadataQuery env instanceId userInfo httpManager serverConfigCtx schemaCache RQLMetadata{..} = do
|
|
|
|
(metadata, currentResourceVersion) <- fetchMetadata
|
2021-01-07 12:04:22 +03:00
|
|
|
((r, modMetadata), modSchemaCache, cacheInvalidations) <-
|
2021-02-19 05:39:30 +03:00
|
|
|
runMetadataQueryM env currentResourceVersion _rqlMetadata
|
2021-01-07 12:04:22 +03:00
|
|
|
& runMetadataT metadata
|
|
|
|
& runCacheRWT schemaCache
|
2021-01-29 08:48:17 +03:00
|
|
|
& peelRun (RunCtx userInfo httpManager serverConfigCtx)
|
2021-01-07 12:04:22 +03:00
|
|
|
& runExceptT
|
|
|
|
& liftEitherM
|
|
|
|
-- set modified metadata in storage
|
2021-05-21 05:46:58 +03:00
|
|
|
if queryModifiesMetadata _rqlMetadata
|
2021-04-06 06:25:02 +03:00
|
|
|
then
|
2021-05-21 05:46:58 +03:00
|
|
|
case _sccMaintenanceMode serverConfigCtx of
|
2021-04-06 06:25:02 +03:00
|
|
|
MaintenanceModeDisabled -> do
|
|
|
|
-- set modified metadata in storage
|
|
|
|
newResourceVersion <- setMetadata (fromMaybe currentResourceVersion _rqlMetadataResourceVersion) modMetadata
|
|
|
|
-- notify schema cache sync
|
|
|
|
notifySchemaCacheSync newResourceVersion instanceId cacheInvalidations
|
|
|
|
(_, modSchemaCache', _) <- setMetadataResourceVersionInSchemaCache newResourceVersion
|
|
|
|
& runCacheRWT modSchemaCache
|
|
|
|
& peelRun (RunCtx userInfo httpManager serverConfigCtx)
|
|
|
|
& runExceptT
|
|
|
|
& liftEitherM
|
|
|
|
pure (r, modSchemaCache')
|
|
|
|
MaintenanceModeEnabled ->
|
|
|
|
throw500 "metadata cannot be modified in maintenance mode"
|
|
|
|
else
|
|
|
|
pure (r, modSchemaCache)
|
2021-01-07 12:04:22 +03:00
|
|
|
|
2021-02-19 05:39:30 +03:00
|
|
|
queryModifiesMetadata :: RQLMetadataRequest -> Bool
|
2021-02-18 19:46:14 +03:00
|
|
|
queryModifiesMetadata = \case
|
|
|
|
RMV1 q ->
|
|
|
|
case q of
|
2021-04-27 07:22:32 +03:00
|
|
|
RMPgRedeliverEvent _ -> False
|
|
|
|
RMPgInvokeEventTrigger _ -> False
|
|
|
|
RMGetInconsistentMetadata _ -> False
|
|
|
|
RMIntrospectRemoteSchema _ -> False
|
|
|
|
RMDumpInternalState _ -> False
|
|
|
|
RMSetCatalogState _ -> False
|
|
|
|
RMGetCatalogState _ -> False
|
|
|
|
RMExportMetadata _ -> False
|
|
|
|
RMGetEventInvocations _ -> False
|
|
|
|
RMGetScheduledEvents _ -> False
|
|
|
|
RMCreateScheduledEvent _ -> False
|
|
|
|
RMDeleteScheduledEvent _ -> False
|
|
|
|
RMBulk qs -> any queryModifiesMetadata qs
|
|
|
|
_ -> True
|
2021-02-19 05:39:30 +03:00
|
|
|
RMV2 q ->
|
|
|
|
case q of
|
2021-04-27 07:22:32 +03:00
|
|
|
RMV2ExportMetadata _ -> False
|
|
|
|
_ -> True
|
2021-02-18 19:46:14 +03:00
|
|
|
|
2021-01-07 12:04:22 +03:00
|
|
|
runMetadataQueryM
|
|
|
|
:: ( HasVersion
|
|
|
|
, MonadIO m
|
|
|
|
, MonadBaseControl IO m
|
|
|
|
, CacheRWM m
|
|
|
|
, Tracing.MonadTrace m
|
|
|
|
, UserInfoM m
|
|
|
|
, MonadUnique m
|
2021-01-09 02:09:15 +03:00
|
|
|
, HTTP.HasHttpManagerM m
|
2021-01-07 12:04:22 +03:00
|
|
|
, MetadataM m
|
|
|
|
, MonadMetadataStorageQueryAPI m
|
2021-01-29 08:48:17 +03:00
|
|
|
, HasServerConfigCtx m
|
2021-01-07 12:04:22 +03:00
|
|
|
)
|
|
|
|
=> Env.Environment
|
2021-02-19 05:39:30 +03:00
|
|
|
-> MetadataResourceVersion
|
|
|
|
-> RQLMetadataRequest
|
2021-01-07 12:04:22 +03:00
|
|
|
-> m EncJSON
|
2021-02-19 05:39:30 +03:00
|
|
|
runMetadataQueryM env currentResourceVersion = withPathK "args" . \case
|
|
|
|
RMV1 q -> runMetadataQueryV1M env currentResourceVersion q
|
|
|
|
RMV2 q -> runMetadataQueryV2M currentResourceVersion q
|
2021-02-16 11:08:19 +03:00
|
|
|
|
|
|
|
runMetadataQueryV1M
|
2021-07-07 04:43:42 +03:00
|
|
|
:: forall m
|
|
|
|
. ( HasVersion
|
2021-02-16 11:08:19 +03:00
|
|
|
, MonadIO m
|
|
|
|
, MonadBaseControl IO m
|
|
|
|
, CacheRWM m
|
|
|
|
, Tracing.MonadTrace m
|
|
|
|
, UserInfoM m
|
|
|
|
, MonadUnique m
|
|
|
|
, HTTP.HasHttpManagerM m
|
|
|
|
, MetadataM m
|
|
|
|
, MonadMetadataStorageQueryAPI m
|
|
|
|
, HasServerConfigCtx m
|
|
|
|
)
|
|
|
|
=> Env.Environment
|
2021-02-19 05:39:30 +03:00
|
|
|
-> MetadataResourceVersion
|
2021-02-16 11:08:19 +03:00
|
|
|
-> RQLMetadataV1
|
|
|
|
-> m EncJSON
|
2021-02-19 05:39:30 +03:00
|
|
|
runMetadataQueryV1M env currentResourceVersion = \case
|
2021-07-07 04:43:42 +03:00
|
|
|
RMAddSource q -> dispatch runAddSource q
|
|
|
|
RMDropSource q -> runDropSource q
|
|
|
|
RMRenameSource q -> runRenameSource q
|
|
|
|
|
|
|
|
RMTrackTable q -> dispatch runTrackTableV2Q q
|
|
|
|
RMUntrackTable q -> dispatch runUntrackTableQ q
|
|
|
|
RMSetTableCustomization q -> dispatch runSetTableCustomization q
|
|
|
|
|
|
|
|
RMPgSetTableIsEnum q -> runSetExistingTableIsEnumQ q
|
|
|
|
|
|
|
|
RMCreateInsertPermission q -> dispatch runCreatePerm q
|
|
|
|
RMCreateSelectPermission q -> dispatch runCreatePerm q
|
|
|
|
RMCreateUpdatePermission q -> dispatch runCreatePerm q
|
|
|
|
RMCreateDeletePermission q -> dispatch runCreatePerm q
|
|
|
|
RMDropInsertPermission q -> dispatch runDropPerm q
|
|
|
|
RMDropSelectPermission q -> dispatch runDropPerm q
|
|
|
|
RMDropUpdatePermission q -> dispatch runDropPerm q
|
|
|
|
RMDropDeletePermission q -> dispatch runDropPerm q
|
|
|
|
RMSetPermissionComment q -> dispatch runSetPermComment q
|
|
|
|
|
|
|
|
RMCreateObjectRelationship q -> dispatch (runCreateRelationship ObjRel . unCreateObjRel) q
|
|
|
|
RMCreateArrayRelationship q -> dispatch (runCreateRelationship ArrRel . unCreateArrRel) q
|
|
|
|
RMDropRelationship q -> dispatch runDropRel q
|
|
|
|
RMSetRelationshipComment q -> dispatch runSetRelComment q
|
|
|
|
RMRenameRelationship q -> dispatch runRenameRel q
|
|
|
|
|
|
|
|
RMCreateRemoteRelationship q -> dispatch runCreateRemoteRelationship q
|
|
|
|
RMUpdateRemoteRelationship q -> dispatch runUpdateRemoteRelationship q
|
|
|
|
RMDeleteRemoteRelationship q -> runDeleteRemoteRelationship q
|
|
|
|
|
|
|
|
RMTrackFunction q -> dispatch runTrackFunctionV2 q
|
|
|
|
RMUntrackFunction q -> dispatch runUntrackFunc q
|
|
|
|
|
|
|
|
RMCreateFunctionPermission q -> dispatch runCreateFunctionPermission q
|
|
|
|
RMDropFunctionPermission q -> dispatch runDropFunctionPermission q
|
|
|
|
|
|
|
|
RMAddComputedField q -> runAddComputedField q
|
|
|
|
RMDropComputedField q -> runDropComputedField q
|
|
|
|
|
|
|
|
RMPgCreateEventTrigger q -> runCreateEventTriggerQuery q
|
|
|
|
RMPgDeleteEventTrigger q -> runDeleteEventTriggerQuery q
|
|
|
|
RMPgRedeliverEvent q -> runRedeliverEvent q
|
|
|
|
RMPgInvokeEventTrigger q -> runInvokeEventTrigger q
|
|
|
|
|
|
|
|
RMAddRemoteSchema q -> runAddRemoteSchema env q
|
|
|
|
RMUpdateRemoteSchema q -> runUpdateRemoteSchema env q
|
|
|
|
RMRemoveRemoteSchema q -> runRemoveRemoteSchema q
|
|
|
|
RMReloadRemoteSchema q -> runReloadRemoteSchema q
|
|
|
|
RMIntrospectRemoteSchema q -> runIntrospectRemoteSchema q
|
|
|
|
|
|
|
|
RMAddRemoteSchemaPermissions q -> runAddRemoteSchemaPermissions q
|
|
|
|
RMDropRemoteSchemaPermissions q -> runDropRemoteSchemaPermissions q
|
|
|
|
|
|
|
|
RMCreateCronTrigger q -> runCreateCronTrigger q
|
|
|
|
RMDeleteCronTrigger q -> runDeleteCronTrigger q
|
|
|
|
RMCreateScheduledEvent q -> runCreateScheduledEvent q
|
|
|
|
RMDeleteScheduledEvent q -> runDeleteScheduledEvent q
|
|
|
|
RMGetScheduledEvents q -> runGetScheduledEvents q
|
|
|
|
RMGetEventInvocations q -> runGetEventInvocations q
|
|
|
|
|
|
|
|
RMCreateAction q -> runCreateAction q
|
|
|
|
RMDropAction q -> runDropAction q
|
|
|
|
RMUpdateAction q -> runUpdateAction q
|
|
|
|
RMCreateActionPermission q -> runCreateActionPermission q
|
|
|
|
RMDropActionPermission q -> runDropActionPermission q
|
|
|
|
|
|
|
|
RMCreateQueryCollection q -> runCreateCollection q
|
|
|
|
RMDropQueryCollection q -> runDropCollection q
|
|
|
|
RMAddQueryToCollection q -> runAddQueryToCollection q
|
|
|
|
RMDropQueryFromCollection q -> runDropQueryFromCollection q
|
|
|
|
RMAddCollectionToAllowlist q -> runAddCollectionToAllowlist q
|
|
|
|
RMDropCollectionFromAllowlist q -> runDropCollectionFromAllowlist q
|
|
|
|
|
|
|
|
RMCreateRestEndpoint q -> runCreateEndpoint q
|
|
|
|
RMDropRestEndpoint q -> runDropEndpoint q
|
|
|
|
|
|
|
|
RMSetCustomTypes q -> runSetCustomTypes q
|
|
|
|
|
|
|
|
RMSetApiLimits q -> runSetApiLimits q
|
|
|
|
RMRemoveApiLimits -> runRemoveApiLimits
|
|
|
|
|
|
|
|
RMSetMetricsConfig q -> runSetMetricsConfig q
|
|
|
|
RMRemoveMetricsConfig -> runRemoveMetricsConfig
|
|
|
|
|
|
|
|
RMAddInheritedRole q -> runAddInheritedRole q
|
|
|
|
RMDropInheritedRole q -> runDropInheritedRole q
|
|
|
|
|
|
|
|
RMReplaceMetadata q -> runReplaceMetadata q
|
|
|
|
RMExportMetadata q -> runExportMetadata q
|
|
|
|
RMClearMetadata q -> runClearMetadata q
|
|
|
|
RMReloadMetadata q -> runReloadMetadata q
|
|
|
|
RMGetInconsistentMetadata q -> runGetInconsistentMetadata q
|
|
|
|
RMDropInconsistentMetadata q -> runDropInconsistentMetadata q
|
[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
|
|
|
|
2021-05-05 15:25:27 +03:00
|
|
|
RMSetGraphqlSchemaIntrospectionOptions q -> runSetGraphqlSchemaIntrospectionOptions q
|
|
|
|
|
2021-07-07 04:43:42 +03:00
|
|
|
RMDumpInternalState q -> runDumpInternalState q
|
|
|
|
RMGetCatalogState q -> runGetCatalogState q
|
|
|
|
RMSetCatalogState q -> runSetCatalogState q
|
|
|
|
|
2021-05-05 15:25:27 +03:00
|
|
|
RMBulk q -> encJFromList <$> indexedMapM (runMetadataQueryM env currentResourceVersion) q
|
2021-07-07 04:43:42 +03:00
|
|
|
where
|
|
|
|
dispatch
|
|
|
|
:: (forall b. BackendMetadata b => i b -> a)
|
|
|
|
-> AnyBackend i
|
|
|
|
-> a
|
|
|
|
dispatch f x = dispatchAnyBackend @BackendMetadata x f
|
|
|
|
|
2021-02-16 11:08:19 +03:00
|
|
|
|
|
|
|
runMetadataQueryV2M
|
|
|
|
:: ( MonadIO m
|
|
|
|
, CacheRWM m
|
|
|
|
, MetadataM m
|
|
|
|
, MonadMetadataStorageQueryAPI m
|
[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
|
|
|
, HasServerConfigCtx m
|
2021-02-16 11:08:19 +03:00
|
|
|
)
|
2021-02-19 05:39:30 +03:00
|
|
|
=> MetadataResourceVersion
|
|
|
|
-> RQLMetadataV2
|
2021-02-16 11:08:19 +03:00
|
|
|
-> m EncJSON
|
2021-02-19 05:39:30 +03:00
|
|
|
runMetadataQueryV2M currentResourceVersion = \case
|
2021-02-16 11:08:19 +03:00
|
|
|
RMV2ReplaceMetadata q -> runReplaceMetadataV2 q
|
2021-02-19 05:39:30 +03:00
|
|
|
RMV2ExportMetadata q -> runExportMetadataV2 currentResourceVersion q
|