mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
server: new function permissions layer
Co-authored-by: Rikin Kachhia <54616969+rikinsk@users.noreply.github.com> Co-authored-by: Rakesh Emmadi <12475069+rakeshkky@users.noreply.github.com> GitOrigin-RevId: 35645121242294cb6bb500ea598e9a1f2ca67fa1
This commit is contained in:
parent
0767333597
commit
10a3f9960d
@ -6,7 +6,7 @@ echo "Running tests on node $CIRCLE_NODE_INDEX of $CIRCLE_NODE_TOTAL"
|
|||||||
if [ -z "$SERVER_TEST_TO_RUN" ]; then
|
if [ -z "$SERVER_TEST_TO_RUN" ]; then
|
||||||
echo 'Please specify $SERVER_TEST_TO_RUN'
|
echo 'Please specify $SERVER_TEST_TO_RUN'
|
||||||
exit 1
|
exit 1
|
||||||
else
|
else
|
||||||
echo "Running test $SERVER_TEST_TO_RUN"
|
echo "Running test $SERVER_TEST_TO_RUN"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -522,7 +522,7 @@ case "$SERVER_TEST_TO_RUN" in
|
|||||||
|
|
||||||
unset HASURA_GRAPHQL_CORS_DOMAIN
|
unset HASURA_GRAPHQL_CORS_DOMAIN
|
||||||
;;
|
;;
|
||||||
|
|
||||||
ws-init-cookie-read-cors-enabled)
|
ws-init-cookie-read-cors-enabled)
|
||||||
# test websocket transport with initial cookie header
|
# test websocket transport with initial cookie header
|
||||||
|
|
||||||
@ -660,6 +660,22 @@ case "$SERVER_TEST_TO_RUN" in
|
|||||||
kill_hge_servers
|
kill_hge_servers
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
function-permissions)
|
||||||
|
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE WITH FUNCTION PERMISSIONS ENABLED ########>\n"
|
||||||
|
TEST_TYPE="remote-schema-permissions"
|
||||||
|
export HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS=false
|
||||||
|
|
||||||
|
run_hge_with_args serve
|
||||||
|
wait_for_port 8080
|
||||||
|
|
||||||
|
pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --test_function_permissions test_graphql_queries.py::TestGraphQLQueryFunctionPermissions
|
||||||
|
pytest -n 1 -vv --hge-urls "$HGE_URL" --pg-urls "$HASURA_GRAPHQL_DATABASE_URL" --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --test_function_permissions test_graphql_mutations.py::TestGraphQLMutationFunctions
|
||||||
|
|
||||||
|
unset HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS
|
||||||
|
|
||||||
|
kill_hge_servers
|
||||||
|
;;
|
||||||
|
|
||||||
query-caching)
|
query-caching)
|
||||||
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE QUERY CACHING #####################################>\n"
|
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE QUERY CACHING #####################################>\n"
|
||||||
TEST_TYPE="query-caching"
|
TEST_TYPE="query-caching"
|
||||||
@ -705,18 +721,18 @@ case "$SERVER_TEST_TO_RUN" in
|
|||||||
if [ "$RUN_WEBHOOK_TESTS" == "true" ] ; then
|
if [ "$RUN_WEBHOOK_TESTS" == "true" ] ; then
|
||||||
TEST_TYPE="post-webhook"
|
TEST_TYPE="post-webhook"
|
||||||
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE WITH ADMIN SECRET & WEBHOOK (POST) #########################>\n"
|
echo -e "\n$(time_elapsed): <########## TEST GRAPHQL-ENGINE WITH ADMIN SECRET & WEBHOOK (POST) #########################>\n"
|
||||||
|
|
||||||
export HASURA_GRAPHQL_AUTH_HOOK="https://localhost:9090/"
|
export HASURA_GRAPHQL_AUTH_HOOK="https://localhost:9090/"
|
||||||
export HASURA_GRAPHQL_ADMIN_SECRET="HGE$RANDOM$RANDOM"
|
export HASURA_GRAPHQL_ADMIN_SECRET="HGE$RANDOM$RANDOM"
|
||||||
init_ssl
|
init_ssl
|
||||||
|
|
||||||
start_multiple_hge_servers
|
start_multiple_hge_servers
|
||||||
|
|
||||||
python3 webhook.py 9090 "$OUTPUT_FOLDER/ssl/webhook-key.pem" "$OUTPUT_FOLDER/ssl/webhook.pem" > "$OUTPUT_FOLDER/webhook.log" 2>&1 & WH_PID=$!
|
python3 webhook.py 9090 "$OUTPUT_FOLDER/ssl/webhook-key.pem" "$OUTPUT_FOLDER/ssl/webhook.pem" > "$OUTPUT_FOLDER/webhook.log" 2>&1 & WH_PID=$!
|
||||||
wait_for_port 9090
|
wait_for_port 9090
|
||||||
|
|
||||||
run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK"
|
run_pytest_parallel --hge-key="$HASURA_GRAPHQL_ADMIN_SECRET" --hge-webhook="$HASURA_GRAPHQL_AUTH_HOOK"
|
||||||
|
|
||||||
kill_hge_servers
|
kill_hge_servers
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
|
18
CHANGELOG.md
18
CHANGELOG.md
@ -60,6 +60,14 @@ access over it.
|
|||||||
either with the server flag ``--enable-remote-schema-permissions`` or the environment
|
either with the server flag ``--enable-remote-schema-permissions`` or the environment
|
||||||
variable ``HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`` set to ``true``.
|
variable ``HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`` set to ``true``.
|
||||||
|
|
||||||
|
### Function Permissions
|
||||||
|
|
||||||
|
Before volatile functions were supported, the permissions for functions were automatically inferred
|
||||||
|
from the select permission of the target table. Now, since volatile functions are supported we can't
|
||||||
|
do this anymore, so function permissions are introduced which will explicitly grant permission to
|
||||||
|
a function for a given role. A pre-requisite to adding a function permission is that the role should
|
||||||
|
have select permissions to the target table of the function.
|
||||||
|
|
||||||
### Breaking changes
|
### Breaking changes
|
||||||
|
|
||||||
- This release contains the [PDV refactor (#4111)](https://github.com/hasura/graphql-engine/pull/4111), a significant rewrite of the internals of the server, which did include some breaking changes:
|
- This release contains the [PDV refactor (#4111)](https://github.com/hasura/graphql-engine/pull/4111), a significant rewrite of the internals of the server, which did include some breaking changes:
|
||||||
@ -69,12 +77,12 @@ variable ``HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS`` set to ``true``.
|
|||||||
- if a query selects table `bar` through table `foo` via a relationship, the required permissions headers will be the union of the required headers of table `foo` and table `bar` (we used to only check the headers of the root table);
|
- if a query selects table `bar` through table `foo` via a relationship, the required permissions headers will be the union of the required headers of table `foo` and table `bar` (we used to only check the headers of the root table);
|
||||||
- if an insert does not have an `on_conflict` clause, it will not require the update permissions headers.
|
- if an insert does not have an `on_conflict` clause, it will not require the update permissions headers.
|
||||||
|
|
||||||
This release contains the remote schema permissions feature, which introduces a breaking change:
|
- This release contains the remote schema permissions feature, which introduces a breaking change:
|
||||||
|
|
||||||
Earlier, remote schemas were considered to be a public entity and all the roles had unrestricted
|
Earlier, remote schemas were considered to be a public entity and all the roles had unrestricted
|
||||||
access to the remote schema. If remote schema permissions are enabled in the graphql-engine, a given
|
access to the remote schema. If remote schema permissions are enabled in the graphql-engine, a given
|
||||||
remote schema will only be accessible to a role ,if the role has permissions configured for the said remote schema
|
remote schema will only be accessible to a role ,if the role has permissions configured for the said remote schema
|
||||||
and be accessible according to the permissions that were configured for the role.
|
and be accessible according to the permissions that were configured for the role.
|
||||||
|
|
||||||
### Bug fixes and improvements
|
### Bug fixes and improvements
|
||||||
|
|
||||||
|
@ -15,29 +15,33 @@ API Reference
|
|||||||
Available APIs
|
Available APIs
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| API | Endpoint | Access |
|
| API | Endpoint | Access |
|
||||||
+=================+=========================================+==================+
|
+==================================+===================================================+==================+
|
||||||
| GraphQL | :ref:`/v1/graphql <graphql_api>` | Permission rules |
|
| GraphQL | :ref:`/v1/graphql <graphql_api>` | Permission rules |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Relay | :ref:`/v1beta1/relay <relay_api>` | Permission rules |
|
| Relay | :ref:`/v1beta1/relay <relay_api>` | Permission rules |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Legacy GraphQL | :ref:`/v1alpha1/graphql <graphql_api>` | Permission rules |
|
| Legacy GraphQL | :ref:`/v1alpha1/graphql <graphql_api>` | Permission rules |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Schema/Metadata | :ref:`/v1/query <schema_metadata_api>` | Admin only |
|
| Schema/Metadata *(< v1.3)* | :ref:`/v1/query <schema_metadata_api>` | Admin only |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| RESTified GQL | :ref:`/api/rest <restified_api>` | GQL REST Routes |
|
| Schema *(> v1.4)* | :ref:`/v2/query <schema_api>` | Admin only |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Version | :ref:`/v1/version <version_api>` | Public |
|
| Metadata *(> v1.4)* | :ref:`/v1/metadata <metadata_api>` | Admin only |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Health | :ref:`/healthz <health_api>` | Public |
|
| Restified GQL | :ref:`/api/rest <restified_api>` | GQL REST Routes |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| PG Dump | :ref:`/v1alpha1/pg_dump <pg_dump_api>` | Admin only |
|
| Version | :ref:`/v1/version <version_api>` | Public |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Config | :ref:`/v1alpha1/config <config_api>` | Admin only |
|
| Health | :ref:`/healthz <health_api>` | Public |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
| Explain | :ref:`/v1/graphql/explain <explain_api>`| Admin only |
|
| PG Dump | :ref:`/v1alpha1/pg_dump <pg_dump_api>` | Admin only |
|
||||||
+-----------------+-----------------------------------------+------------------+
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
|
| Config | :ref:`/v1alpha1/config <config_api>` | Admin only |
|
||||||
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
|
| Explain | :ref:`/v1/graphql/explain <explain_api>` | Admin only |
|
||||||
|
+----------------------------------+---------------------------------------------------+------------------+
|
||||||
|
|
||||||
.. _graphql_api:
|
.. _graphql_api:
|
||||||
|
|
||||||
@ -61,14 +65,36 @@ See details at :ref:`api_reference_relay_graphql`.
|
|||||||
|
|
||||||
.. _schema_metadata_api:
|
.. _schema_metadata_api:
|
||||||
|
|
||||||
Schema / metadata API
|
Schema / metadata API V1 (v1.3 and below)
|
||||||
^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Hasura exposes a schema / metadata API for managing metadata for permissions/relationships or for directly
|
Hasura exposes a schema / metadata API for managing metadata for permissions/relationships or for directly
|
||||||
executing SQL on the underlying Postgres.
|
executing SQL on the underlying Postgres.
|
||||||
|
|
||||||
This is primarily intended to be used as an ``admin`` API to manage the Hasura schema and metadata.
|
This is primarily intended to be used as an ``admin`` API to manage the Hasura schema and metadata.
|
||||||
|
|
||||||
|
See details at :ref:`schema_metadata_apis` .
|
||||||
|
|
||||||
|
.. _schema_api:
|
||||||
|
|
||||||
|
Schema API (v1.4 and above)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Hasura exposes a schema API for directly executing SQL on the underlying Postgres.
|
||||||
|
|
||||||
|
This is primarily intended to be used as an ``admin`` API to manage the Hasura schema.
|
||||||
|
|
||||||
|
See details at :ref:`schema_apis`.
|
||||||
|
|
||||||
|
.. _metadata_api:
|
||||||
|
|
||||||
|
Metadata API (v1.4 and above)
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Hasura exposes a metadata API for managing metadata.
|
||||||
|
|
||||||
|
This is primarily intended to be used as an ``admin`` API to manage the Hasura metadata.
|
||||||
|
|
||||||
See details at :ref:`metadata_apis`.
|
See details at :ref:`metadata_apis`.
|
||||||
|
|
||||||
.. _version_api:
|
.. _version_api:
|
||||||
@ -142,7 +168,9 @@ You can refer to the following to know about all PostgreSQL types supported by t
|
|||||||
|
|
||||||
GraphQL API <graphql-api/index>
|
GraphQL API <graphql-api/index>
|
||||||
Relay GraphQL API <relay-graphql-api/index>
|
Relay GraphQL API <relay-graphql-api/index>
|
||||||
Schema / Metadata APIs <schema-metadata-api/index>
|
Schema / Metadata APIs V1 <schema-metadata-api/index>
|
||||||
|
Schema APIs <schema-api/index>
|
||||||
|
Metadata APIs <metadata-api/index>
|
||||||
RESTified GraphQL Endpoints <restified>
|
RESTified GraphQL Endpoints <restified>
|
||||||
Version API <version>
|
Version API <version>
|
||||||
Health check API <health>
|
Health check API <health>
|
||||||
|
62
docs/graphql/core/api-reference/metadata-api/dataerrors.csv
Normal file
62
docs/graphql/core/api-reference/metadata-api/dataerrors.csv
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
Status Code,Code,Error
|
||||||
|
400,postgres-error,Not-NULL violation. null value in column <column-name> violates not-null constraint
|
||||||
|
400,permission-denied,select on <column/table> for role <role-name> is not allowed.
|
||||||
|
400,not-exists,table <table-name> does not exist
|
||||||
|
400,not-exists,no such table/view exists in postgres : <table-name>
|
||||||
|
400,not-exists,<field-name> does not exist
|
||||||
|
400,already-tracked,view/table already tracked : <table-name>
|
||||||
|
400,access-denied,restricted access : admin only
|
||||||
|
400,not-supported,table renames are not yet supported : <table-name>
|
||||||
|
400,not-exists,<column-name> does not exist
|
||||||
|
400,already-exists,cannot add column <column-name> in table <table-name> as a relationship with the name already exists
|
||||||
|
400,invalid-json,invalid json
|
||||||
|
400,not-supported,column renames are not yet supported : <table-name>.<column-name>
|
||||||
|
400,invalid-headers,missing header : <header-name>
|
||||||
|
400,dependency-error,cannot change type of column <column-name> in table <table-name> because of the following dependencies : <dependencies>
|
||||||
|
400,invalid-headers,X-Hasura-User-Id should be an integer
|
||||||
|
400,dependency-error,cannot drop due to the following dependent objects : <dependencies>
|
||||||
|
400,access-denied,You have to be admin to access this endpoint
|
||||||
|
400,parse-failed,parsing dotted table failed : <table-name>
|
||||||
|
400,access-denied,not authorised to access this tx
|
||||||
|
400,already-exists,multiple declarations exist for the following <table-name> : <duplicates>
|
||||||
|
400,not-exists,tx does not exists
|
||||||
|
400,already-exists,column/relationship of table <table-name> already exists
|
||||||
|
400,already-initialised,the state seems to be initialised already. \ \ you may need to migrate from this version: <catalog-version>
|
||||||
|
400,constraint-error,no foreign constraint exists on the given column
|
||||||
|
400,not-supported,unsupported version : <catalog-version>
|
||||||
|
400,constraint-error,more than one foreign key constraint exists on the given column
|
||||||
|
400,already-exists,the query template already exists <template-name>
|
||||||
|
400,permission-error,<permission-type>' permission on <table-name> for role <role-name> already exists
|
||||||
|
400,permission-error,<permission-type>' permission on <table-name> for role <role-name> does not exist
|
||||||
|
400,unexpected-payload,Unknown operator : <operator-type>
|
||||||
|
400,unexpected-payload,expecting a string for column operator
|
||||||
|
400,unexpected-payload,"incompatible column types : '<column-name>', '<column-name>' "
|
||||||
|
400,unexpected-payload,Expecting 'constraint' or 'constraint_on' when the 'action' is 'update'
|
||||||
|
400,unexpected-payload,constraint' and 'constraint_on' cannot be set at a time
|
||||||
|
400,unexpected-payload,upsert is not allowed for role '<role-name>'
|
||||||
|
400,unexpected-payload,objects should not be empty
|
||||||
|
400,invalid-params,missing parameter : <param-name>
|
||||||
|
400,unexpected-payload,can't be empty
|
||||||
|
400,,<col-name>' is a relationship and should be expanded
|
||||||
|
400,unexpected-payload,<column-name>' should be included in 'columns'
|
||||||
|
400,unexpected-payload,<column-name>' is an array relationship and can't be used in 'order_by'
|
||||||
|
400,,<column-name>' is a Postgres column and cannot be chained further
|
||||||
|
400,unexpected-payload,order_by array should not be empty
|
||||||
|
400,unexpected-payload,"when selecting an 'obj_relationship' 'where', 'order_by', 'limit' and 'offset' can't be used"
|
||||||
|
400,unexpected-payload,"atleast one of $set, $inc, $mul has to be present"
|
||||||
|
400,permission-denied,<permission-type> on <table-name> for role <role-name> is not allowed
|
||||||
|
400,not-exists,no such column exists : <column-name>
|
||||||
|
400,permission-denied,role <role-name> does not have permission to <permission-type> column <column-name>
|
||||||
|
400,,"expecting a postgres column; but, <name> is relationship"
|
||||||
|
400,unexpected-payload,JSON column can not be part of where clause
|
||||||
|
400,unexpected-payload,is of type <type-name>; this operator works only on column of types <[types]>
|
||||||
|
400,postgres-error,query execution failed
|
||||||
|
500,unexpected,unexpected dependency of relationship : <dependency>
|
||||||
|
500,unexpected,unexpected dependent object : <dependency>
|
||||||
|
500,unexpected,field already exists
|
||||||
|
500,unexpected,field does not exist
|
||||||
|
500,unexpected,permission does not exist
|
||||||
|
500,postgres-error,postgres transaction error
|
||||||
|
500,postgres-error,connection error
|
||||||
|
500,postgres-error,postgres query error
|
||||||
|
404,not-found,No such resource exists
|
|
164
docs/graphql/core/api-reference/metadata-api/index.rst
Normal file
164
docs/graphql/core/api-reference/metadata-api/index.rst
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
.. meta::
|
||||||
|
:description: Hasura metadata API reference
|
||||||
|
:keywords: hasura, docs, metadata API, API reference
|
||||||
|
|
||||||
|
.. _metadata_apis:
|
||||||
|
|
||||||
|
Metadata API Reference (v1.4 and above)
|
||||||
|
=======================================
|
||||||
|
|
||||||
|
.. contents:: Table of contents
|
||||||
|
:backlinks: none
|
||||||
|
:depth: 1
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
This is primarily intended to be used as an ``admin`` API to manage the Hasura metadata.
|
||||||
|
|
||||||
|
Endpoint
|
||||||
|
--------
|
||||||
|
|
||||||
|
All requests are ``POST`` requests to the ``/v1/metadata`` endpoint.
|
||||||
|
|
||||||
|
Request structure
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
|
POST /v1/metadata HTTP/1.1
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "<query-type>",
|
||||||
|
"args": <args-object>
|
||||||
|
}
|
||||||
|
|
||||||
|
Request body
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
Query_
|
||||||
|
|
||||||
|
.. _Query:
|
||||||
|
|
||||||
|
Query
|
||||||
|
*****
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Key
|
||||||
|
- Required
|
||||||
|
- Schema
|
||||||
|
- Description
|
||||||
|
* - type
|
||||||
|
- true
|
||||||
|
- String
|
||||||
|
- Type of the query
|
||||||
|
* - args
|
||||||
|
- true
|
||||||
|
- JSON Value
|
||||||
|
- The arguments to the query
|
||||||
|
* - version
|
||||||
|
- false
|
||||||
|
- Integer
|
||||||
|
- Version of the API (default: 1)
|
||||||
|
|
||||||
|
Request types
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The various types of queries are listed in the following table:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - ``type``
|
||||||
|
- ``args``
|
||||||
|
- ``version``
|
||||||
|
- Synopsis
|
||||||
|
|
||||||
|
* - **bulk**
|
||||||
|
- :ref:`Query <Query>` array
|
||||||
|
- 1
|
||||||
|
- Execute multiple operations in a single query
|
||||||
|
|
||||||
|
* - :ref:`pg_create_function_permission`
|
||||||
|
- :ref:`pg_create_function_permission_args <pg_create_function_permission_args_syntax>`
|
||||||
|
- 1
|
||||||
|
- Create a function permission
|
||||||
|
|
||||||
|
* - :ref:`pg_drop_function_permission`
|
||||||
|
- :ref:`pg_drop_function_permission_args <pg_drop_function_permission_args_syntax>`
|
||||||
|
- 1
|
||||||
|
- Drop an existing function permission
|
||||||
|
|
||||||
|
Response structure
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: 10 10 30
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Status code
|
||||||
|
- Description
|
||||||
|
- Response structure
|
||||||
|
|
||||||
|
* - ``200``
|
||||||
|
- Success
|
||||||
|
- .. parsed-literal::
|
||||||
|
|
||||||
|
Request specific
|
||||||
|
|
||||||
|
* - ``400``
|
||||||
|
- Bad request
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"path" : String,
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
* - ``401``
|
||||||
|
- Unauthorized
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
* - ``500``
|
||||||
|
- Internal server error
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Error codes
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. csv-table::
|
||||||
|
:file: dataerrors.csv
|
||||||
|
:widths: 10, 20, 70
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
Disabling metadata API
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Since this API can be used to make changes to the GraphQL schema, it can be
|
||||||
|
disabled, especially in production deployments.
|
||||||
|
|
||||||
|
The ``enabled-apis`` flag or the ``HASURA_GRAPHQL_ENABLED_APIS`` env var can be used to
|
||||||
|
enable/disable this API. By default, the schema/metadata API is enabled. To disable it, you need
|
||||||
|
to explicitly state that this API is not enabled i.e. remove it from the list of enabled APIs.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# enable only graphql api, disable metadata and pgdump
|
||||||
|
--enabled-apis="graphql"
|
||||||
|
HASURA_GRAPHQL_ENABLED_APIS="graphql"
|
||||||
|
|
||||||
|
See :ref:`server_flag_reference` for info on setting the above flag/env var.
|
154
docs/graphql/core/api-reference/schema-api/index.rst
Normal file
154
docs/graphql/core/api-reference/schema-api/index.rst
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
.. meta::
|
||||||
|
:description: Hasura schema API reference
|
||||||
|
:keywords: hasura, docs, schema API, API reference
|
||||||
|
|
||||||
|
.. _schema_apis:
|
||||||
|
|
||||||
|
Schema API Reference (v1.4 and above)
|
||||||
|
=====================================
|
||||||
|
|
||||||
|
.. contents:: Table of contents
|
||||||
|
:backlinks: none
|
||||||
|
:depth: 1
|
||||||
|
:local:
|
||||||
|
|
||||||
|
Introduction
|
||||||
|
------------
|
||||||
|
|
||||||
|
The schema API provides the following features:
|
||||||
|
|
||||||
|
1. Execute SQL on the underlying Postgres database, supports schema modifying actions.
|
||||||
|
|
||||||
|
This is primarily intended to be used as an ``admin`` API to manage the Hasura schema.
|
||||||
|
|
||||||
|
Endpoint
|
||||||
|
--------
|
||||||
|
|
||||||
|
All requests are ``POST`` requests to the ``/v2/query`` endpoint.
|
||||||
|
|
||||||
|
Request structure
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
|
POST /v1/query HTTP/1.1
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "<query-type>",
|
||||||
|
"args": <args-object>
|
||||||
|
}
|
||||||
|
|
||||||
|
Request body
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. parsed-literal::
|
||||||
|
|
||||||
|
Query_
|
||||||
|
|
||||||
|
.. _Query_:
|
||||||
|
|
||||||
|
Query
|
||||||
|
*****
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Key
|
||||||
|
- Required
|
||||||
|
- Schema
|
||||||
|
- Description
|
||||||
|
* - type
|
||||||
|
- true
|
||||||
|
- String
|
||||||
|
- Type of the query
|
||||||
|
* - args
|
||||||
|
- true
|
||||||
|
- JSON Value
|
||||||
|
- The arguments to the query
|
||||||
|
* - version
|
||||||
|
- false
|
||||||
|
- Integer
|
||||||
|
- Version of the API (default: 1)
|
||||||
|
|
||||||
|
Request types
|
||||||
|
-------------
|
||||||
|
|
||||||
|
The various types of queries are listed in the following table:
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - ``type``
|
||||||
|
- ``args``
|
||||||
|
- ``version``
|
||||||
|
- Synopsis
|
||||||
|
|
||||||
|
* - **bulk**
|
||||||
|
- :ref:`Query <Query>` array
|
||||||
|
- 1
|
||||||
|
- Execute multiple operations in a single query
|
||||||
|
|
||||||
|
* - :ref:`run_sql`
|
||||||
|
- :ref:`run_sql_args <run_sql_syntax>`
|
||||||
|
- 1
|
||||||
|
- Run SQL directly on Postgres
|
||||||
|
|
||||||
|
Response structure
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:widths: 10 10 30
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Status code
|
||||||
|
- Description
|
||||||
|
- Response structure
|
||||||
|
|
||||||
|
* - ``200``
|
||||||
|
- Success
|
||||||
|
- .. parsed-literal::
|
||||||
|
|
||||||
|
Request specific
|
||||||
|
|
||||||
|
* - ``400``
|
||||||
|
- Bad request
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"path" : String,
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
* - ``401``
|
||||||
|
- Unauthorized
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
* - ``500``
|
||||||
|
- Internal server error
|
||||||
|
- .. code-block:: haskell
|
||||||
|
|
||||||
|
{
|
||||||
|
"error" : String
|
||||||
|
}
|
||||||
|
|
||||||
|
Disabling schema API
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Since this API can be used to make changes to the GraphQL schema, it can be
|
||||||
|
disabled, especially in production deployments.
|
||||||
|
|
||||||
|
The ``enabled-apis`` flag or the ``HASURA_GRAPHQL_ENABLED_APIS`` env var can be used to
|
||||||
|
enable/disable this API. By default, the schema/metadata API is enabled. To disable it, you need
|
||||||
|
to explicitly state that this API is not enabled i.e. remove it from the list of enabled APIs.
|
||||||
|
|
||||||
|
.. code-block:: bash
|
||||||
|
|
||||||
|
# enable only graphql api, disable metadata and pgdump
|
||||||
|
--enabled-apis="graphql"
|
||||||
|
HASURA_GRAPHQL_ENABLED_APIS="graphql"
|
||||||
|
|
||||||
|
See :ref:`server_flag_reference` for info on setting the above flag/env var.
|
@ -107,7 +107,7 @@ In most cases you will want ``VOLATILE`` functions to only be exposed as
|
|||||||
mutations, and only ``STABLE`` and ``IMMUTABLE`` functions to be queries.
|
mutations, and only ``STABLE`` and ``IMMUTABLE`` functions to be queries.
|
||||||
When tracking ``VOLATILE`` functions under the ``query`` root, the user needs
|
When tracking ``VOLATILE`` functions under the ``query`` root, the user needs
|
||||||
to guarantee that the field is idempotent and side-effect free, in the context
|
to guarantee that the field is idempotent and side-effect free, in the context
|
||||||
of the resulting GraphQL API.
|
of the resulting GraphQL API.
|
||||||
|
|
||||||
One such use case might be a function that wraps a simple query and performs
|
One such use case might be a function that wraps a simple query and performs
|
||||||
some logging visible only to administrators.
|
some logging visible only to administrators.
|
||||||
@ -170,6 +170,102 @@ Function Configuration
|
|||||||
- **Return type**: MUST be ``SETOF <table-name>`` where ``<table-name>`` is already tracked
|
- **Return type**: MUST be ``SETOF <table-name>`` where ``<table-name>`` is already tracked
|
||||||
- **Argument modes**: ONLY ``IN``
|
- **Argument modes**: ONLY ``IN``
|
||||||
|
|
||||||
|
.. _pg_create_function_permission:
|
||||||
|
|
||||||
|
pg_create_function_permission
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
``pg_create_function_permission`` is used to add permission to an existing custom function.
|
||||||
|
To add a function permission, the graphql-engine should have disabled inferring of
|
||||||
|
function permissions and the provided role should have select permissions to the
|
||||||
|
target table of the function.
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
|
POST /v1/metadata HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Hasura-Role: admin
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "pg_create_function_permission",
|
||||||
|
"args": {
|
||||||
|
"function": "get_articles",
|
||||||
|
"role": "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. _pg_create_function_permission_args_syntax:
|
||||||
|
|
||||||
|
Args syntax
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Key
|
||||||
|
- Required
|
||||||
|
- Schema
|
||||||
|
- Description
|
||||||
|
* - function
|
||||||
|
- true
|
||||||
|
- :ref:`FunctionName <FunctionName>`
|
||||||
|
- Name of the SQL function
|
||||||
|
* - role
|
||||||
|
- true
|
||||||
|
- :ref:`RoleName <RoleName>`
|
||||||
|
- Name of the role
|
||||||
|
* - source
|
||||||
|
- false
|
||||||
|
- Text
|
||||||
|
- Name of the source of the SQL function
|
||||||
|
|
||||||
|
.. _pg_drop_function_permission:
|
||||||
|
|
||||||
|
pg_drop_function_permission
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
``pg_drop_function_permission`` is used to drop an existing function permission.
|
||||||
|
|
||||||
|
.. code-block:: http
|
||||||
|
|
||||||
|
POST /v1/metadata HTTP/1.1
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Hasura-Role: admin
|
||||||
|
|
||||||
|
{
|
||||||
|
"type": "pg_drop_function_permission",
|
||||||
|
"args": {
|
||||||
|
"function": "get_articles",
|
||||||
|
"role": "user"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.. _pg_drop_function_permission_args_syntax:
|
||||||
|
|
||||||
|
Args syntax
|
||||||
|
^^^^^^^^^^^
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:header-rows: 1
|
||||||
|
|
||||||
|
* - Key
|
||||||
|
- Required
|
||||||
|
- Schema
|
||||||
|
- Description
|
||||||
|
* - function
|
||||||
|
- true
|
||||||
|
- :ref:`FunctionName <FunctionName>`
|
||||||
|
- Name of the SQL function
|
||||||
|
* - role
|
||||||
|
- true
|
||||||
|
- :ref:`RoleName <RoleName>`
|
||||||
|
- Name of the role
|
||||||
|
* - source
|
||||||
|
- false
|
||||||
|
- Text
|
||||||
|
- Name of the source of the SQL function
|
||||||
|
|
||||||
|
|
||||||
.. _untrack_function:
|
.. _untrack_function:
|
||||||
|
|
||||||
untrack_function
|
untrack_function
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
:description: Hasura schema/metadata API reference
|
:description: Hasura schema/metadata API reference
|
||||||
:keywords: hasura, docs, schema/metadata API, API reference
|
:keywords: hasura, docs, schema/metadata API, API reference
|
||||||
|
|
||||||
.. _metadata_apis:
|
.. _schema_metadata_apis:
|
||||||
|
|
||||||
Schema / Metadata API Reference
|
Schema / Metadata API Reference (v1.3 and below)
|
||||||
===============================
|
================================================
|
||||||
|
|
||||||
.. contents:: Table of contents
|
.. contents:: Table of contents
|
||||||
:backlinks: none
|
:backlinks: none
|
||||||
|
@ -177,7 +177,7 @@ API should be called with the schema document.
|
|||||||
}
|
}
|
||||||
|
|
||||||
Argument Presets
|
Argument Presets
|
||||||
^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Argument presets can be used to automatically inject input values for fields
|
Argument presets can be used to automatically inject input values for fields
|
||||||
during execution. This way the field is executed with limited input values. Argument
|
during execution. This way the field is executed with limited input values. Argument
|
||||||
@ -344,7 +344,7 @@ Args syntax
|
|||||||
.. _RemoteSchemaPermission:
|
.. _RemoteSchemaPermission:
|
||||||
|
|
||||||
RemoteSchemaPermission
|
RemoteSchemaPermission
|
||||||
&&&&&&&&&&&&&&&&&&&&&&
|
""""""""""""""""""""""
|
||||||
|
|
||||||
.. list-table::
|
.. list-table::
|
||||||
:header-rows: 1
|
:header-rows: 1
|
||||||
|
@ -227,6 +227,16 @@ For the ``serve`` sub-command these are the available flags and ENV variables:
|
|||||||
- ``HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS``
|
- ``HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS``
|
||||||
- Enable remote schema permissions (default: ``false``)
|
- Enable remote schema permissions (default: ``false``)
|
||||||
|
|
||||||
|
* - ``--infer-function-permissions``
|
||||||
|
- ``HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS``
|
||||||
|
- When the ``--infer-function-permissions`` flag is set to ``false``, a function ``f``, stable, immutable or volatile is
|
||||||
|
only exposed for a role ``r`` if there is a permission defined on the function ``f`` for the role ``r``, creating a
|
||||||
|
function permission will only be allowed if there is a select permission on the table type.
|
||||||
|
|
||||||
|
When the ``--infer-function-permissions`` flag is set to ``true`` or the flag is omitted (defaults to ``true``), the
|
||||||
|
permission of the function is inferred from the select permissions from the target table of the function, only for
|
||||||
|
stable/immutable functions. Volatile functions are not exposed to any of the roles in this case.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
When the equivalent flags for environment variables are used, the flags will take precedence.
|
When the equivalent flags for environment variables are used, the flags will take precedence.
|
||||||
|
@ -583,3 +583,8 @@ Permissions for custom function queries
|
|||||||
**For example**, in our text-search example above, if the role ``user`` doesn't have the requisite permissions to view
|
**For example**, in our text-search example above, if the role ``user`` doesn't have the requisite permissions to view
|
||||||
the table ``article``, a validation error will be thrown if the ``search_articles`` query is run using the ``user``
|
the table ``article``, a validation error will be thrown if the ``search_articles`` query is run using the ``user``
|
||||||
role.
|
role.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When inferring of function permissions is disabled, then there should be a function permission configured for the function to
|
||||||
|
be accessible to a role, otherwise the function is not exposed to the role.
|
||||||
|
@ -101,12 +101,14 @@ runApp env (HGEOptionsG rci metadataDbUrl hgeCmd) = do
|
|||||||
remoteSchemaPermsCtx = RemoteSchemaPermsDisabled
|
remoteSchemaPermsCtx = RemoteSchemaPermsDisabled
|
||||||
pgLogger = print
|
pgLogger = print
|
||||||
pgSourceResolver = mkPgSourceResolver pgLogger
|
pgSourceResolver = mkPgSourceResolver pgLogger
|
||||||
cacheBuildParams = CacheBuildParams _gcHttpManager sqlGenCtx remoteSchemaPermsCtx pgSourceResolver
|
functionPermsCtx = FunctionPermissionsInferred
|
||||||
|
serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
|
cacheBuildParams = CacheBuildParams _gcHttpManager pgSourceResolver serverConfigCtx
|
||||||
runManagedT (mkMinimalPool _gcMetadataDbConnInfo) $ \metadataDbPool -> do
|
runManagedT (mkMinimalPool _gcMetadataDbConnInfo) $ \metadataDbPool -> do
|
||||||
res <- flip runPGMetadataStorageApp (metadataDbPool, pgLogger) $
|
res <- flip runPGMetadataStorageApp (metadataDbPool, pgLogger) $
|
||||||
runMetadataStorageT $ liftEitherM do
|
runMetadataStorageT $ liftEitherM do
|
||||||
metadata <- fetchMetadata
|
metadata <- fetchMetadata
|
||||||
runAsAdmin sqlGenCtx _gcHttpManager remoteSchemaPermsCtx $ do
|
runAsAdmin sqlGenCtx _gcHttpManager remoteSchemaPermsCtx functionPermsCtx $ do
|
||||||
schemaCache <- runCacheBuild cacheBuildParams $
|
schemaCache <- runCacheBuild cacheBuildParams $
|
||||||
buildRebuildableSchemaCache env metadata
|
buildRebuildableSchemaCache env metadata
|
||||||
execQuery env queryBs
|
execQuery env queryBs
|
||||||
|
@ -275,7 +275,7 @@ initialiseServeCtx env GlobalCtx{..} so@ServeOptions{..} = do
|
|||||||
(rebuildableSchemaCache, cacheInitStartTime) <-
|
(rebuildableSchemaCache, cacheInitStartTime) <-
|
||||||
lift . flip onException (flushLogger loggerCtx) $
|
lift . flip onException (flushLogger loggerCtx) $
|
||||||
migrateCatalogSchema env logger metadataDbPool maybeDefaultSourceConfig _gcHttpManager
|
migrateCatalogSchema env logger metadataDbPool maybeDefaultSourceConfig _gcHttpManager
|
||||||
sqlGenCtx soEnableRemoteSchemaPermissions (mkPgSourceResolver pgLogger)
|
sqlGenCtx soEnableRemoteSchemaPermissions soInferFunctionPermissions (mkPgSourceResolver pgLogger)
|
||||||
|
|
||||||
let schemaSyncCtx = SchemaSyncCtx schemaSyncListenerThread schemaSyncEventRef cacheInitStartTime
|
let schemaSyncCtx = SchemaSyncCtx schemaSyncListenerThread schemaSyncEventRef cacheInitStartTime
|
||||||
pure $ ServeCtx _gcHttpManager instanceId loggers metadataDbPool latch
|
pure $ ServeCtx _gcHttpManager instanceId loggers metadataDbPool latch
|
||||||
@ -297,14 +297,19 @@ mkLoggers enabledLogs logLevel = do
|
|||||||
migrateCatalogSchema
|
migrateCatalogSchema
|
||||||
:: (HasVersion, MonadIO m, MonadBaseControl IO m)
|
:: (HasVersion, MonadIO m, MonadBaseControl IO m)
|
||||||
=> Env.Environment -> Logger Hasura -> Q.PGPool -> Maybe SourceConfiguration
|
=> Env.Environment -> Logger Hasura -> Q.PGPool -> Maybe SourceConfiguration
|
||||||
-> HTTP.Manager -> SQLGenCtx -> RemoteSchemaPermsCtx -> SourceResolver
|
-> HTTP.Manager -> SQLGenCtx -> RemoteSchemaPermsCtx -> FunctionPermissionsCtx
|
||||||
|
-> SourceResolver
|
||||||
-> m (RebuildableSchemaCache, UTCTime)
|
-> m (RebuildableSchemaCache, UTCTime)
|
||||||
migrateCatalogSchema env logger pool defaultSourceConfig httpManager sqlGenCtx remoteSchemaPermsCtx sourceResolver = do
|
migrateCatalogSchema env logger pool defaultSourceConfig
|
||||||
|
httpManager sqlGenCtx remoteSchemaPermsCtx functionPermsCtx
|
||||||
|
sourceResolver = do
|
||||||
currentTime <- liftIO Clock.getCurrentTime
|
currentTime <- liftIO Clock.getCurrentTime
|
||||||
initialiseResult <- runExceptT $ do
|
initialiseResult <- runExceptT $ do
|
||||||
(migrationResult, metadata) <- Q.runTx pool (Q.Serializable, Just Q.ReadWrite) $
|
(migrationResult, metadata) <- Q.runTx pool (Q.Serializable, Just Q.ReadWrite) $
|
||||||
migrateCatalog defaultSourceConfig currentTime
|
migrateCatalog defaultSourceConfig currentTime
|
||||||
let cacheBuildParams = CacheBuildParams httpManager sqlGenCtx remoteSchemaPermsCtx sourceResolver
|
let serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
|
cacheBuildParams =
|
||||||
|
CacheBuildParams httpManager sourceResolver serverConfigCtx
|
||||||
buildReason = case getMigratedFrom migrationResult of
|
buildReason = case getMigratedFrom migrationResult of
|
||||||
Nothing -> CatalogSync
|
Nothing -> CatalogSync
|
||||||
Just version ->
|
Just version ->
|
||||||
@ -451,6 +456,7 @@ runHGEServer env ServeOptions{..} ServeCtx{..} initTime postPollHook serverMetri
|
|||||||
_scSchemaCache
|
_scSchemaCache
|
||||||
ekgStore
|
ekgStore
|
||||||
soEnableRemoteSchemaPermissions
|
soEnableRemoteSchemaPermissions
|
||||||
|
soInferFunctionPermissions
|
||||||
soConnectionOptions
|
soConnectionOptions
|
||||||
soWebsocketKeepAlive
|
soWebsocketKeepAlive
|
||||||
|
|
||||||
@ -462,6 +468,7 @@ runHGEServer env ServeOptions{..} ServeCtx{..} initTime postPollHook serverMetri
|
|||||||
_ <- startSchemaSyncProcessorThread sqlGenCtx
|
_ <- startSchemaSyncProcessorThread sqlGenCtx
|
||||||
logger _scHttpManager _sscSyncEventRef
|
logger _scHttpManager _sscSyncEventRef
|
||||||
cacheRef _scInstanceId _sscCacheInitStartTime soEnableRemoteSchemaPermissions
|
cacheRef _scInstanceId _sscCacheInitStartTime soEnableRemoteSchemaPermissions
|
||||||
|
soInferFunctionPermissions
|
||||||
|
|
||||||
let
|
let
|
||||||
maxEvThrds = fromMaybe defaultMaxEventThreads soEventsHttpPoolSize
|
maxEvThrds = fromMaybe defaultMaxEventThreads soEventsHttpPoolSize
|
||||||
@ -613,10 +620,12 @@ runAsAdmin
|
|||||||
:: SQLGenCtx
|
:: SQLGenCtx
|
||||||
-> HTTP.Manager
|
-> HTTP.Manager
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> RunT m a
|
-> RunT m a
|
||||||
-> m (Either QErr a)
|
-> m (Either QErr a)
|
||||||
runAsAdmin sqlGenCtx httpManager remoteSchemaPermsCtx m = do
|
runAsAdmin sqlGenCtx httpManager remoteSchemaPermsCtx functionPermsCtx m = do
|
||||||
let runCtx = RunCtx adminUserInfo httpManager sqlGenCtx remoteSchemaPermsCtx
|
let serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
|
runCtx = RunCtx adminUserInfo httpManager serverConfigCtx
|
||||||
runExceptT $ peelRun runCtx m
|
runExceptT $ peelRun runCtx m
|
||||||
|
|
||||||
execQuery
|
execQuery
|
||||||
@ -626,10 +635,9 @@ execQuery
|
|||||||
, MonadBaseControl IO m
|
, MonadBaseControl IO m
|
||||||
, MonadUnique m
|
, MonadUnique m
|
||||||
, HasHttpManagerM m
|
, HasHttpManagerM m
|
||||||
, HasSQLGenCtx m
|
|
||||||
, UserInfoM m
|
, UserInfoM m
|
||||||
, Tracing.MonadTrace m
|
, Tracing.MonadTrace m
|
||||||
, HasRemoteSchemaPermsCtx m
|
, HasServerConfigCtx m
|
||||||
, MetadataM m
|
, MetadataM m
|
||||||
, MonadMetadataStorageQueryAPI m
|
, MonadMetadataStorageQueryAPI m
|
||||||
)
|
)
|
||||||
|
@ -77,8 +77,7 @@ buildGQLContext
|
|||||||
, MonadError QErr m
|
, MonadError QErr m
|
||||||
, MonadIO m
|
, MonadIO m
|
||||||
, MonadUnique m
|
, MonadUnique m
|
||||||
, HasSQLGenCtx m
|
, HasServerConfigCtx m
|
||||||
, HasRemoteSchemaPermsCtx m
|
|
||||||
)
|
)
|
||||||
=> ( GraphQLQueryType
|
=> ( GraphQLQueryType
|
||||||
, SourceCache
|
, SourceCache
|
||||||
@ -92,9 +91,8 @@ buildGQLContext
|
|||||||
)
|
)
|
||||||
buildGQLContext =
|
buildGQLContext =
|
||||||
proc (queryType, pgSources, allRemoteSchemas, allActions, nonObjectCustomTypes) -> do
|
proc (queryType, pgSources, allRemoteSchemas, allActions, nonObjectCustomTypes) -> do
|
||||||
|
ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx@(SQLGenCtx stringifyNum) <-
|
||||||
sqlGenCtx@(SQLGenCtx{ stringifyNum }) <- bindA -< askSQLGenCtx
|
bindA -< askServerConfigCtx
|
||||||
remoteSchemaPermsCtx <- bindA -< askRemoteSchemaPermsCtx
|
|
||||||
|
|
||||||
let remoteSchemasRoles = concatMap (Map.keys . _rscPermissions . fst . snd) $ Map.toList allRemoteSchemas
|
let remoteSchemasRoles = concatMap (Map.keys . _rscPermissions . fst . snd) $ Map.toList allRemoteSchemas
|
||||||
|
|
||||||
@ -108,7 +106,9 @@ buildGQLContext =
|
|||||||
allRemoteSchemas
|
allRemoteSchemas
|
||||||
<&> (\(remoteSchemaCtx, _metadataObj) ->
|
<&> (\(remoteSchemaCtx, _metadataObj) ->
|
||||||
(_rscIntro remoteSchemaCtx, _rscParsed remoteSchemaCtx))
|
(_rscIntro remoteSchemaCtx, _rscParsed remoteSchemaCtx))
|
||||||
adminQueryContext = QueryContext stringifyNum queryType adminRemoteRelationshipQueryCtx
|
-- The function permissions context doesn't actually matter because the
|
||||||
|
-- admin will have access to the function anyway
|
||||||
|
adminQueryContext = QueryContext stringifyNum queryType adminRemoteRelationshipQueryCtx FunctionPermissionsInferred
|
||||||
|
|
||||||
-- build the admin DB-only context so that we can check against name clashes with remotes
|
-- build the admin DB-only context so that we can check against name clashes with remotes
|
||||||
-- TODO: Is there a better way to check for conflicts without actually building the admin schema?
|
-- TODO: Is there a better way to check for conflicts without actually building the admin schema?
|
||||||
@ -142,10 +142,10 @@ buildGQLContext =
|
|||||||
( Set.toMap allRoles & Map.traverseWithKey \roleName () ->
|
( Set.toMap allRoles & Map.traverseWithKey \roleName () ->
|
||||||
case queryType of
|
case queryType of
|
||||||
QueryHasura ->
|
QueryHasura ->
|
||||||
buildRoleContext (sqlGenCtx, queryType) pgSources allRemoteSchemas allActionInfos
|
buildRoleContext (sqlGenCtx, queryType, functionPermsCtx) pgSources allRemoteSchemas allActionInfos
|
||||||
nonObjectCustomTypes remotes roleName remoteSchemaPermsCtx
|
nonObjectCustomTypes remotes roleName remoteSchemaPermsCtx
|
||||||
QueryRelay ->
|
QueryRelay ->
|
||||||
buildRelayRoleContext (sqlGenCtx, queryType) pgSources allActionInfos
|
buildRelayRoleContext (sqlGenCtx, queryType, functionPermsCtx) pgSources allActionInfos
|
||||||
nonObjectCustomTypes adminMutationRemotes roleName
|
nonObjectCustomTypes adminMutationRemotes roleName
|
||||||
)
|
)
|
||||||
unauthenticated <- bindA -< unauthenticatedContext adminQueryRemotes adminMutationRemotes remoteSchemaPermsCtx
|
unauthenticated <- bindA -< unauthenticatedContext adminQueryRemotes adminMutationRemotes remoteSchemaPermsCtx
|
||||||
@ -187,13 +187,13 @@ buildRoleBasedRemoteSchemaParser role remoteSchemaCache = do
|
|||||||
-- TODO: Integrate relay schema
|
-- TODO: Integrate relay schema
|
||||||
buildRoleContext
|
buildRoleContext
|
||||||
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
||||||
=> (SQLGenCtx, GraphQLQueryType) -> SourceCache -> RemoteSchemaCache
|
=> (SQLGenCtx, GraphQLQueryType, FunctionPermissionsCtx) -> SourceCache -> RemoteSchemaCache
|
||||||
-> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
-> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
||||||
-> [( RemoteSchemaName , (IntrospectionResult, ParsedIntrospection))]
|
-> [( RemoteSchemaName , (IntrospectionResult, ParsedIntrospection))]
|
||||||
-> RoleName
|
-> RoleName
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
-> m (RoleContext GQLContext)
|
-> m (RoleContext GQLContext)
|
||||||
buildRoleContext (SQLGenCtx stringifyNum, queryType) sources
|
buildRoleContext (SQLGenCtx stringifyNum, queryType, functionPermsCtx) sources
|
||||||
allRemoteSchemas allActionInfos nonObjectCustomTypes remotes roleName remoteSchemaPermsCtx = do
|
allRemoteSchemas allActionInfos nonObjectCustomTypes remotes roleName remoteSchemaPermsCtx = do
|
||||||
|
|
||||||
roleBasedRemoteSchemas <-
|
roleBasedRemoteSchemas <-
|
||||||
@ -206,7 +206,7 @@ buildRoleContext (SQLGenCtx stringifyNum, queryType) sources
|
|||||||
let queryRemotes = getQueryRemotes $ snd . snd <$> roleBasedRemoteSchemas
|
let queryRemotes = getQueryRemotes $ snd . snd <$> roleBasedRemoteSchemas
|
||||||
mutationRemotes = getMutationRemotes $ snd . snd <$> roleBasedRemoteSchemas
|
mutationRemotes = getMutationRemotes $ snd . snd <$> roleBasedRemoteSchemas
|
||||||
remoteRelationshipQueryContext = Map.fromList roleBasedRemoteSchemas
|
remoteRelationshipQueryContext = Map.fromList roleBasedRemoteSchemas
|
||||||
roleQueryContext = QueryContext stringifyNum queryType remoteRelationshipQueryContext
|
roleQueryContext = QueryContext stringifyNum queryType remoteRelationshipQueryContext functionPermsCtx
|
||||||
|
|
||||||
runMonadSchema roleName roleQueryContext sources $ do
|
runMonadSchema roleName roleQueryContext sources $ do
|
||||||
let pgSources = mapMaybe unsafeSourceInfo $ toList sources
|
let pgSources = mapMaybe unsafeSourceInfo $ toList sources
|
||||||
@ -252,7 +252,6 @@ buildRoleContext (SQLGenCtx stringifyNum, queryType) sources
|
|||||||
-> [P.FieldParser (P.ParseT Identity) RemoteField]
|
-> [P.FieldParser (P.ParseT Identity) RemoteField]
|
||||||
getMutationRemotes = concatMap (concat . piMutation)
|
getMutationRemotes = concatMap (concat . piMutation)
|
||||||
|
|
||||||
|
|
||||||
buildFullestDBSchema
|
buildFullestDBSchema
|
||||||
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
||||||
=> QueryContext -> SourceCache -> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
=> QueryContext -> SourceCache -> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
||||||
@ -287,16 +286,16 @@ buildFullestDBSchema queryContext sources allActionInfos nonObjectCustomTypes =
|
|||||||
|
|
||||||
buildRelayRoleContext
|
buildRelayRoleContext
|
||||||
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
:: (MonadError QErr m, MonadIO m, MonadUnique m)
|
||||||
=> (SQLGenCtx, GraphQLQueryType) -> SourceCache -> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
=> (SQLGenCtx, GraphQLQueryType, FunctionPermissionsCtx) -> SourceCache -> [ActionInfo 'Postgres] -> NonObjectTypeMap
|
||||||
-> [P.FieldParser (P.ParseT Identity) RemoteField]
|
-> [P.FieldParser (P.ParseT Identity) RemoteField]
|
||||||
-> RoleName
|
-> RoleName
|
||||||
-> m (RoleContext GQLContext)
|
-> m (RoleContext GQLContext)
|
||||||
buildRelayRoleContext (SQLGenCtx stringifyNum, queryType) sources
|
buildRelayRoleContext (SQLGenCtx stringifyNum, queryType, functionPermsCtx) sources
|
||||||
allActionInfos nonObjectCustomTypes mutationRemotes roleName =
|
allActionInfos nonObjectCustomTypes mutationRemotes roleName =
|
||||||
-- TODO: At the time of writing this, remote schema queries are not supported in relay.
|
-- TODO: At the time of writing this, remote schema queries are not supported in relay.
|
||||||
-- When they are supported, we should get do what `buildRoleContext` does. Since, they
|
-- When they are supported, we should get do what `buildRoleContext` does. Since, they
|
||||||
-- are not supported yet, we use `mempty` below for `RemoteRelationshipQueryContext`.
|
-- are not supported yet, we use `mempty` below for `RemoteRelationshipQueryContext`.
|
||||||
let roleQueryContext = QueryContext stringifyNum queryType mempty
|
let roleQueryContext = QueryContext stringifyNum queryType mempty functionPermsCtx
|
||||||
in
|
in
|
||||||
|
|
||||||
runMonadSchema roleName roleQueryContext sources $ do
|
runMonadSchema roleName roleQueryContext sources $ do
|
||||||
@ -432,6 +431,8 @@ buildQueryFields
|
|||||||
-> [FunctionInfo b]
|
-> [FunctionInfo b]
|
||||||
-> m [P.FieldParser n (QueryRootField (UnpreparedValue b))]
|
-> m [P.FieldParser n (QueryRootField (UnpreparedValue b))]
|
||||||
buildQueryFields sourceName sourceConfig tables (takeExposedAs FEAQuery id -> functions) = do
|
buildQueryFields sourceName sourceConfig tables (takeExposedAs FEAQuery id -> functions) = do
|
||||||
|
functionPermsCtx <- asks $ qcFunctionPermsContext . getter
|
||||||
|
roleName <- askRoleName
|
||||||
tableSelectExpParsers <- for tables \(table, _tableInfo) -> do
|
tableSelectExpParsers <- for tables \(table, _tableInfo) -> do
|
||||||
selectPerms <- tableSelectPermissions table
|
selectPerms <- tableSelectPermissions table
|
||||||
customRootFields <- _tcCustomRootFields . _tciCustomConfig . _tiCoreInfo <$> askTableInfo @'Postgres table
|
customRootFields <- _tcCustomRootFields . _tciCustomConfig . _tiCoreInfo <$> askTableInfo @'Postgres table
|
||||||
@ -447,32 +448,38 @@ buildQueryFields sourceName sourceConfig tables (takeExposedAs FEAQuery id -> fu
|
|||||||
, mapMaybeFieldParser (asDbRootField . QDBPrimaryKey) $ selectTableByPk table (fromMaybe pkName $ _tcrfSelectByPk customRootFields) (Just pkDesc) perms
|
, mapMaybeFieldParser (asDbRootField . QDBPrimaryKey) $ selectTableByPk table (fromMaybe pkName $ _tcrfSelectByPk customRootFields) (Just pkDesc) perms
|
||||||
, mapMaybeFieldParser (asDbRootField . QDBAggregation) $ selectTableAggregate table (fromMaybe aggName $ _tcrfSelectAggregate customRootFields) (Just aggDesc) perms
|
, mapMaybeFieldParser (asDbRootField . QDBAggregation) $ selectTableAggregate table (fromMaybe aggName $ _tcrfSelectAggregate customRootFields) (Just aggDesc) perms
|
||||||
]
|
]
|
||||||
functionSelectExpParsers <- for functions \function -> do
|
functionSelectExpParsers <- for functions \function -> runMaybeT $ do
|
||||||
let targetTable = fiReturnType function
|
let targetTable = _fiReturnType function
|
||||||
functionName = fiName function
|
functionName = _fiName function
|
||||||
selectPerms <- tableSelectPermissions targetTable
|
selectPerms <- lift $ tableSelectPermissions targetTable
|
||||||
for selectPerms \perms -> do
|
perms <- hoistMaybe selectPerms
|
||||||
displayName <- functionGraphQLName @b functionName `onLeft` throwError
|
when (functionPermsCtx == FunctionPermissionsManual) $
|
||||||
let functionDesc = G.Description $ "execute function " <> functionName <<> " which returns " <>> targetTable
|
-- see Note [Function Permissions]
|
||||||
aggName = displayName <> $$(G.litName "_aggregate")
|
guard $ roleName == adminRoleName || roleName `elem` (_fiPermissions function)
|
||||||
aggDesc = G.Description $ "execute function " <> functionName <<> " and query aggregates on result of table type " <>> targetTable
|
displayName <- functionGraphQLName @b functionName `onLeft` throwError
|
||||||
catMaybes <$> sequenceA
|
let functionDesc = G.Description $ "execute function " <> functionName <<> " which returns " <>> targetTable
|
||||||
[ requiredFieldParser (asDbRootField . QDBSimple) $ selectFunction function displayName (Just functionDesc) perms
|
aggName = displayName <> $$(G.litName "_aggregate")
|
||||||
, mapMaybeFieldParser (asDbRootField . QDBAggregation) $ selectFunctionAggregate function aggName (Just aggDesc) perms
|
aggDesc = G.Description $ "execute function " <> functionName <<> " and query aggregates on result of table type " <>> targetTable
|
||||||
]
|
catMaybes <$> sequenceA
|
||||||
|
[ requiredFieldParser (asDbRootField . QDBSimple) $ lift $ selectFunction function displayName (Just functionDesc) perms
|
||||||
|
, mapMaybeFieldParser (asDbRootField . QDBAggregation) $ lift $ selectFunctionAggregate function aggName (Just aggDesc) perms
|
||||||
|
]
|
||||||
pure $ (concat . catMaybes) (tableSelectExpParsers <> functionSelectExpParsers)
|
pure $ (concat . catMaybes) (tableSelectExpParsers <> functionSelectExpParsers)
|
||||||
where
|
where
|
||||||
asDbRootField =
|
asDbRootField =
|
||||||
let pgExecCtx = PG._pscExecCtx sourceConfig
|
let pgExecCtx = PG._pscExecCtx sourceConfig
|
||||||
in RFDB sourceName pgExecCtx
|
in RFDB sourceName pgExecCtx
|
||||||
|
|
||||||
mapMaybeFieldParser :: (a -> a') -> m (Maybe (P.FieldParser n a)) -> m (Maybe (P.FieldParser n a'))
|
|
||||||
mapMaybeFieldParser f = fmap $ fmap $ fmap f
|
|
||||||
|
|
||||||
requiredFieldParser
|
requiredFieldParser
|
||||||
:: (Functor n, Functor m)=> (a -> b) -> m (P.FieldParser n a) -> m (Maybe (P.FieldParser n b))
|
:: (Functor n, Functor m)=> (a -> b) -> m (P.FieldParser n a) -> m (Maybe (P.FieldParser n b))
|
||||||
requiredFieldParser f = fmap $ Just . fmap f
|
requiredFieldParser f = fmap $ Just . fmap f
|
||||||
|
|
||||||
|
mapMaybeFieldParser
|
||||||
|
:: (Functor n, Functor m)
|
||||||
|
=> (a -> b)
|
||||||
|
-> m (Maybe (P.FieldParser n a))
|
||||||
|
-> m (Maybe (P.FieldParser n b))
|
||||||
|
mapMaybeFieldParser f = fmap $ fmap $ fmap f
|
||||||
|
|
||||||
-- | Includes remote schema fields and actions
|
-- | Includes remote schema fields and actions
|
||||||
buildActionQueryFields
|
buildActionQueryFields
|
||||||
@ -536,8 +543,8 @@ buildRelayPostgresQueryFields sourceName sourceConfig allTables (takeExposedAs F
|
|||||||
lift $ selectTableConnection table fieldName fieldDesc pkeyColumns selectPerms
|
lift $ selectTableConnection table fieldName fieldDesc pkeyColumns selectPerms
|
||||||
|
|
||||||
functionConnectionFields <- for queryFunctions $ \function -> runMaybeT do
|
functionConnectionFields <- for queryFunctions $ \function -> runMaybeT do
|
||||||
let returnTable = fiReturnType function
|
let returnTable = _fiReturnType function
|
||||||
functionName = fiName function
|
functionName = _fiName function
|
||||||
pkeyColumns <- MaybeT $ (^? tiCoreInfo.tciPrimaryKey._Just.pkColumns)
|
pkeyColumns <- MaybeT $ (^? tiCoreInfo.tciPrimaryKey._Just.pkColumns)
|
||||||
<$> askTableInfo returnTable
|
<$> askTableInfo returnTable
|
||||||
selectPerms <- MaybeT $ tableSelectPermissions returnTable
|
selectPerms <- MaybeT $ tableSelectPermissions returnTable
|
||||||
@ -762,24 +769,34 @@ buildMutationParser
|
|||||||
-> m (Maybe (Parser 'Output n (OMap.InsOrdHashMap G.Name (MutationRootField (UnpreparedValue 'Postgres)))))
|
-> m (Maybe (Parser 'Output n (OMap.InsOrdHashMap G.Name (MutationRootField (UnpreparedValue 'Postgres)))))
|
||||||
buildMutationParser allRemotes allActions nonObjectCustomTypes
|
buildMutationParser allRemotes allActions nonObjectCustomTypes
|
||||||
(takeExposedAs FEAMutation fst -> mutationFunctions) pgMutationFields = do
|
(takeExposedAs FEAMutation fst -> mutationFunctions) pgMutationFields = do
|
||||||
|
roleName <- askRoleName
|
||||||
|
functionPermsCtx <- asks $ qcFunctionPermsContext . getter
|
||||||
-- NOTE: this is basically copied from functionSelectExpParsers body
|
-- NOTE: this is basically copied from functionSelectExpParsers body
|
||||||
functionMutationExpParsers <- for mutationFunctions \(function@FunctionInfo{..}, (sourceName, sourceConfig)) -> do
|
functionMutationExpParsers <-
|
||||||
selectPerms <- tableSelectPermissions fiReturnType
|
case functionPermsCtx of
|
||||||
for selectPerms \perms -> do
|
-- when function permissions are inferred, we don't expose the
|
||||||
displayName <- PG.qualifiedObjectToName fiName
|
-- mutation functions. See Note [Function Permissions]
|
||||||
let functionDesc = G.Description $
|
FunctionPermissionsInferred -> pure []
|
||||||
"execute VOLATILE function " <> fiName <<> " which returns " <>> fiReturnType
|
FunctionPermissionsManual ->
|
||||||
asDbRootField =
|
for mutationFunctions \(function@FunctionInfo{..}, (sourceName, sourceConfig)) -> runMaybeT do
|
||||||
let pgExecCtx = PG._pscExecCtx sourceConfig
|
selectPerms <- lift $ tableSelectPermissions _fiReturnType
|
||||||
in RFDB sourceName pgExecCtx
|
-- A function exposed as mutation must have a function permission
|
||||||
|
-- configured for the role. See Note [Function Permissions]
|
||||||
|
guard $ roleName == adminRoleName || roleName `elem` _fiPermissions
|
||||||
|
perms <- hoistMaybe selectPerms
|
||||||
|
displayName <- PG.qualifiedObjectToName _fiName
|
||||||
|
let functionDesc = G.Description $
|
||||||
|
"execute VOLATILE function " <> _fiName <<> " which returns " <>> _fiReturnType
|
||||||
|
asDbRootField =
|
||||||
|
let pgExecCtx = PG._pscExecCtx sourceConfig
|
||||||
|
in RFDB sourceName pgExecCtx
|
||||||
|
|
||||||
catMaybes <$> sequenceA
|
catMaybes <$> sequenceA
|
||||||
[ requiredFieldParser (asDbRootField . MDBFunction) $
|
[ requiredFieldParser (asDbRootField . MDBFunction) $
|
||||||
selectFunction function displayName (Just functionDesc) perms
|
lift $ selectFunction function displayName (Just functionDesc) perms
|
||||||
-- FWIW: The equivalent of this is possible for mutations; do we want that?:
|
-- FWIW: The equivalent of this is possible for mutations; do we want that?:
|
||||||
-- , mapMaybeFieldParser (asDbRootField . QDBAggregation) $ selectFunctionAggregate function aggName (Just aggDesc) perms
|
-- , mapMaybeFieldParser (asDbRootField . QDBAggregation) $ selectFunctionAggregate function aggName (Just aggDesc) perms
|
||||||
]
|
]
|
||||||
|
|
||||||
actionParsers <- for allActions $ \actionInfo ->
|
actionParsers <- for allActions $ \actionInfo ->
|
||||||
case _adType (_aiDefinition actionInfo) of
|
case _adType (_aiDefinition actionInfo) of
|
||||||
@ -804,7 +821,7 @@ buildMutationParser allRemotes allActions nonObjectCustomTypes
|
|||||||
-- local helpers
|
-- local helpers
|
||||||
|
|
||||||
takeExposedAs :: FunctionExposedAs -> (a -> FunctionInfo b) -> [a] -> [a]
|
takeExposedAs :: FunctionExposedAs -> (a -> FunctionInfo b) -> [a] -> [a]
|
||||||
takeExposedAs x f = filter ((== x) . fiExposedAs . f)
|
takeExposedAs x f = filter ((== x) . _fiExposedAs . f)
|
||||||
|
|
||||||
subscriptionRoot :: G.Name
|
subscriptionRoot :: G.Name
|
||||||
subscriptionRoot = $$(G.litName "subscription_root")
|
subscriptionRoot = $$(G.litName "subscription_root")
|
||||||
|
@ -33,6 +33,7 @@ data QueryContext =
|
|||||||
{ qcStringifyNum :: !Bool
|
{ qcStringifyNum :: !Bool
|
||||||
, qcQueryType :: !ET.GraphQLQueryType
|
, qcQueryType :: !ET.GraphQLQueryType
|
||||||
, qcRemoteRelationshipContext :: !(HashMap RemoteSchemaName (IntrospectionResult, ParsedIntrospection))
|
, qcRemoteRelationshipContext :: !(HashMap RemoteSchemaName (IntrospectionResult, ParsedIntrospection))
|
||||||
|
, qcFunctionPermsContext :: !FunctionPermissionsCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
textToName :: MonadError QErr m => Text -> m G.Name
|
textToName :: MonadError QErr m => Text -> m G.Name
|
||||||
@ -129,4 +130,4 @@ takeValidTables = Map.filterWithKey graphQLTableFilter . Map.filter tableFilter
|
|||||||
takeValidFunctions :: forall b. FunctionCache b -> [FunctionInfo b]
|
takeValidFunctions :: forall b. FunctionCache b -> [FunctionInfo b]
|
||||||
takeValidFunctions = Map.elems . Map.filter functionFilter
|
takeValidFunctions = Map.elems . Map.filter functionFilter
|
||||||
where
|
where
|
||||||
functionFilter = not . isSystemDefined . fiSystemDefined
|
functionFilter = not . isSystemDefined . _fiSystemDefined
|
||||||
|
@ -464,7 +464,7 @@ selectFunction
|
|||||||
-> m (FieldParser n (SelectExp b))
|
-> m (FieldParser n (SelectExp b))
|
||||||
selectFunction function fieldName description selectPermissions = do
|
selectFunction function fieldName description selectPermissions = do
|
||||||
stringifyNum <- asks $ qcStringifyNum . getter
|
stringifyNum <- asks $ qcStringifyNum . getter
|
||||||
let table = fiReturnType function
|
let table = _fiReturnType function
|
||||||
tableArgsParser <- tableArgs table selectPermissions
|
tableArgsParser <- tableArgs table selectPermissions
|
||||||
functionArgsParser <- customSQLFunctionArgs function
|
functionArgsParser <- customSQLFunctionArgs function
|
||||||
selectionSetParser <- tableSelectionList table selectPermissions
|
selectionSetParser <- tableSelectionList table selectPermissions
|
||||||
@ -472,7 +472,7 @@ selectFunction function fieldName description selectPermissions = do
|
|||||||
pure $ P.subselection fieldName description argsParser selectionSetParser
|
pure $ P.subselection fieldName description argsParser selectionSetParser
|
||||||
<&> \((funcArgs, tableArgs'), fields) -> IR.AnnSelectG
|
<&> \((funcArgs, tableArgs'), fields) -> IR.AnnSelectG
|
||||||
{ IR._asnFields = fields
|
{ IR._asnFields = fields
|
||||||
, IR._asnFrom = IR.FromFunction (fiName function) funcArgs Nothing
|
, IR._asnFrom = IR.FromFunction (_fiName function) funcArgs Nothing
|
||||||
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
||||||
, IR._asnArgs = tableArgs'
|
, IR._asnArgs = tableArgs'
|
||||||
, IR._asnStrfyNum = stringifyNum
|
, IR._asnStrfyNum = stringifyNum
|
||||||
@ -492,7 +492,7 @@ selectFunctionAggregate
|
|||||||
-> SelPermInfo b -- ^ select permissions of the target table
|
-> SelPermInfo b -- ^ select permissions of the target table
|
||||||
-> m (Maybe (FieldParser n (AggSelectExp b)))
|
-> m (Maybe (FieldParser n (AggSelectExp b)))
|
||||||
selectFunctionAggregate function fieldName description selectPermissions = runMaybeT do
|
selectFunctionAggregate function fieldName description selectPermissions = runMaybeT do
|
||||||
let table = fiReturnType function
|
let table = _fiReturnType function
|
||||||
stringifyNum <- asks $ qcStringifyNum . getter
|
stringifyNum <- asks $ qcStringifyNum . getter
|
||||||
guard $ spiAllowAgg selectPermissions
|
guard $ spiAllowAgg selectPermissions
|
||||||
tableGQLName <- getTableGQLName @b table
|
tableGQLName <- getTableGQLName @b table
|
||||||
@ -511,7 +511,7 @@ selectFunctionAggregate function fieldName description selectPermissions = runMa
|
|||||||
pure $ P.subselection fieldName description argsParser aggregationParser
|
pure $ P.subselection fieldName description argsParser aggregationParser
|
||||||
<&> \((funcArgs, tableArgs'), fields) -> IR.AnnSelectG
|
<&> \((funcArgs, tableArgs'), fields) -> IR.AnnSelectG
|
||||||
{ IR._asnFields = fields
|
{ IR._asnFields = fields
|
||||||
, IR._asnFrom = IR.FromFunction (fiName function) funcArgs Nothing
|
, IR._asnFrom = IR.FromFunction (_fiName function) funcArgs Nothing
|
||||||
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
||||||
, IR._asnArgs = tableArgs'
|
, IR._asnArgs = tableArgs'
|
||||||
, IR._asnStrfyNum = stringifyNum
|
, IR._asnStrfyNum = stringifyNum
|
||||||
@ -532,7 +532,7 @@ selectFunctionConnection
|
|||||||
-> m (FieldParser n (ConnectionSelectExp 'Postgres))
|
-> m (FieldParser n (ConnectionSelectExp 'Postgres))
|
||||||
selectFunctionConnection function fieldName description pkeyColumns selectPermissions = do
|
selectFunctionConnection function fieldName description pkeyColumns selectPermissions = do
|
||||||
stringifyNum <- asks $ qcStringifyNum . getter
|
stringifyNum <- asks $ qcStringifyNum . getter
|
||||||
let table = fiReturnType function
|
let table = _fiReturnType function
|
||||||
tableConnectionArgsParser <- tableConnectionArgs pkeyColumns table selectPermissions
|
tableConnectionArgsParser <- tableConnectionArgs pkeyColumns table selectPermissions
|
||||||
functionArgsParser <- customSQLFunctionArgs function
|
functionArgsParser <- customSQLFunctionArgs function
|
||||||
selectionSetParser <- tableConnectionSelectionSet table selectPermissions
|
selectionSetParser <- tableConnectionSelectionSet table selectPermissions
|
||||||
@ -544,7 +544,7 @@ selectFunctionConnection function fieldName description pkeyColumns selectPermis
|
|||||||
, IR._csSlice = slice
|
, IR._csSlice = slice
|
||||||
, IR._csSelect = IR.AnnSelectG
|
, IR._csSelect = IR.AnnSelectG
|
||||||
{ IR._asnFields = fields
|
{ IR._asnFields = fields
|
||||||
, IR._asnFrom = IR.FromFunction (fiName function) funcArgs Nothing
|
, IR._asnFrom = IR.FromFunction (_fiName function) funcArgs Nothing
|
||||||
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
, IR._asnPerm = tablePermissionsInfo selectPermissions
|
||||||
, IR._asnArgs = args
|
, IR._asnArgs = args
|
||||||
, IR._asnStrfyNum = stringifyNum
|
, IR._asnStrfyNum = stringifyNum
|
||||||
@ -1130,7 +1130,7 @@ customSQLFunctionArgs
|
|||||||
:: (BackendSchema b, MonadSchema n m, MonadTableInfo r m)
|
:: (BackendSchema b, MonadSchema n m, MonadTableInfo r m)
|
||||||
=> FunctionInfo b
|
=> FunctionInfo b
|
||||||
-> m (InputFieldsParser n (IR.FunctionArgsExpTableRow b (UnpreparedValue b)))
|
-> m (InputFieldsParser n (IR.FunctionArgsExpTableRow b (UnpreparedValue b)))
|
||||||
customSQLFunctionArgs FunctionInfo{..} = functionArgs fiName fiInputArgs
|
customSQLFunctionArgs FunctionInfo{..} = functionArgs _fiName _fiInputArgs
|
||||||
|
|
||||||
-- | Parses the arguments to the underlying sql function of a computed field or
|
-- | Parses the arguments to the underlying sql function of a computed field or
|
||||||
-- a custom function. All arguments to the underlying sql function are parsed
|
-- a custom function. All arguments to the underlying sql function are parsed
|
||||||
|
@ -55,7 +55,7 @@ pgIdenTrigger op trn = pgFmtIdentifier . qualifyTriggerName op $ triggerNameToTx
|
|||||||
qualifyTriggerName op' trn' = "notify_hasura_" <> trn' <> "_" <> tshow op'
|
qualifyTriggerName op' trn' = "notify_hasura_" <> trn' <> "_" <> tshow op'
|
||||||
|
|
||||||
mkAllTriggersQ
|
mkAllTriggersQ
|
||||||
:: (MonadTx m, HasSQLGenCtx m)
|
:: (MonadTx m, HasServerConfigCtx m)
|
||||||
=> TriggerName
|
=> TriggerName
|
||||||
-> QualifiedTable
|
-> QualifiedTable
|
||||||
-> [ColumnInfo 'Postgres]
|
-> [ColumnInfo 'Postgres]
|
||||||
@ -67,7 +67,7 @@ mkAllTriggersQ trn qt allCols fullspec = do
|
|||||||
onJust (tdDelete fullspec) (mkTriggerQ trn qt allCols DELETE)
|
onJust (tdDelete fullspec) (mkTriggerQ trn qt allCols DELETE)
|
||||||
|
|
||||||
mkTriggerQ
|
mkTriggerQ
|
||||||
:: (MonadTx m, HasSQLGenCtx m)
|
:: (MonadTx m, HasServerConfigCtx m)
|
||||||
=> TriggerName
|
=> TriggerName
|
||||||
-> QualifiedTable
|
-> QualifiedTable
|
||||||
-> [ColumnInfo 'Postgres]
|
-> [ColumnInfo 'Postgres]
|
||||||
@ -75,7 +75,7 @@ mkTriggerQ
|
|||||||
-> SubscribeOpSpec
|
-> SubscribeOpSpec
|
||||||
-> m ()
|
-> m ()
|
||||||
mkTriggerQ trn qt@(QualifiedObject schema table) allCols op (SubscribeOpSpec columns payload) = do
|
mkTriggerQ trn qt@(QualifiedObject schema table) allCols op (SubscribeOpSpec columns payload) = do
|
||||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||||
liftTx $ Q.multiQE defaultTxErrorHandler $ Q.fromText . TL.toStrict $
|
liftTx $ Q.multiQE defaultTxErrorHandler $ Q.fromText . TL.toStrict $
|
||||||
let payloadColumns = fromMaybe SubCStar payload
|
let payloadColumns = fromMaybe SubCStar payload
|
||||||
mkQId opVar colInfo = toJSONableExp strfyNum (pgiType colInfo) False $
|
mkQId opVar colInfo = toJSONableExp strfyNum (pgiType colInfo) False $
|
||||||
@ -255,7 +255,7 @@ runCreateEventTriggerQuery q = do
|
|||||||
-- transaction as soon as after @'runCreateEventTriggerQuery' is called and
|
-- transaction as soon as after @'runCreateEventTriggerQuery' is called and
|
||||||
-- in building schema cache.
|
-- in building schema cache.
|
||||||
createPostgresTableEventTrigger
|
createPostgresTableEventTrigger
|
||||||
:: (MonadTx m, HasSQLGenCtx m)
|
:: (MonadTx m, HasServerConfigCtx m)
|
||||||
=> QualifiedTable
|
=> QualifiedTable
|
||||||
-> [ColumnInfo 'Postgres]
|
-> [ColumnInfo 'Postgres]
|
||||||
-> TriggerName
|
-> TriggerName
|
||||||
|
@ -20,7 +20,7 @@ import qualified Data.HashMap.Strict.InsOrd as OMap
|
|||||||
import qualified Data.HashSet as HS
|
import qualified Data.HashSet as HS
|
||||||
import qualified Data.List as L
|
import qualified Data.List as L
|
||||||
|
|
||||||
import Control.Lens ((^?))
|
import Control.Lens ((.~), (^?))
|
||||||
import Data.Aeson
|
import Data.Aeson
|
||||||
|
|
||||||
import Hasura.Metadata.Class
|
import Hasura.Metadata.Class
|
||||||
@ -108,11 +108,38 @@ runReplaceMetadata replaceMetadata = do
|
|||||||
|
|
||||||
pure successMsg
|
pure successMsg
|
||||||
|
|
||||||
|
|
||||||
runExportMetadata
|
runExportMetadata
|
||||||
:: (MetadataM m)
|
:: forall m . ( QErrM m, MetadataM m, HasServerConfigCtx m)
|
||||||
=> ExportMetadata -> m EncJSON
|
=> ExportMetadata -> m EncJSON
|
||||||
runExportMetadata _ =
|
runExportMetadata _ = do
|
||||||
AO.toEncJSON . metadataToOrdJSON <$> getMetadata
|
functionPermsCtx <- _sccFunctionPermsCtx <$> askServerConfigCtx
|
||||||
|
metadata <- getMetadata
|
||||||
|
exportMetadata <- processFunctionPermissions functionPermsCtx metadata
|
||||||
|
pure $ AO.toEncJSON . metadataToOrdJSON $ exportMetadata
|
||||||
|
where
|
||||||
|
-- | when FunctionPermissionsCtx is set to `FunctionPermissionsInferred`
|
||||||
|
-- we don't export the function permissions to the exported metadata
|
||||||
|
-- Note: Please **do not** make this function public as this is only meant
|
||||||
|
-- to be used while exporting metadata i.e. we don't intend on deleting
|
||||||
|
-- any function permissions that may exist in the DB, we simply hide it
|
||||||
|
-- from the user.
|
||||||
|
processFunctionPermissions :: FunctionPermissionsCtx -> Metadata -> m Metadata
|
||||||
|
processFunctionPermissions FunctionPermissionsManual metadata = pure metadata
|
||||||
|
processFunctionPermissions FunctionPermissionsInferred metadata =
|
||||||
|
let sources = OMap.keys $ _metaSources metadata
|
||||||
|
in foldrM clearFunctionPermission metadata sources
|
||||||
|
where
|
||||||
|
clearFunctionPermission sourceName accumulatedMetadata = do
|
||||||
|
let sourceFunctions =
|
||||||
|
OMap.keys . _smFunctions <$> OMap.lookup sourceName (_metaSources accumulatedMetadata)
|
||||||
|
functions <- onNothing sourceFunctions (throw500 "unexpected: runExportMetadata - source not found")
|
||||||
|
pure $ foldr
|
||||||
|
(\functionName md ->
|
||||||
|
((metaSources.ix sourceName.smFunctions.ix functionName.fmPermissions .~ mempty) md))
|
||||||
|
accumulatedMetadata
|
||||||
|
functions
|
||||||
|
|
||||||
|
|
||||||
runReloadMetadata :: (QErrM m, CacheRWM m, MetadataM m) => ReloadMetadata -> m EncJSON
|
runReloadMetadata :: (QErrM m, CacheRWM m, MetadataM m) => ReloadMetadata -> m EncJSON
|
||||||
runReloadMetadata (ReloadMetadata reloadRemoteSchemas reloadSources) = do
|
runReloadMetadata (ReloadMetadata reloadRemoteSchemas reloadSources) = do
|
||||||
@ -177,7 +204,8 @@ purgeMetadataObj = \case
|
|||||||
MTOTrigger trn -> dropEventTriggerInMetadata trn
|
MTOTrigger trn -> dropEventTriggerInMetadata trn
|
||||||
MTOComputedField ccn -> dropComputedFieldInMetadata ccn
|
MTOComputedField ccn -> dropComputedFieldInMetadata ccn
|
||||||
MTORemoteRelationship rn -> dropRemoteRelationshipInMetadata rn
|
MTORemoteRelationship rn -> dropRemoteRelationshipInMetadata rn
|
||||||
SMOFunction qf -> dropFunctionInMetadata source qf
|
SMOFunction qf -> dropFunctionInMetadata source qf
|
||||||
|
SMOFunctionPermission qf rn -> dropFunctionPermissionInMetadata source qf rn
|
||||||
MORemoteSchema rsn -> dropRemoteSchemaInMetadata rsn
|
MORemoteSchema rsn -> dropRemoteSchemaInMetadata rsn
|
||||||
MORemoteSchemaPermissions rsName role -> dropRemoteSchemaPermissionInMetadata rsName role
|
MORemoteSchemaPermissions rsName role -> dropRemoteSchemaPermissionInMetadata rsName role
|
||||||
MOCustomTypes -> clearCustomTypesInMetadata
|
MOCustomTypes -> clearCustomTypesInMetadata
|
||||||
|
@ -64,6 +64,9 @@ instance Arbitrary MetadataVersion where
|
|||||||
instance Arbitrary FunctionMetadata where
|
instance Arbitrary FunctionMetadata where
|
||||||
arbitrary = genericArbitrary
|
arbitrary = genericArbitrary
|
||||||
|
|
||||||
|
instance Arbitrary FunctionPermissionMetadata where
|
||||||
|
arbitrary = genericArbitrary
|
||||||
|
|
||||||
instance Arbitrary PostgresPoolSettings where
|
instance Arbitrary PostgresPoolSettings where
|
||||||
arbitrary = genericArbitrary
|
arbitrary = genericArbitrary
|
||||||
|
|
||||||
|
@ -56,13 +56,13 @@ runAddRemoteSchema env q@(AddRemoteSchemaQuery name defn comment) = do
|
|||||||
runAddRemoteSchemaPermissions
|
runAddRemoteSchemaPermissions
|
||||||
:: ( QErrM m
|
:: ( QErrM m
|
||||||
, CacheRWM m
|
, CacheRWM m
|
||||||
, HasRemoteSchemaPermsCtx m
|
, HasServerConfigCtx m
|
||||||
, MetadataM m
|
, MetadataM m
|
||||||
)
|
)
|
||||||
=> AddRemoteSchemaPermissions
|
=> AddRemoteSchemaPermissions
|
||||||
-> m EncJSON
|
-> m EncJSON
|
||||||
runAddRemoteSchemaPermissions q = do
|
runAddRemoteSchemaPermissions q = do
|
||||||
remoteSchemaPermsCtx <- askRemoteSchemaPermsCtx
|
remoteSchemaPermsCtx <- _sccRemoteSchemaPermsCtx <$> askServerConfigCtx
|
||||||
unless (remoteSchemaPermsCtx == RemoteSchemaPermsEnabled) $ do
|
unless (remoteSchemaPermsCtx == RemoteSchemaPermsEnabled) $ do
|
||||||
throw400 ConstraintViolation
|
throw400 ConstraintViolation
|
||||||
$ "remote schema permissions can only be added when "
|
$ "remote schema permissions can only be added when "
|
||||||
|
@ -105,7 +105,7 @@ isSchemaCacheBuildRequiredRunSQL RunSQL {..} =
|
|||||||
{ TDFA.captureGroups = False }
|
{ TDFA.captureGroups = False }
|
||||||
"\\balter\\b|\\bdrop\\b|\\breplace\\b|\\bcreate function\\b|\\bcomment on\\b")
|
"\\balter\\b|\\bdrop\\b|\\breplace\\b|\\bcreate function\\b|\\bcomment on\\b")
|
||||||
|
|
||||||
runRunSQL :: (MonadIO m, MonadBaseControl IO m, MonadError QErr m, CacheRWM m, HasSQLGenCtx m, MetadataM m)
|
runRunSQL :: (MonadIO m, MonadBaseControl IO m, MonadError QErr m, CacheRWM m, HasServerConfigCtx m, MetadataM m)
|
||||||
=> RunSQL -> m EncJSON
|
=> RunSQL -> m EncJSON
|
||||||
runRunSQL q@RunSQL {..}
|
runRunSQL q@RunSQL {..}
|
||||||
-- see Note [Checking metadata consistency in run_sql]
|
-- see Note [Checking metadata consistency in run_sql]
|
||||||
|
@ -92,8 +92,8 @@ newtype CacheRWT m a
|
|||||||
= CacheRWT (StateT (RebuildableSchemaCache, CacheInvalidations) m a)
|
= CacheRWT (StateT (RebuildableSchemaCache, CacheInvalidations) m a)
|
||||||
deriving
|
deriving
|
||||||
( Functor, Applicative, Monad, MonadIO, MonadUnique, MonadReader r, MonadError e, MonadTx
|
( Functor, Applicative, Monad, MonadIO, MonadUnique, MonadReader r, MonadError e, MonadTx
|
||||||
, UserInfoM, HasHttpManagerM, HasSQLGenCtx, HasSystemDefined, MonadMetadataStorage
|
, UserInfoM, HasHttpManagerM, HasSystemDefined, MonadMetadataStorage
|
||||||
, MonadMetadataStorageQueryAPI, HasRemoteSchemaPermsCtx, Tracing.MonadTrace)
|
, MonadMetadataStorageQueryAPI, Tracing.MonadTrace, HasServerConfigCtx)
|
||||||
|
|
||||||
deriving instance (MonadBase IO m) => MonadBase IO (CacheRWT m)
|
deriving instance (MonadBase IO m) => MonadBase IO (CacheRWT m)
|
||||||
deriving instance (MonadBaseControl IO m) => MonadBaseControl IO (CacheRWT m)
|
deriving instance (MonadBaseControl IO m) => MonadBaseControl IO (CacheRWT m)
|
||||||
@ -110,8 +110,8 @@ instance MonadTrans CacheRWT where
|
|||||||
instance (Monad m) => CacheRM (CacheRWT m) where
|
instance (Monad m) => CacheRM (CacheRWT m) where
|
||||||
askSchemaCache = CacheRWT $ gets (lastBuiltSchemaCache . (^. _1))
|
askSchemaCache = CacheRWT $ gets (lastBuiltSchemaCache . (^. _1))
|
||||||
|
|
||||||
instance (MonadIO m, MonadError QErr m, HasHttpManagerM m, HasSQLGenCtx m
|
instance (MonadIO m, MonadError QErr m, HasHttpManagerM m
|
||||||
, HasRemoteSchemaPermsCtx m, MonadResolveSource m) => CacheRWM (CacheRWT m) where
|
, MonadResolveSource m, HasServerConfigCtx m) => CacheRWM (CacheRWT m) where
|
||||||
buildSchemaCacheWithOptions buildReason invalidations metadata = CacheRWT do
|
buildSchemaCacheWithOptions buildReason invalidations metadata = CacheRWT do
|
||||||
(RebuildableSchemaCache _ invalidationKeys rule, oldInvalidations) <- get
|
(RebuildableSchemaCache _ invalidationKeys rule, oldInvalidations) <- get
|
||||||
let newInvalidationKeys = invalidateKeys invalidations invalidationKeys
|
let newInvalidationKeys = invalidateKeys invalidations invalidationKeys
|
||||||
@ -134,7 +134,8 @@ buildSchemaCacheRule
|
|||||||
-- what we want!
|
-- what we want!
|
||||||
:: ( HasVersion, ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
:: ( HasVersion, ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
||||||
, MonadIO m, MonadUnique m, MonadBaseControl IO m, MonadError QErr m
|
, MonadIO m, MonadUnique m, MonadBaseControl IO m, MonadError QErr m
|
||||||
, MonadReader BuildReason m, HasHttpManagerM m, HasSQLGenCtx m , HasRemoteSchemaPermsCtx m, MonadResolveSource m)
|
, MonadReader BuildReason m, HasHttpManagerM m, MonadResolveSource m
|
||||||
|
, HasServerConfigCtx m)
|
||||||
=> Env.Environment
|
=> Env.Environment
|
||||||
-> (Metadata, InvalidationKeys) `arr` SchemaCache
|
-> (Metadata, InvalidationKeys) `arr` SchemaCache
|
||||||
buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
||||||
@ -216,7 +217,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
buildSource
|
buildSource
|
||||||
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
||||||
, ArrowWriter (Seq CollectedInfo) arr, MonadBaseControl IO m
|
, ArrowWriter (Seq CollectedInfo) arr, MonadBaseControl IO m
|
||||||
, HasSQLGenCtx m, MonadIO m, MonadError QErr m, MonadReader BuildReason m)
|
, HasServerConfigCtx m, MonadIO m, MonadError QErr m, MonadReader BuildReason m)
|
||||||
=> ( SourceMetadata
|
=> ( SourceMetadata
|
||||||
, SourceConfig 'Postgres
|
, SourceConfig 'Postgres
|
||||||
, DBTablesMetadata 'Postgres
|
, DBTablesMetadata 'Postgres
|
||||||
@ -259,7 +260,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
|
|
||||||
-- sql functions
|
-- sql functions
|
||||||
functionCache <- (mapFromL _fmFunction (OMap.elems functions) >- returnA)
|
functionCache <- (mapFromL _fmFunction (OMap.elems functions) >- returnA)
|
||||||
>-> (| Inc.keyed (\_ (FunctionMetadata qf config) -> do
|
>-> (| Inc.keyed (\_ (FunctionMetadata qf config funcPermissions) -> do
|
||||||
let systemDefined = SystemDefined False
|
let systemDefined = SystemDefined False
|
||||||
definition = toJSON $ TrackFunction qf
|
definition = toJSON $ TrackFunction qf
|
||||||
metadataObject = MetadataObject (MOSourceObjId source $ SMOFunction qf) definition
|
metadataObject = MetadataObject (MOSourceObjId source $ SMOFunction qf) definition
|
||||||
@ -269,7 +270,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
(| modifyErrA (do
|
(| modifyErrA (do
|
||||||
let funcDefs = fromMaybe [] $ M.lookup qf pgFunctions
|
let funcDefs = fromMaybe [] $ M.lookup qf pgFunctions
|
||||||
rawfi <- bindErrorA -< handleMultipleFunctions qf funcDefs
|
rawfi <- bindErrorA -< handleMultipleFunctions qf funcDefs
|
||||||
(fi, dep) <- bindErrorA -< mkFunctionInfo source qf systemDefined config rawfi
|
(fi, dep) <- bindErrorA -< mkFunctionInfo source qf systemDefined config funcPermissions rawfi
|
||||||
recordDependencies -< (metadataObject, schemaObject, [dep])
|
recordDependencies -< (metadataObject, schemaObject, [dep])
|
||||||
returnA -< fi)
|
returnA -< fi)
|
||||||
|) addFunctionContext)
|
|) addFunctionContext)
|
||||||
@ -282,7 +283,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, Inc.ArrowCache m arr
|
||||||
, ArrowWriter (Seq CollectedInfo) arr, MonadIO m, MonadUnique m, MonadError QErr m
|
, ArrowWriter (Seq CollectedInfo) arr, MonadIO m, MonadUnique m, MonadError QErr m
|
||||||
, MonadReader BuildReason m, MonadBaseControl IO m
|
, MonadReader BuildReason m, MonadBaseControl IO m
|
||||||
, HasHttpManagerM m, HasSQLGenCtx m, MonadResolveSource m)
|
, HasHttpManagerM m, HasServerConfigCtx m, MonadResolveSource m)
|
||||||
=> (Metadata, Inc.Dependency InvalidationKeys) `arr` BuildOutputs 'Postgres
|
=> (Metadata, Inc.Dependency InvalidationKeys) `arr` BuildOutputs 'Postgres
|
||||||
buildAndCollectInfo = proc (metadata, invalidationKeys) -> do
|
buildAndCollectInfo = proc (metadata, invalidationKeys) -> do
|
||||||
let Metadata sources remoteSchemas collections allowlists
|
let Metadata sources remoteSchemas collections allowlists
|
||||||
@ -466,7 +467,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
buildTableEventTriggers
|
buildTableEventTriggers
|
||||||
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, ArrowWriter (Seq CollectedInfo) arr
|
:: ( ArrowChoice arr, Inc.ArrowDistribute arr, ArrowWriter (Seq CollectedInfo) arr
|
||||||
, Inc.ArrowCache m arr, MonadIO m, MonadError QErr m, MonadBaseControl IO m
|
, Inc.ArrowCache m arr, MonadIO m, MonadError QErr m, MonadBaseControl IO m
|
||||||
, MonadReader BuildReason m, HasSQLGenCtx m)
|
, MonadReader BuildReason m, HasServerConfigCtx m)
|
||||||
=> ( SourceName, SourceConfig 'Postgres, TableCoreInfo 'Postgres
|
=> ( SourceName, SourceConfig 'Postgres, TableCoreInfo 'Postgres
|
||||||
, [EventTriggerConf], Inc.Dependency Inc.InvalidationKey
|
, [EventTriggerConf], Inc.Dependency Inc.InvalidationKey
|
||||||
) `arr` EventTriggerInfoMap
|
) `arr` EventTriggerInfoMap
|
||||||
@ -562,7 +563,7 @@ buildSchemaCacheRule env = proc (metadata, invalidationKeys) -> do
|
|||||||
-- result. If it did, it checks to ensure the changes do not violate any integrity constraints, and
|
-- result. If it did, it checks to ensure the changes do not violate any integrity constraints, and
|
||||||
-- if not, incorporates them into the schema cache.
|
-- if not, incorporates them into the schema cache.
|
||||||
withMetadataCheck
|
withMetadataCheck
|
||||||
:: (MonadIO m, MonadBaseControl IO m, MonadError QErr m, CacheRWM m, HasSQLGenCtx m, MetadataM m)
|
:: (MonadIO m, MonadBaseControl IO m, MonadError QErr m, CacheRWM m, HasServerConfigCtx m, MetadataM m)
|
||||||
=> SourceName -> Bool -> Q.TxAccess -> LazyTxT QErr m a -> m a
|
=> SourceName -> Bool -> Q.TxAccess -> LazyTxT QErr m a -> m a
|
||||||
withMetadataCheck source cascade txAccess action = do
|
withMetadataCheck source cascade txAccess action = do
|
||||||
SourceInfo _ preActionTables preActionFunctions sourceConfig <- askSourceInfo source
|
SourceInfo _ preActionTables preActionFunctions sourceConfig <- askSourceInfo source
|
||||||
|
@ -117,10 +117,9 @@ $(makeLenses ''BuildOutputs)
|
|||||||
-- | Parameters required for schema cache build
|
-- | Parameters required for schema cache build
|
||||||
data CacheBuildParams
|
data CacheBuildParams
|
||||||
= CacheBuildParams
|
= CacheBuildParams
|
||||||
{ _cbpManager :: !HTTP.Manager
|
{ _cbpManager :: !HTTP.Manager
|
||||||
, _cbpSqlGenCtx :: !SQLGenCtx
|
, _cbpSourceResolver :: !SourceResolver
|
||||||
, _cbpRemoteSchemaPermsCtx :: !RemoteSchemaPermsCtx
|
, _cbpServerConfigCtx :: !ServerConfigCtx
|
||||||
, _cbpSourceResolver :: !SourceResolver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | The monad in which @'RebuildableSchemaCache' is being run
|
-- | The monad in which @'RebuildableSchemaCache' is being run
|
||||||
@ -138,11 +137,8 @@ newtype CacheBuild a
|
|||||||
instance HTTP.HasHttpManagerM CacheBuild where
|
instance HTTP.HasHttpManagerM CacheBuild where
|
||||||
askHttpManager = asks _cbpManager
|
askHttpManager = asks _cbpManager
|
||||||
|
|
||||||
instance HasSQLGenCtx CacheBuild where
|
instance HasServerConfigCtx CacheBuild where
|
||||||
askSQLGenCtx = asks _cbpSqlGenCtx
|
askServerConfigCtx = asks _cbpServerConfigCtx
|
||||||
|
|
||||||
instance HasRemoteSchemaPermsCtx CacheBuild where
|
|
||||||
askRemoteSchemaPermsCtx = asks _cbpRemoteSchemaPermsCtx
|
|
||||||
|
|
||||||
instance MonadResolveSource CacheBuild where
|
instance MonadResolveSource CacheBuild where
|
||||||
getSourceResolver = asks _cbpSourceResolver
|
getSourceResolver = asks _cbpSourceResolver
|
||||||
@ -160,17 +156,15 @@ runCacheBuildM
|
|||||||
:: ( MonadIO m
|
:: ( MonadIO m
|
||||||
, MonadError QErr m
|
, MonadError QErr m
|
||||||
, HTTP.HasHttpManagerM m
|
, HTTP.HasHttpManagerM m
|
||||||
, HasSQLGenCtx m
|
, HasServerConfigCtx m
|
||||||
, HasRemoteSchemaPermsCtx m
|
|
||||||
, MonadResolveSource m
|
, MonadResolveSource m
|
||||||
)
|
)
|
||||||
=> CacheBuild a -> m a
|
=> CacheBuild a -> m a
|
||||||
runCacheBuildM m = do
|
runCacheBuildM m = do
|
||||||
params <- CacheBuildParams
|
params <- CacheBuildParams
|
||||||
<$> HTTP.askHttpManager
|
<$> HTTP.askHttpManager
|
||||||
<*> askSQLGenCtx
|
|
||||||
<*> askRemoteSchemaPermsCtx
|
|
||||||
<*> getSourceResolver
|
<*> getSourceResolver
|
||||||
|
<*> askServerConfigCtx
|
||||||
runCacheBuild params m
|
runCacheBuild params m
|
||||||
|
|
||||||
data RebuildableSchemaCache
|
data RebuildableSchemaCache
|
||||||
|
@ -156,6 +156,8 @@ deleteMetadataObject = \case
|
|||||||
deleteObjFn = \case
|
deleteObjFn = \case
|
||||||
SMOTable name -> siTables %~ M.delete name
|
SMOTable name -> siTables %~ M.delete name
|
||||||
SMOFunction name -> siFunctions %~ M.delete name
|
SMOFunction name -> siFunctions %~ M.delete name
|
||||||
|
SMOFunctionPermission functionName role ->
|
||||||
|
siFunctions.ix functionName.fiPermissions %~ HS.delete role
|
||||||
SMOTableObj tableName tableObjectId -> siTables.ix tableName %~ case tableObjectId of
|
SMOTableObj tableName tableObjectId -> siTables.ix tableName %~ case tableObjectId of
|
||||||
MTORel name _ -> tiCoreInfo.tciFieldInfoMap %~ M.delete (fromRel name)
|
MTORel name _ -> tiCoreInfo.tciFieldInfoMap %~ M.delete (fromRel name)
|
||||||
MTOComputedField name -> tiCoreInfo.tciFieldInfoMap %~ M.delete (fromComputedField name)
|
MTOComputedField name -> tiCoreInfo.tciFieldInfoMap %~ M.delete (fromComputedField name)
|
||||||
|
@ -7,7 +7,9 @@ module Hasura.RQL.DDL.Schema.Function where
|
|||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
|
|
||||||
import qualified Control.Monad.Validate as MV
|
import qualified Control.Monad.Validate as MV
|
||||||
|
import qualified Data.HashMap.Strict as Map
|
||||||
import qualified Data.HashMap.Strict.InsOrd as OMap
|
import qualified Data.HashMap.Strict.InsOrd as OMap
|
||||||
|
import qualified Data.HashSet as Set
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Database.PG.Query as Q
|
import qualified Database.PG.Query as Q
|
||||||
@ -24,6 +26,7 @@ import Hasura.EncJSON
|
|||||||
import Hasura.RQL.Types
|
import Hasura.RQL.Types
|
||||||
import Hasura.Server.Utils (englishList, makeReasonMessage)
|
import Hasura.Server.Utils (englishList, makeReasonMessage)
|
||||||
|
|
||||||
|
import Hasura.Session
|
||||||
|
|
||||||
mkFunctionArgs :: Int -> [QualifiedPGType] -> [FunctionArgName] -> [FunctionArg 'Postgres]
|
mkFunctionArgs :: Int -> [QualifiedPGType] -> [FunctionArgName] -> [FunctionArg 'Postgres]
|
||||||
mkFunctionArgs defArgsNo tys argNames =
|
mkFunctionArgs defArgsNo tys argNames =
|
||||||
@ -69,9 +72,10 @@ mkFunctionInfo
|
|||||||
-> QualifiedFunction
|
-> QualifiedFunction
|
||||||
-> SystemDefined
|
-> SystemDefined
|
||||||
-> FunctionConfig
|
-> FunctionConfig
|
||||||
|
-> [FunctionPermissionMetadata]
|
||||||
-> RawFunctionInfo
|
-> RawFunctionInfo
|
||||||
-> m (FunctionInfo 'Postgres, SchemaDependency)
|
-> m (FunctionInfo 'Postgres, SchemaDependency)
|
||||||
mkFunctionInfo source qf systemDefined FunctionConfig{..} rawFuncInfo =
|
mkFunctionInfo source qf systemDefined FunctionConfig{..} permissions rawFuncInfo =
|
||||||
either (throw400 NotSupported . showErrors) pure
|
either (throw400 NotSupported . showErrors) pure
|
||||||
=<< MV.runValidateT validateFunction
|
=<< MV.runValidateT validateFunction
|
||||||
where
|
where
|
||||||
@ -113,8 +117,10 @@ mkFunctionInfo source qf systemDefined FunctionConfig{..} rawFuncInfo =
|
|||||||
inputArguments <- makeInputArguments
|
inputArguments <- makeInputArguments
|
||||||
|
|
||||||
let retTable = typeToTable returnType
|
let retTable = typeToTable returnType
|
||||||
|
functionInfo =
|
||||||
|
FunctionInfo qf systemDefined funVol exposeAs inputArguments retTable descM (Set.fromList $ _fpmRole <$> permissions)
|
||||||
|
|
||||||
pure ( FunctionInfo qf systemDefined funVol exposeAs inputArguments retTable descM
|
pure ( functionInfo
|
||||||
, SchemaDependency (SOSourceObj source $ SOITable retTable) DRTable
|
, SchemaDependency (SOSourceObj source $ SOITable retTable) DRTable
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -183,7 +189,7 @@ trackFunctionP2 sourceName qf config = do
|
|||||||
buildSchemaCacheFor (MOSourceObjId sourceName $ SMOFunction qf)
|
buildSchemaCacheFor (MOSourceObjId sourceName $ SMOFunction qf)
|
||||||
$ MetadataModifier
|
$ MetadataModifier
|
||||||
$ metaSources.ix sourceName.smFunctions
|
$ metaSources.ix sourceName.smFunctions
|
||||||
%~ OMap.insert qf (FunctionMetadata qf config)
|
%~ OMap.insert qf (FunctionMetadata qf config mempty)
|
||||||
pure successMsg
|
pure successMsg
|
||||||
|
|
||||||
handleMultipleFunctions :: (QErrM m) => QualifiedFunction -> [a] -> m a
|
handleMultipleFunctions :: (QErrM m) => QualifiedFunction -> [a] -> m a
|
||||||
@ -240,14 +246,19 @@ instance FromJSON UnTrackFunction where
|
|||||||
UnTrackFunction <$> o .: "table"
|
UnTrackFunction <$> o .: "table"
|
||||||
<*> o .:? "source" .!= defaultSource
|
<*> o .:? "source" .!= defaultSource
|
||||||
|
|
||||||
|
askPGFunctionInfo
|
||||||
|
:: (CacheRM m, MonadError QErr m)
|
||||||
|
=> SourceName -> QualifiedFunction -> m (FunctionInfo 'Postgres)
|
||||||
|
askPGFunctionInfo source functionName = do
|
||||||
|
sourceCache <- scPostgres <$> askSchemaCache
|
||||||
|
unsafeFunctionInfo @'Postgres source functionName sourceCache
|
||||||
|
`onNothing` throw400 NotExists ("function " <> functionName <<> " not found in the cache")
|
||||||
|
|
||||||
runUntrackFunc
|
runUntrackFunc
|
||||||
:: (CacheRWM m, MonadError QErr m, MetadataM m)
|
:: (CacheRWM m, MonadError QErr m, MetadataM m)
|
||||||
=> UnTrackFunction -> m EncJSON
|
=> UnTrackFunction -> m EncJSON
|
||||||
runUntrackFunc (UnTrackFunction functionName sourceName) = do
|
runUntrackFunc (UnTrackFunction functionName sourceName) = do
|
||||||
schemaCache <- askSchemaCache
|
void $ askPGFunctionInfo sourceName functionName
|
||||||
unsafeFunctionInfo @'Postgres sourceName functionName (scPostgres schemaCache)
|
|
||||||
`onNothing` throw400 NotExists ("function not found in cache " <>> functionName)
|
|
||||||
-- Delete function from metadata
|
|
||||||
withNewInconsistentObjsCheck
|
withNewInconsistentObjsCheck
|
||||||
$ buildSchemaCache
|
$ buildSchemaCache
|
||||||
$ dropFunctionInMetadata defaultSource functionName
|
$ dropFunctionInMetadata defaultSource functionName
|
||||||
@ -256,3 +267,93 @@ runUntrackFunc (UnTrackFunction functionName sourceName) = do
|
|||||||
dropFunctionInMetadata :: SourceName -> QualifiedFunction -> MetadataModifier
|
dropFunctionInMetadata :: SourceName -> QualifiedFunction -> MetadataModifier
|
||||||
dropFunctionInMetadata source function = MetadataModifier $
|
dropFunctionInMetadata source function = MetadataModifier $
|
||||||
metaSources.ix source.smFunctions %~ OMap.delete function
|
metaSources.ix source.smFunctions %~ OMap.delete function
|
||||||
|
|
||||||
|
{- Note [Function Permissions]
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
Before we started supporting tracking volatile functions, permissions
|
||||||
|
for a function was inferred from the target table of the function.
|
||||||
|
The rationale behind this is that a stable/immutable function does not
|
||||||
|
modify the database and the data returned by the function is filtered using
|
||||||
|
the permissions that are specified precisely for that data.
|
||||||
|
Now consider mutable/volatile functions, we can't automatically infer whether or
|
||||||
|
not these functions should be exposed for the sole reason that they can modify
|
||||||
|
the database. This necessitates a permission system for functions.
|
||||||
|
So, we introduce a new API `pg_create_function_permission` which will
|
||||||
|
explicitly grant permission to a function to a role. For creating a
|
||||||
|
function permission, the role must have select permissions configured
|
||||||
|
for the target table.
|
||||||
|
Since, this is a breaking change, we enable it only when the graphql-engine
|
||||||
|
is started with
|
||||||
|
`--infer-function-permissions`/HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS set
|
||||||
|
to false (by default, it's set to true).
|
||||||
|
-}
|
||||||
|
|
||||||
|
data CreateFunctionPermission
|
||||||
|
= CreateFunctionPermission
|
||||||
|
{ _afpFunction :: !QualifiedFunction
|
||||||
|
, _afpSource :: !SourceName
|
||||||
|
, _afpRole :: !RoleName
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
$(deriveToJSON hasuraJSON ''CreateFunctionPermission)
|
||||||
|
|
||||||
|
instance FromJSON CreateFunctionPermission where
|
||||||
|
parseJSON v =
|
||||||
|
flip (withObject "CreateFunctionPermission") v $ \o ->
|
||||||
|
CreateFunctionPermission
|
||||||
|
<$> o .: "function"
|
||||||
|
<*> o .:? "source" .!= defaultSource
|
||||||
|
<*> o .: "role"
|
||||||
|
|
||||||
|
runCreateFunctionPermission
|
||||||
|
:: ( CacheRWM m
|
||||||
|
, MonadError QErr m
|
||||||
|
, MetadataM m
|
||||||
|
, HasServerConfigCtx m
|
||||||
|
)
|
||||||
|
=> CreateFunctionPermission
|
||||||
|
-> m EncJSON
|
||||||
|
runCreateFunctionPermission (CreateFunctionPermission functionName source role) = do
|
||||||
|
functionPermsCtx <- _sccFunctionPermsCtx <$> askServerConfigCtx
|
||||||
|
unless (functionPermsCtx == FunctionPermissionsManual) $
|
||||||
|
throw400 NotSupported "function permission can only be created when inferring of function permissions is disabled"
|
||||||
|
sourceCache <- scPostgres <$> askSchemaCache
|
||||||
|
functionInfo <- askPGFunctionInfo source functionName
|
||||||
|
when (role `elem` _fiPermissions functionInfo) $
|
||||||
|
throw400 AlreadyExists $
|
||||||
|
"permission of role "
|
||||||
|
<> role <<> " already exists for function " <> functionName <<> " in source: " <>> source
|
||||||
|
functionTableInfo <-
|
||||||
|
unsafeTableInfo @'Postgres source (_fiReturnType functionInfo) sourceCache
|
||||||
|
`onNothing` throw400 NotExists ("function's return table " <> (_fiReturnType functionInfo) <<> " not found in the cache")
|
||||||
|
unless (role `Map.member` _tiRolePermInfoMap functionTableInfo) $
|
||||||
|
throw400 NotSupported $
|
||||||
|
"function permission can only be added when the function's return table "
|
||||||
|
<> _fiReturnType functionInfo <<> " has select permission configured for role: " <>> role
|
||||||
|
buildSchemaCacheFor (MOSourceObjId source $ SMOFunctionPermission functionName role)
|
||||||
|
$ MetadataModifier
|
||||||
|
$ metaSources.ix source.smFunctions.ix functionName.fmPermissions
|
||||||
|
%~ (:) (FunctionPermissionMetadata role)
|
||||||
|
pure successMsg
|
||||||
|
|
||||||
|
dropFunctionPermissionInMetadata :: SourceName -> QualifiedFunction -> RoleName -> MetadataModifier
|
||||||
|
dropFunctionPermissionInMetadata source function role = MetadataModifier $
|
||||||
|
metaSources.ix source.smFunctions.ix function.fmPermissions %~ filter ((/=) role . _fpmRole)
|
||||||
|
|
||||||
|
type DropFunctionPermission = CreateFunctionPermission
|
||||||
|
|
||||||
|
runDropFunctionPermission
|
||||||
|
:: ( CacheRWM m
|
||||||
|
, MonadError QErr m
|
||||||
|
, MetadataM m
|
||||||
|
)
|
||||||
|
=> DropFunctionPermission
|
||||||
|
-> m EncJSON
|
||||||
|
runDropFunctionPermission (CreateFunctionPermission functionName source role) = do
|
||||||
|
functionInfo <- askPGFunctionInfo source functionName
|
||||||
|
unless (role `elem` _fiPermissions functionInfo) $
|
||||||
|
throw400 NotExists $
|
||||||
|
"permission of role "
|
||||||
|
<> role <<> " does not exist for function " <> functionName <<> " in source: " <>> source
|
||||||
|
buildSchemaCacheFor (MOSourceObjId source $ SMOFunctionPermission functionName role)
|
||||||
|
$ dropFunctionPermissionInMetadata source functionName role
|
||||||
|
pure successMsg
|
||||||
|
@ -69,7 +69,7 @@ saveMetadataToHdbTables (MetadataNoSources tables functions schemas collections
|
|||||||
|
|
||||||
-- sql functions
|
-- sql functions
|
||||||
withPathK "functions" $ indexedForM_ functions $
|
withPathK "functions" $ indexedForM_ functions $
|
||||||
\(FunctionMetadata function config) -> addFunctionToCatalog function config
|
\(FunctionMetadata function config _) -> addFunctionToCatalog function config
|
||||||
|
|
||||||
-- query collections
|
-- query collections
|
||||||
systemDefined <- askSystemDefined
|
systemDefined <- askSystemDefined
|
||||||
@ -407,7 +407,10 @@ fetchMetadataFromHdbTables = liftTx do
|
|||||||
|] () False
|
|] () False
|
||||||
pure $ oMapFromL _fmFunction $
|
pure $ oMapFromL _fmFunction $
|
||||||
flip map l $ \(sn, fn, Q.AltJ config) ->
|
flip map l $ \(sn, fn, Q.AltJ config) ->
|
||||||
FunctionMetadata (QualifiedObject sn fn) config
|
-- function permissions were only introduced post 43rd
|
||||||
|
-- migration, so it's impossible we get any permissions
|
||||||
|
-- here
|
||||||
|
FunctionMetadata (QualifiedObject sn fn) config []
|
||||||
|
|
||||||
fetchRemoteSchemas =
|
fetchRemoteSchemas =
|
||||||
map fromRow <$> Q.listQE defaultTxErrorHandler
|
map fromRow <$> Q.listQE defaultTxErrorHandler
|
||||||
|
@ -93,7 +93,7 @@ validateDeleteQ query = do
|
|||||||
|
|
||||||
runDelete
|
runDelete
|
||||||
:: ( HasVersion, QErrM m, UserInfoM m, CacheRM m
|
:: ( HasVersion, QErrM m, UserInfoM m, CacheRM m
|
||||||
, HasSQLGenCtx m, MonadIO m
|
, HasServerConfigCtx m, MonadIO m
|
||||||
, Tracing.MonadTrace m, MonadBaseControl IO m
|
, Tracing.MonadTrace m, MonadBaseControl IO m
|
||||||
)
|
)
|
||||||
=> Env.Environment
|
=> Env.Environment
|
||||||
@ -101,7 +101,7 @@ runDelete
|
|||||||
-> m EncJSON
|
-> m EncJSON
|
||||||
runDelete env q = do
|
runDelete env q = do
|
||||||
sourceConfig <- askSourceConfig (doSource q)
|
sourceConfig <- askSourceConfig (doSource q)
|
||||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||||
validateDeleteQ q
|
validateDeleteQ q
|
||||||
>>= runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite
|
>>= runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite
|
||||||
. execDeleteQuery env strfyNum Nothing
|
. execDeleteQuery env strfyNum Nothing
|
||||||
|
@ -208,7 +208,7 @@ convInsQ query = do
|
|||||||
|
|
||||||
runInsert
|
runInsert
|
||||||
:: ( HasVersion, QErrM m, UserInfoM m
|
:: ( HasVersion, QErrM m, UserInfoM m
|
||||||
, CacheRM m, HasSQLGenCtx m
|
, CacheRM m, HasServerConfigCtx m
|
||||||
, MonadIO m, Tracing.MonadTrace m
|
, MonadIO m, Tracing.MonadTrace m
|
||||||
, MonadBaseControl IO m
|
, MonadBaseControl IO m
|
||||||
)
|
)
|
||||||
@ -216,7 +216,7 @@ runInsert
|
|||||||
runInsert env q = do
|
runInsert env q = do
|
||||||
sourceConfig <- askSourceConfig (iqSource q)
|
sourceConfig <- askSourceConfig (iqSource q)
|
||||||
res <- convInsQ q
|
res <- convInsQ q
|
||||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||||
runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite $
|
runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite $
|
||||||
execInsertQuery env strfyNum Nothing res
|
execInsertQuery env strfyNum Nothing res
|
||||||
|
|
||||||
|
@ -23,15 +23,15 @@ import Hasura.Backends.Postgres.SQL.Value
|
|||||||
import Hasura.Backends.Postgres.Translate.BoolExp
|
import Hasura.Backends.Postgres.Translate.BoolExp
|
||||||
import Hasura.Backends.Postgres.Translate.Column
|
import Hasura.Backends.Postgres.Translate.Column
|
||||||
import Hasura.RQL.Types
|
import Hasura.RQL.Types
|
||||||
import Hasura.Session
|
|
||||||
import Hasura.SQL.Types
|
import Hasura.SQL.Types
|
||||||
|
import Hasura.Session
|
||||||
|
|
||||||
|
|
||||||
newtype DMLP1T m a
|
newtype DMLP1T m a
|
||||||
= DMLP1T { unDMLP1T :: StateT (DS.Seq Q.PrepArg) m a }
|
= DMLP1T { unDMLP1T :: StateT (DS.Seq Q.PrepArg) m a }
|
||||||
deriving ( Functor, Applicative, Monad, MonadTrans
|
deriving ( Functor, Applicative, Monad, MonadTrans
|
||||||
, MonadState (DS.Seq Q.PrepArg), MonadError e
|
, MonadState (DS.Seq Q.PrepArg), MonadError e
|
||||||
, SourceM, TableCoreInfoRM b, TableInfoRM b, CacheRM, UserInfoM, HasSQLGenCtx
|
, SourceM, TableCoreInfoRM b, TableInfoRM b, CacheRM, UserInfoM, HasServerConfigCtx
|
||||||
)
|
)
|
||||||
|
|
||||||
runDMLP1T :: DMLP1T m a -> m (a, DS.Seq Q.PrepArg)
|
runDMLP1T :: DMLP1T m a -> m (a, DS.Seq Q.PrepArg)
|
||||||
|
@ -193,7 +193,7 @@ convOrderByElem sessVarBldr (flds, spi) = \case
|
|||||||
throw400 UnexpectedPayload (mconcat [ fldName <<> " is a remote field" ])
|
throw400 UnexpectedPayload (mconcat [ fldName <<> " is a remote field" ])
|
||||||
|
|
||||||
convSelectQ
|
convSelectQ
|
||||||
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasSQLGenCtx m)
|
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasServerConfigCtx m)
|
||||||
=> TableName 'Postgres
|
=> TableName 'Postgres
|
||||||
-> FieldInfoMap (FieldInfo 'Postgres) -- Table information of current table
|
-> FieldInfoMap (FieldInfo 'Postgres) -- Table information of current table
|
||||||
-> SelPermInfo 'Postgres -- Additional select permission info
|
-> SelPermInfo 'Postgres -- Additional select permission info
|
||||||
@ -238,7 +238,7 @@ convSelectQ table fieldInfoMap selPermInfo selQ sessVarBldr prepValBldr = do
|
|||||||
tabArgs = SelectArgs wClause annOrdByM mQueryLimit
|
tabArgs = SelectArgs wClause annOrdByM mQueryLimit
|
||||||
(S.intToSQLExp <$> mQueryOffset) Nothing
|
(S.intToSQLExp <$> mQueryOffset) Nothing
|
||||||
|
|
||||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||||
return $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum
|
return $ AnnSelectG annFlds tabFrom tabPerm tabArgs strfyNum
|
||||||
|
|
||||||
where
|
where
|
||||||
@ -259,7 +259,7 @@ convExtSimple fieldInfoMap selPermInfo pgCol = do
|
|||||||
relWhenPGErr = "relationships have to be expanded"
|
relWhenPGErr = "relationships have to be expanded"
|
||||||
|
|
||||||
convExtRel
|
convExtRel
|
||||||
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasSQLGenCtx m)
|
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasServerConfigCtx m)
|
||||||
=> FieldInfoMap (FieldInfo 'Postgres)
|
=> FieldInfoMap (FieldInfo 'Postgres)
|
||||||
-> RelName
|
-> RelName
|
||||||
-> Maybe RelName
|
-> Maybe RelName
|
||||||
@ -297,7 +297,7 @@ convExtRel fieldInfoMap relName mAlias selQ sessVarBldr prepValBldr = do
|
|||||||
]
|
]
|
||||||
|
|
||||||
convSelectQuery
|
convSelectQuery
|
||||||
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasSQLGenCtx m)
|
:: (UserInfoM m, QErrM m, TableInfoRM 'Postgres m, HasServerConfigCtx m)
|
||||||
=> SessVarBldr 'Postgres m
|
=> SessVarBldr 'Postgres m
|
||||||
-> (ColumnType 'Postgres -> Value -> m S.SQLExp)
|
-> (ColumnType 'Postgres -> Value -> m S.SQLExp)
|
||||||
-> SelectQuery
|
-> SelectQuery
|
||||||
@ -318,7 +318,7 @@ selectP2 jsonAggSelect (sel, p) =
|
|||||||
selectSQL = toSQL $ mkSQLSelect jsonAggSelect sel
|
selectSQL = toSQL $ mkSQLSelect jsonAggSelect sel
|
||||||
|
|
||||||
phaseOne
|
phaseOne
|
||||||
:: (QErrM m, UserInfoM m, CacheRM m, HasSQLGenCtx m)
|
:: (QErrM m, UserInfoM m, CacheRM m, HasServerConfigCtx m)
|
||||||
=> SelectQuery -> m (AnnSimpleSel 'Postgres, DS.Seq Q.PrepArg)
|
=> SelectQuery -> m (AnnSimpleSel 'Postgres, DS.Seq Q.PrepArg)
|
||||||
phaseOne query = do
|
phaseOne query = do
|
||||||
let sourceName = getSourceDMLQuery query
|
let sourceName = getSourceDMLQuery query
|
||||||
@ -332,7 +332,7 @@ phaseTwo =
|
|||||||
|
|
||||||
runSelect
|
runSelect
|
||||||
:: ( QErrM m, UserInfoM m, CacheRM m
|
:: ( QErrM m, UserInfoM m, CacheRM m
|
||||||
, HasSQLGenCtx m, MonadIO m, MonadBaseControl IO m
|
, HasServerConfigCtx m, MonadIO m, MonadBaseControl IO m
|
||||||
, Tracing.MonadTrace m
|
, Tracing.MonadTrace m
|
||||||
)
|
)
|
||||||
=> SelectQuery -> m EncJSON
|
=> SelectQuery -> m EncJSON
|
||||||
|
@ -186,13 +186,13 @@ validateUpdateQuery query = do
|
|||||||
|
|
||||||
runUpdate
|
runUpdate
|
||||||
:: ( HasVersion, QErrM m, UserInfoM m, CacheRM m
|
:: ( HasVersion, QErrM m, UserInfoM m, CacheRM m
|
||||||
, HasSQLGenCtx m, MonadBaseControl IO m
|
, HasServerConfigCtx m, MonadBaseControl IO m
|
||||||
, MonadIO m, Tracing.MonadTrace m
|
, MonadIO m, Tracing.MonadTrace m
|
||||||
)
|
)
|
||||||
=> Env.Environment -> UpdateQuery -> m EncJSON
|
=> Env.Environment -> UpdateQuery -> m EncJSON
|
||||||
runUpdate env q = do
|
runUpdate env q = do
|
||||||
sourceConfig <- askSourceConfig (uqSource q)
|
sourceConfig <- askSourceConfig (uqSource q)
|
||||||
strfyNum <- stringifyNum <$> askSQLGenCtx
|
strfyNum <- stringifyNum . _sccSQLGenCtx <$> askServerConfigCtx
|
||||||
validateUpdateQuery q
|
validateUpdateQuery q
|
||||||
>>= runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite
|
>>= runQueryLazyTx (_pscExecCtx sourceConfig) Q.ReadWrite
|
||||||
. execUpdateQuery env strfyNum Nothing
|
. execUpdateQuery env strfyNum Nothing
|
||||||
|
@ -2,10 +2,10 @@ module Hasura.RQL.Types
|
|||||||
( MonadTx(..)
|
( MonadTx(..)
|
||||||
|
|
||||||
, SQLGenCtx(..)
|
, SQLGenCtx(..)
|
||||||
, HasSQLGenCtx(..)
|
|
||||||
|
|
||||||
, RemoteSchemaPermsCtx(..)
|
, RemoteSchemaPermsCtx(..)
|
||||||
, HasRemoteSchemaPermsCtx(..)
|
|
||||||
|
, ServerConfigCtx(..)
|
||||||
|
, HasServerConfigCtx(..)
|
||||||
|
|
||||||
, HasSystemDefined(..)
|
, HasSystemDefined(..)
|
||||||
, HasSystemDefinedT
|
, HasSystemDefinedT
|
||||||
@ -110,6 +110,42 @@ askTabInfoSource
|
|||||||
askTabInfoSource tableName = do
|
askTabInfoSource tableName = do
|
||||||
lookupTableInfo tableName >>= (`onNothing` throwTableDoesNotExist tableName)
|
lookupTableInfo tableName >>= (`onNothing` throwTableDoesNotExist tableName)
|
||||||
|
|
||||||
|
data ServerConfigCtx
|
||||||
|
= ServerConfigCtx
|
||||||
|
{ _sccFunctionPermsCtx :: !FunctionPermissionsCtx
|
||||||
|
, _sccRemoteSchemaPermsCtx :: !RemoteSchemaPermsCtx
|
||||||
|
, _sccSQLGenCtx :: !SQLGenCtx
|
||||||
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
|
class (Monad m) => HasServerConfigCtx m where
|
||||||
|
askServerConfigCtx :: m ServerConfigCtx
|
||||||
|
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (ReaderT r m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (StateT s m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (Monoid w, HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (WriterT w m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (TableCoreCacheRT b m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (TraceT m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (MetadataT m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m)
|
||||||
|
=> HasServerConfigCtx (LazyTxT QErr m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m) => HasServerConfigCtx (Q.TxET QErr m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
instance (HasServerConfigCtx m) => HasServerConfigCtx (TableCacheRT b m) where
|
||||||
|
askServerConfigCtx = lift askServerConfigCtx
|
||||||
|
|
||||||
data RemoteSchemaPermsCtx
|
data RemoteSchemaPermsCtx
|
||||||
= RemoteSchemaPermsEnabled
|
= RemoteSchemaPermsEnabled
|
||||||
| RemoteSchemaPermsDisabled
|
| RemoteSchemaPermsDisabled
|
||||||
@ -127,53 +163,6 @@ instance ToJSON RemoteSchemaPermsCtx where
|
|||||||
RemoteSchemaPermsEnabled -> "true"
|
RemoteSchemaPermsEnabled -> "true"
|
||||||
RemoteSchemaPermsDisabled -> "false"
|
RemoteSchemaPermsDisabled -> "false"
|
||||||
|
|
||||||
class (Monad m) => HasRemoteSchemaPermsCtx m where
|
|
||||||
askRemoteSchemaPermsCtx :: m RemoteSchemaPermsCtx
|
|
||||||
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (ReaderT r m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (StateT s m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (Monoid w, HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (WriterT w m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (TableCoreCacheRT b m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (TraceT m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (MetadataT m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
instance (HasRemoteSchemaPermsCtx m)
|
|
||||||
=> HasRemoteSchemaPermsCtx (LazyTxT QErr m) where
|
|
||||||
askRemoteSchemaPermsCtx = lift askRemoteSchemaPermsCtx
|
|
||||||
|
|
||||||
class (Monad m) => HasSQLGenCtx m where
|
|
||||||
askSQLGenCtx :: m SQLGenCtx
|
|
||||||
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (ReaderT r m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (StateT s m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (Monoid w, HasSQLGenCtx m) => HasSQLGenCtx (WriterT w m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (TableCoreCacheRT b m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (TraceT m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (MetadataT m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (Q.TxET QErr m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (LazyTxT QErr m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
instance (HasSQLGenCtx m) => HasSQLGenCtx (TableCacheRT b m) where
|
|
||||||
askSQLGenCtx = lift askSQLGenCtx
|
|
||||||
|
|
||||||
class (Monad m) => HasSystemDefined m where
|
class (Monad m) => HasSystemDefined m where
|
||||||
askSystemDefined :: m SystemDefined
|
askSystemDefined :: m SystemDefined
|
||||||
|
|
||||||
@ -189,7 +178,7 @@ instance (HasSystemDefined m) => HasSystemDefined (TraceT m) where
|
|||||||
newtype HasSystemDefinedT m a
|
newtype HasSystemDefinedT m a
|
||||||
= HasSystemDefinedT { unHasSystemDefinedT :: ReaderT SystemDefined m a }
|
= HasSystemDefinedT { unHasSystemDefinedT :: ReaderT SystemDefined m a }
|
||||||
deriving ( Functor, Applicative, Monad, MonadTrans, MonadIO, MonadUnique, MonadError e, MonadTx
|
deriving ( Functor, Applicative, Monad, MonadTrans, MonadIO, MonadUnique, MonadError e, MonadTx
|
||||||
, HasHttpManagerM, HasSQLGenCtx, SourceM, TableCoreInfoRM b, CacheRM, UserInfoM, HasRemoteSchemaPermsCtx)
|
, HasHttpManagerM, SourceM, TableCoreInfoRM b, CacheRM, UserInfoM, HasServerConfigCtx)
|
||||||
|
|
||||||
runHasSystemDefinedT :: SystemDefined -> HasSystemDefinedT m a -> m a
|
runHasSystemDefinedT :: SystemDefined -> HasSystemDefinedT m a -> m a
|
||||||
runHasSystemDefinedT systemDefined = flip runReaderT systemDefined . unHasSystemDefinedT
|
runHasSystemDefinedT systemDefined = flip runReaderT systemDefined . unHasSystemDefinedT
|
||||||
|
@ -2,6 +2,7 @@ module Hasura.RQL.Types.Function where
|
|||||||
|
|
||||||
import Hasura.Prelude
|
import Hasura.Prelude
|
||||||
|
|
||||||
|
import qualified Data.HashSet as Set
|
||||||
import qualified Data.Sequence as Seq
|
import qualified Data.Sequence as Seq
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ import qualified Hasura.Backends.Postgres.SQL.Types as PG
|
|||||||
import Hasura.Incremental (Cacheable)
|
import Hasura.Incremental (Cacheable)
|
||||||
import Hasura.RQL.Types.Common
|
import Hasura.RQL.Types.Common
|
||||||
import Hasura.SQL.Backend
|
import Hasura.SQL.Backend
|
||||||
|
import Hasura.Session
|
||||||
|
|
||||||
-- | https://www.postgresql.org/docs/current/xfunc-volatility.html
|
-- | https://www.postgresql.org/docs/current/xfunc-volatility.html
|
||||||
data FunctionVolatility
|
data FunctionVolatility
|
||||||
@ -86,28 +87,31 @@ $(deriveJSON
|
|||||||
-- | Tracked SQL function metadata. See 'mkFunctionInfo'.
|
-- | Tracked SQL function metadata. See 'mkFunctionInfo'.
|
||||||
data FunctionInfo (b :: BackendType)
|
data FunctionInfo (b :: BackendType)
|
||||||
= FunctionInfo
|
= FunctionInfo
|
||||||
{ fiName :: !(FunctionName b)
|
{ _fiName :: !(FunctionName b)
|
||||||
, fiSystemDefined :: !SystemDefined
|
, _fiSystemDefined :: !SystemDefined
|
||||||
, fiVolatility :: !FunctionVolatility
|
, _fiVolatility :: !FunctionVolatility
|
||||||
, fiExposedAs :: !FunctionExposedAs
|
, _fiExposedAs :: !FunctionExposedAs
|
||||||
-- ^ In which part of the schema should this function be exposed?
|
-- ^ In which part of the schema should this function be exposed?
|
||||||
--
|
--
|
||||||
-- See 'mkFunctionInfo' and '_fcExposedAs'.
|
-- See 'mkFunctionInfo' and '_fcExposedAs'.
|
||||||
, fiInputArgs :: !(Seq.Seq (FunctionInputArgument b))
|
, _fiInputArgs :: !(Seq.Seq (FunctionInputArgument b))
|
||||||
, fiReturnType :: !(TableName b)
|
, _fiReturnType :: !(TableName b)
|
||||||
-- ^ NOTE: when a table is created, a new composite type of the same name is
|
-- ^ NOTE: when a table is created, a new composite type of the same name is
|
||||||
-- automatically created; so strictly speaking this field means "the function
|
-- automatically created; so strictly speaking this field means "the function
|
||||||
-- returns the composite type corresponding to this table".
|
-- returns the composite type corresponding to this table".
|
||||||
, fiDescription :: !(Maybe PG.PGDescription) -- FIXME: make generic
|
, _fiDescription :: !(Maybe PG.PGDescription) -- FIXME: make generic
|
||||||
|
, _fiPermissions :: !(Set.HashSet RoleName)
|
||||||
|
-- ^ Roles to which the function is accessible
|
||||||
} deriving (Generic)
|
} deriving (Generic)
|
||||||
deriving instance Backend b => Show (FunctionInfo b)
|
deriving instance Backend b => Show (FunctionInfo b)
|
||||||
deriving instance Backend b => Eq (FunctionInfo b)
|
deriving instance Backend b => Eq (FunctionInfo b)
|
||||||
instance (Backend b) => ToJSON (FunctionInfo b) where
|
instance (Backend b) => ToJSON (FunctionInfo b) where
|
||||||
toJSON = genericToJSON hasuraJSON
|
toJSON = genericToJSON hasuraJSON
|
||||||
|
$(makeLenses ''FunctionInfo)
|
||||||
|
|
||||||
getInputArgs :: FunctionInfo b -> Seq.Seq (FunctionArg b)
|
getInputArgs :: FunctionInfo b -> Seq.Seq (FunctionArg b)
|
||||||
getInputArgs =
|
getInputArgs =
|
||||||
Seq.fromList . mapMaybe (^? _IAUserProvided) . toList . fiInputArgs
|
Seq.fromList . mapMaybe (^? _IAUserProvided) . toList . _fiInputArgs
|
||||||
|
|
||||||
type FunctionCache b = HashMap (FunctionName b) (FunctionInfo b) -- info of all functions
|
type FunctionCache b = HashMap (FunctionName b) (FunctionInfo b) -- info of all functions
|
||||||
|
|
||||||
@ -172,3 +176,20 @@ instance Cacheable RawFunctionInfo
|
|||||||
$(deriveJSON hasuraJSON ''RawFunctionInfo)
|
$(deriveJSON hasuraJSON ''RawFunctionInfo)
|
||||||
|
|
||||||
type PostgresFunctionsMetadata = HashMap PG.QualifiedFunction [RawFunctionInfo]
|
type PostgresFunctionsMetadata = HashMap PG.QualifiedFunction [RawFunctionInfo]
|
||||||
|
|
||||||
|
data FunctionPermissionsCtx
|
||||||
|
= FunctionPermissionsInferred
|
||||||
|
| FunctionPermissionsManual
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
instance FromJSON FunctionPermissionsCtx where
|
||||||
|
parseJSON = withText "FunctionPermissionsCtx" $ \t ->
|
||||||
|
case T.toLower t of
|
||||||
|
"true" -> pure FunctionPermissionsInferred
|
||||||
|
"false" -> pure FunctionPermissionsManual
|
||||||
|
_ -> fail "infer_function_permissions should be a boolean value"
|
||||||
|
|
||||||
|
instance ToJSON FunctionPermissionsCtx where
|
||||||
|
toJSON = \case
|
||||||
|
FunctionPermissionsInferred -> Bool True
|
||||||
|
FunctionPermissionsManual -> Bool False
|
||||||
|
@ -51,6 +51,7 @@ instance Hashable TableMetadataObjId
|
|||||||
data SourceMetadataObjId
|
data SourceMetadataObjId
|
||||||
= SMOTable !QualifiedTable
|
= SMOTable !QualifiedTable
|
||||||
| SMOFunction !QualifiedFunction
|
| SMOFunction !QualifiedFunction
|
||||||
|
| SMOFunctionPermission !QualifiedFunction !RoleName
|
||||||
| SMOTableObj !QualifiedTable !TableMetadataObjId
|
| SMOTableObj !QualifiedTable !TableMetadataObjId
|
||||||
deriving (Show, Eq, Generic)
|
deriving (Show, Eq, Generic)
|
||||||
instance Hashable SourceMetadataObjId
|
instance Hashable SourceMetadataObjId
|
||||||
@ -76,6 +77,7 @@ moiTypeName = \case
|
|||||||
MOSourceObjId _ sourceObjId -> case sourceObjId of
|
MOSourceObjId _ sourceObjId -> case sourceObjId of
|
||||||
SMOTable _ -> "table"
|
SMOTable _ -> "table"
|
||||||
SMOFunction _ -> "function"
|
SMOFunction _ -> "function"
|
||||||
|
SMOFunctionPermission _ _ -> "function_permission"
|
||||||
SMOTableObj _ tableObjectId -> case tableObjectId of
|
SMOTableObj _ tableObjectId -> case tableObjectId of
|
||||||
MTORel _ relType -> relTypeToTxt relType <> "_relation"
|
MTORel _ relType -> relTypeToTxt relType <> "_relation"
|
||||||
MTOPerm _ permType -> permTypeToCode permType <> "_permission"
|
MTOPerm _ permType -> permTypeToCode permType <> "_permission"
|
||||||
@ -96,6 +98,9 @@ moiName objectId = moiTypeName objectId <> " " <> case objectId of
|
|||||||
MOSourceObjId source sourceObjId -> case sourceObjId of
|
MOSourceObjId source sourceObjId -> case sourceObjId of
|
||||||
SMOTable name -> toTxt name <> " in source " <> toTxt source
|
SMOTable name -> toTxt name <> " in source " <> toTxt source
|
||||||
SMOFunction name -> toTxt name <> " in source " <> toTxt source
|
SMOFunction name -> toTxt name <> " in source " <> toTxt source
|
||||||
|
SMOFunctionPermission functionName roleName ->
|
||||||
|
toTxt roleName <> " permission for function "
|
||||||
|
<> toTxt functionName <> " in source " <> toTxt source
|
||||||
SMOTableObj tableName tableObjectId ->
|
SMOTableObj tableName tableObjectId ->
|
||||||
let tableObjectName = case tableObjectId of
|
let tableObjectName = case tableObjectId of
|
||||||
MTORel name _ -> toTxt name
|
MTORel name _ -> toTxt name
|
||||||
@ -322,20 +327,30 @@ instance FromJSON TableMetadata where
|
|||||||
, cfKey, rrKey
|
, cfKey, rrKey
|
||||||
]
|
]
|
||||||
|
|
||||||
|
newtype FunctionPermissionMetadata
|
||||||
|
= FunctionPermissionMetadata
|
||||||
|
{ _fpmRole :: RoleName
|
||||||
|
} deriving (Show, Eq, Generic)
|
||||||
|
instance Cacheable FunctionPermissionMetadata
|
||||||
|
$(makeLenses ''FunctionPermissionMetadata)
|
||||||
|
$(deriveJSON hasuraJSON ''FunctionPermissionMetadata)
|
||||||
|
|
||||||
data FunctionMetadata
|
data FunctionMetadata
|
||||||
= FunctionMetadata
|
= FunctionMetadata
|
||||||
{ _fmFunction :: !QualifiedFunction
|
{ _fmFunction :: !QualifiedFunction
|
||||||
, _fmConfiguration :: !FunctionConfig
|
, _fmConfiguration :: !FunctionConfig
|
||||||
|
, _fmPermissions :: ![FunctionPermissionMetadata]
|
||||||
} deriving (Show, Eq, Generic)
|
} deriving (Show, Eq, Generic)
|
||||||
instance Cacheable FunctionMetadata
|
instance Cacheable FunctionMetadata
|
||||||
$(makeLenses ''FunctionMetadata)
|
$(makeLenses ''FunctionMetadata)
|
||||||
$(deriveToJSON hasuraJSON ''FunctionMetadata)
|
$(deriveToJSON hasuraJSON ''FunctionMetadata)
|
||||||
|
|
||||||
instance FromJSON FunctionMetadata where
|
instance FromJSON FunctionMetadata where
|
||||||
parseJSON = withObject "Object" $ \o ->
|
parseJSON = withObject "FunctionMetadata" $ \o ->
|
||||||
FunctionMetadata
|
FunctionMetadata
|
||||||
<$> o .: "function"
|
<$> o .: "function"
|
||||||
<*> o .:? "configuration" .!= emptyFunctionConfig
|
<*> o .:? "configuration" .!= emptyFunctionConfig
|
||||||
|
<*> o .:? "permissions" .!= []
|
||||||
|
|
||||||
type Tables = InsOrdHashMap QualifiedTable TableMetadata
|
type Tables = InsOrdHashMap QualifiedTable TableMetadata
|
||||||
type Functions = InsOrdHashMap QualifiedFunction FunctionMetadata
|
type Functions = InsOrdHashMap QualifiedFunction FunctionMetadata
|
||||||
@ -454,7 +469,7 @@ instance FromJSON MetadataNoSources where
|
|||||||
tables <- oMapFromL _tmTable <$> o .: "tables"
|
tables <- oMapFromL _tmTable <$> o .: "tables"
|
||||||
functionList <- o .:? "functions" .!= []
|
functionList <- o .:? "functions" .!= []
|
||||||
let functions = OM.fromList $ flip map functionList $
|
let functions = OM.fromList $ flip map functionList $
|
||||||
\function -> (function, FunctionMetadata function emptyFunctionConfig)
|
\function -> (function, FunctionMetadata function emptyFunctionConfig mempty)
|
||||||
pure (tables, functions)
|
pure (tables, functions)
|
||||||
MVVersion2 -> do
|
MVVersion2 -> do
|
||||||
tables <- oMapFromL _tmTable <$> o .: "tables"
|
tables <- oMapFromL _tmTable <$> o .: "tables"
|
||||||
@ -652,9 +667,13 @@ metadataToOrdJSON ( Metadata
|
|||||||
|
|
||||||
functionMetadataToOrdJSON :: FunctionMetadata -> AO.Value
|
functionMetadataToOrdJSON :: FunctionMetadata -> AO.Value
|
||||||
functionMetadataToOrdJSON FunctionMetadata{..} =
|
functionMetadataToOrdJSON FunctionMetadata{..} =
|
||||||
AO.object $ [("function", AO.toOrdered _fmFunction)]
|
let confKeyPair =
|
||||||
<> if _fmConfiguration == emptyFunctionConfig then []
|
if _fmConfiguration == emptyFunctionConfig then []
|
||||||
else pure ("configuration", AO.toOrdered _fmConfiguration)
|
else pure ("configuration", AO.toOrdered _fmConfiguration)
|
||||||
|
permissionsKeyPair =
|
||||||
|
if (null _fmPermissions) then []
|
||||||
|
else pure ("permissions", AO.toOrdered _fmPermissions)
|
||||||
|
in AO.object $ [("function", AO.toOrdered _fmFunction)] <> confKeyPair <> permissionsKeyPair
|
||||||
|
|
||||||
remoteSchemaQToOrdJSON :: RemoteSchemaMetadata -> AO.Value
|
remoteSchemaQToOrdJSON :: RemoteSchemaMetadata -> AO.Value
|
||||||
remoteSchemaQToOrdJSON (RemoteSchemaMetadata name definition comment permissions) =
|
remoteSchemaQToOrdJSON (RemoteSchemaMetadata name definition comment permissions) =
|
||||||
|
@ -24,10 +24,9 @@ import Hasura.Session
|
|||||||
|
|
||||||
data RunCtx
|
data RunCtx
|
||||||
= RunCtx
|
= RunCtx
|
||||||
{ _rcUserInfo :: !UserInfo
|
{ _rcUserInfo :: !UserInfo
|
||||||
, _rcHttpMgr :: !HTTP.Manager
|
, _rcHttpMgr :: !HTTP.Manager
|
||||||
, _rcSqlGenCtx :: !SQLGenCtx
|
, _rcServerConfigCtx :: !ServerConfigCtx
|
||||||
, _rcRemoteSchemaPermsCtx :: !RemoteSchemaPermsCtx
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype RunT m a
|
newtype RunT m a
|
||||||
@ -54,11 +53,8 @@ instance (Monad m) => UserInfoM (RunT m) where
|
|||||||
instance (Monad m) => HTTP.HasHttpManagerM (RunT m) where
|
instance (Monad m) => HTTP.HasHttpManagerM (RunT m) where
|
||||||
askHttpManager = asks _rcHttpMgr
|
askHttpManager = asks _rcHttpMgr
|
||||||
|
|
||||||
instance (Monad m) => HasSQLGenCtx (RunT m) where
|
instance (Monad m) => HasServerConfigCtx (RunT m) where
|
||||||
askSQLGenCtx = asks _rcSqlGenCtx
|
askServerConfigCtx = asks _rcServerConfigCtx
|
||||||
|
|
||||||
instance (Monad m) => HasRemoteSchemaPermsCtx (RunT m) where
|
|
||||||
askRemoteSchemaPermsCtx = asks _rcRemoteSchemaPermsCtx
|
|
||||||
|
|
||||||
instance (MonadResolveSource m) => MonadResolveSource (RunT m) where
|
instance (MonadResolveSource m) => MonadResolveSource (RunT m) where
|
||||||
getSourceResolver = RunT . lift . lift $ getSourceResolver
|
getSourceResolver = RunT . lift . lift $ getSourceResolver
|
||||||
|
@ -51,6 +51,10 @@ data RQLMetadata
|
|||||||
| RMPgTrackFunction !TrackFunctionV2
|
| RMPgTrackFunction !TrackFunctionV2
|
||||||
| RMPgUntrackFunction !UnTrackFunction
|
| RMPgUntrackFunction !UnTrackFunction
|
||||||
|
|
||||||
|
-- Postgres function permissions
|
||||||
|
| RMPgCreateFunctionPermission !CreateFunctionPermission
|
||||||
|
| RMPgDropFunctionPermission !DropFunctionPermission
|
||||||
|
|
||||||
-- Postgres table relationships
|
-- Postgres table relationships
|
||||||
| RMPgCreateObjectRelationship !CreateObjRel
|
| RMPgCreateObjectRelationship !CreateObjRel
|
||||||
| RMPgCreateArrayRelationship !CreateArrRel
|
| RMPgCreateArrayRelationship !CreateArrRel
|
||||||
@ -160,21 +164,19 @@ runMetadataQuery
|
|||||||
-> InstanceId
|
-> InstanceId
|
||||||
-> UserInfo
|
-> UserInfo
|
||||||
-> HTTP.Manager
|
-> HTTP.Manager
|
||||||
-> SQLGenCtx
|
-> ServerConfigCtx
|
||||||
-> RemoteSchemaPermsCtx
|
|
||||||
-> RebuildableSchemaCache
|
-> RebuildableSchemaCache
|
||||||
-> RQLMetadata
|
-> RQLMetadata
|
||||||
-> m (EncJSON, RebuildableSchemaCache)
|
-> m (EncJSON, RebuildableSchemaCache)
|
||||||
runMetadataQuery env instanceId userInfo httpManager sqlGenCtx remoteSchemaPermsCtx schemaCache query = do
|
runMetadataQuery env instanceId userInfo httpManager serverConfigCtx schemaCache query = do
|
||||||
metadata <- fetchMetadata
|
metadata <- fetchMetadata
|
||||||
((r, modMetadata), modSchemaCache, cacheInvalidations) <-
|
((r, modMetadata), modSchemaCache, cacheInvalidations) <-
|
||||||
runMetadataQueryM env query
|
runMetadataQueryM env query
|
||||||
& runMetadataT metadata
|
& runMetadataT metadata
|
||||||
& runCacheRWT schemaCache
|
& runCacheRWT schemaCache
|
||||||
& peelRun (RunCtx userInfo httpManager sqlGenCtx remoteSchemaPermsCtx)
|
& peelRun (RunCtx userInfo httpManager serverConfigCtx)
|
||||||
& runExceptT
|
& runExceptT
|
||||||
& liftEitherM
|
& liftEitherM
|
||||||
|
|
||||||
-- set modified metadata in storage
|
-- set modified metadata in storage
|
||||||
setMetadata modMetadata
|
setMetadata modMetadata
|
||||||
-- notify schema cache sync
|
-- notify schema cache sync
|
||||||
@ -193,7 +195,7 @@ runMetadataQueryM
|
|||||||
, HTTP.HasHttpManagerM m
|
, HTTP.HasHttpManagerM m
|
||||||
, MetadataM m
|
, MetadataM m
|
||||||
, MonadMetadataStorageQueryAPI m
|
, MonadMetadataStorageQueryAPI m
|
||||||
, HasRemoteSchemaPermsCtx m
|
, HasServerConfigCtx m
|
||||||
)
|
)
|
||||||
=> Env.Environment
|
=> Env.Environment
|
||||||
-> RQLMetadata
|
-> RQLMetadata
|
||||||
@ -210,6 +212,9 @@ runMetadataQueryM env = withPathK "args" . \case
|
|||||||
RMPgTrackFunction q -> runTrackFunctionV2 q
|
RMPgTrackFunction q -> runTrackFunctionV2 q
|
||||||
RMPgUntrackFunction q -> runUntrackFunc q
|
RMPgUntrackFunction q -> runUntrackFunc q
|
||||||
|
|
||||||
|
RMPgCreateFunctionPermission q -> runCreateFunctionPermission q
|
||||||
|
RMPgDropFunctionPermission q -> runDropFunctionPermission q
|
||||||
|
|
||||||
RMPgCreateObjectRelationship q -> runCreateRelationship ObjRel q
|
RMPgCreateObjectRelationship q -> runCreateRelationship ObjRel q
|
||||||
RMPgCreateArrayRelationship q -> runCreateRelationship ArrRel q
|
RMPgCreateArrayRelationship q -> runCreateRelationship ArrRel q
|
||||||
RMPgDropRelationship q -> runDropRel q
|
RMPgDropRelationship q -> runDropRel q
|
||||||
|
@ -191,8 +191,9 @@ runQuery
|
|||||||
=> Env.Environment
|
=> Env.Environment
|
||||||
-> InstanceId
|
-> InstanceId
|
||||||
-> UserInfo -> RebuildableSchemaCache -> HTTP.Manager
|
-> UserInfo -> RebuildableSchemaCache -> HTTP.Manager
|
||||||
-> SQLGenCtx -> RemoteSchemaPermsCtx -> RQLQuery -> m (EncJSON, RebuildableSchemaCache)
|
-> SQLGenCtx -> RemoteSchemaPermsCtx -> FunctionPermissionsCtx
|
||||||
runQuery env instanceId userInfo sc hMgr sqlGenCtx remoteSchemaPermsCtx query = do
|
-> RQLQuery -> m (EncJSON, RebuildableSchemaCache)
|
||||||
|
runQuery env instanceId userInfo sc hMgr sqlGenCtx remoteSchemaPermsCtx functionPermsCtx query = do
|
||||||
metadata <- fetchMetadata
|
metadata <- fetchMetadata
|
||||||
result <- runQueryM env query & Tracing.interpTraceT \x -> do
|
result <- runQueryM env query & Tracing.interpTraceT \x -> do
|
||||||
(((js, tracemeta), meta), rsc, ci) <-
|
(((js, tracemeta), meta), rsc, ci) <-
|
||||||
@ -204,7 +205,8 @@ runQuery env instanceId userInfo sc hMgr sqlGenCtx remoteSchemaPermsCtx query =
|
|||||||
pure ((js, rsc, ci, meta), tracemeta)
|
pure ((js, rsc, ci, meta), tracemeta)
|
||||||
withReload result
|
withReload result
|
||||||
where
|
where
|
||||||
runCtx = RunCtx userInfo hMgr sqlGenCtx remoteSchemaPermsCtx
|
serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
|
runCtx = RunCtx userInfo hMgr serverConfigCtx
|
||||||
|
|
||||||
withReload (result, updatedCache, invalidations, updatedMetadata) = do
|
withReload (result, updatedCache, invalidations, updatedMetadata) = do
|
||||||
when (queryModifiesSchemaCache query) $ do
|
when (queryModifiesSchemaCache query) $ do
|
||||||
@ -350,8 +352,8 @@ reconcileAccessModes (Just mode1) (Just mode2)
|
|||||||
runQueryM
|
runQueryM
|
||||||
:: ( HasVersion, CacheRWM m, UserInfoM m
|
:: ( HasVersion, CacheRWM m, UserInfoM m
|
||||||
, MonadBaseControl IO m, MonadIO m, MonadUnique m
|
, MonadBaseControl IO m, MonadIO m, MonadUnique m
|
||||||
, HasHttpManagerM m, HasSQLGenCtx m
|
, HasHttpManagerM m
|
||||||
, HasRemoteSchemaPermsCtx m
|
, HasServerConfigCtx m
|
||||||
, Tracing.MonadTrace m
|
, Tracing.MonadTrace m
|
||||||
, MetadataM m
|
, MetadataM m
|
||||||
, MonadMetadataStorageQueryAPI m
|
, MonadMetadataStorageQueryAPI m
|
||||||
|
@ -66,9 +66,10 @@ runQuery
|
|||||||
-> HTTP.Manager
|
-> HTTP.Manager
|
||||||
-> SQLGenCtx
|
-> SQLGenCtx
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> RQLQuery
|
-> RQLQuery
|
||||||
-> m (EncJSON, RebuildableSchemaCache)
|
-> m (EncJSON, RebuildableSchemaCache)
|
||||||
runQuery env instanceId userInfo schemaCache httpManager sqlGenCtx remoteSchemaPermCtx rqlQuery = do
|
runQuery env instanceId userInfo schemaCache httpManager sqlGenCtx remoteSchemaPermCtx functionPermsCtx rqlQuery = do
|
||||||
metadata <- fetchMetadata
|
metadata <- fetchMetadata
|
||||||
result <- runQueryM env rqlQuery & Tracing.interpTraceT \x -> do
|
result <- runQueryM env rqlQuery & Tracing.interpTraceT \x -> do
|
||||||
(((js, tracemeta), meta), rsc, ci) <-
|
(((js, tracemeta), meta), rsc, ci) <-
|
||||||
@ -80,7 +81,8 @@ runQuery env instanceId userInfo schemaCache httpManager sqlGenCtx remoteSchemaP
|
|||||||
pure ((js, rsc, ci, meta), tracemeta)
|
pure ((js, rsc, ci, meta), tracemeta)
|
||||||
withReload result
|
withReload result
|
||||||
where
|
where
|
||||||
runCtx = RunCtx userInfo httpManager sqlGenCtx remoteSchemaPermCtx
|
runCtx = RunCtx userInfo httpManager
|
||||||
|
$ ServerConfigCtx functionPermsCtx remoteSchemaPermCtx sqlGenCtx
|
||||||
|
|
||||||
withReload (result, updatedCache, invalidations, updatedMetadata) = do
|
withReload (result, updatedCache, invalidations, updatedMetadata) = do
|
||||||
when (queryModifiesSchema rqlQuery) $ do
|
when (queryModifiesSchema rqlQuery) $ do
|
||||||
@ -92,9 +94,9 @@ runQuery env instanceId userInfo schemaCache httpManager sqlGenCtx remoteSchemaP
|
|||||||
|
|
||||||
queryModifiesSchema :: RQLQuery -> Bool
|
queryModifiesSchema :: RQLQuery -> Bool
|
||||||
queryModifiesSchema = \case
|
queryModifiesSchema = \case
|
||||||
RQRunSql q -> isSchemaCacheBuildRequiredRunSQL q
|
RQRunSql q -> isSchemaCacheBuildRequiredRunSQL q
|
||||||
RQBulk l -> any queryModifiesSchema l
|
RQBulk l -> any queryModifiesSchema l
|
||||||
_ -> False
|
_ -> False
|
||||||
|
|
||||||
runQueryM
|
runQueryM
|
||||||
:: ( HasVersion
|
:: ( HasVersion
|
||||||
@ -103,7 +105,7 @@ runQueryM
|
|||||||
, MonadBaseControl IO m
|
, MonadBaseControl IO m
|
||||||
, UserInfoM m
|
, UserInfoM m
|
||||||
, CacheRWM m
|
, CacheRWM m
|
||||||
, HasSQLGenCtx m
|
, HasServerConfigCtx m
|
||||||
, Tracing.MonadTrace m
|
, Tracing.MonadTrace m
|
||||||
, MetadataM m
|
, MetadataM m
|
||||||
)
|
)
|
||||||
|
@ -113,6 +113,7 @@ data ServerCtx
|
|||||||
, scResponseInternalErrorsConfig :: !ResponseInternalErrorsConfig
|
, scResponseInternalErrorsConfig :: !ResponseInternalErrorsConfig
|
||||||
, scEnvironment :: !Env.Environment
|
, scEnvironment :: !Env.Environment
|
||||||
, scRemoteSchemaPermsCtx :: !RemoteSchemaPermsCtx
|
, scRemoteSchemaPermsCtx :: !RemoteSchemaPermsCtx
|
||||||
|
, scFunctionPermsCtx :: !FunctionPermissionsCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
data HandlerCtx
|
data HandlerCtx
|
||||||
@ -391,15 +392,16 @@ v1QueryHandler query = do
|
|||||||
return $ HttpResponse res []
|
return $ HttpResponse res []
|
||||||
where
|
where
|
||||||
action = do
|
action = do
|
||||||
userInfo <- asks hcUser
|
userInfo <- asks hcUser
|
||||||
scRef <- asks (scCacheRef . hcServerCtx)
|
scRef <- asks (scCacheRef . hcServerCtx)
|
||||||
schemaCache <- fmap fst $ liftIO $ readIORef $ _scrCache scRef
|
schemaCache <- fmap fst $ liftIO $ readIORef $ _scrCache scRef
|
||||||
httpMgr <- asks (scManager . hcServerCtx)
|
httpMgr <- asks (scManager . hcServerCtx)
|
||||||
sqlGenCtx <- asks (scSQLGenCtx . hcServerCtx)
|
sqlGenCtx <- asks (scSQLGenCtx . hcServerCtx)
|
||||||
instanceId <- asks (scInstanceId . hcServerCtx)
|
instanceId <- asks (scInstanceId . hcServerCtx)
|
||||||
env <- asks (scEnvironment . hcServerCtx)
|
env <- asks (scEnvironment . hcServerCtx)
|
||||||
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
||||||
runQuery env instanceId userInfo schemaCache httpMgr sqlGenCtx remoteSchemaPermsCtx query
|
functionPermsCtx <- asks (scFunctionPermsCtx . hcServerCtx)
|
||||||
|
runQuery env instanceId userInfo schemaCache httpMgr sqlGenCtx remoteSchemaPermsCtx functionPermsCtx query
|
||||||
|
|
||||||
v1MetadataHandler
|
v1MetadataHandler
|
||||||
:: ( HasVersion
|
:: ( HasVersion
|
||||||
@ -414,17 +416,19 @@ v1MetadataHandler
|
|||||||
=> RQLMetadata -> m (HttpResponse EncJSON)
|
=> RQLMetadata -> m (HttpResponse EncJSON)
|
||||||
v1MetadataHandler query = do
|
v1MetadataHandler query = do
|
||||||
(liftEitherM . authorizeV1MetadataApi query) =<< ask
|
(liftEitherM . authorizeV1MetadataApi query) =<< ask
|
||||||
userInfo <- asks hcUser
|
userInfo <- asks hcUser
|
||||||
scRef <- asks (scCacheRef . hcServerCtx)
|
scRef <- asks (scCacheRef . hcServerCtx)
|
||||||
schemaCache <- fmap fst $ liftIO $ readIORef $ _scrCache scRef
|
schemaCache <- fmap fst $ liftIO $ readIORef $ _scrCache scRef
|
||||||
httpMgr <- asks (scManager . hcServerCtx)
|
httpMgr <- asks (scManager . hcServerCtx)
|
||||||
sqlGenCtx <- asks (scSQLGenCtx . hcServerCtx)
|
sqlGenCtx <- asks (scSQLGenCtx . hcServerCtx)
|
||||||
env <- asks (scEnvironment . hcServerCtx)
|
env <- asks (scEnvironment . hcServerCtx)
|
||||||
instanceId <- asks (scInstanceId . hcServerCtx)
|
instanceId <- asks (scInstanceId . hcServerCtx)
|
||||||
logger <- asks (scLogger . hcServerCtx)
|
logger <- asks (scLogger . hcServerCtx)
|
||||||
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
||||||
|
functionPermsCtx <- asks (scFunctionPermsCtx . hcServerCtx)
|
||||||
|
let serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
r <- withSCUpdate scRef logger $
|
r <- withSCUpdate scRef logger $
|
||||||
runMetadataQuery env instanceId userInfo httpMgr sqlGenCtx remoteSchemaPermsCtx schemaCache query
|
runMetadataQuery env instanceId userInfo httpMgr serverConfigCtx schemaCache query
|
||||||
pure $ HttpResponse r []
|
pure $ HttpResponse r []
|
||||||
|
|
||||||
v2QueryHandler
|
v2QueryHandler
|
||||||
@ -453,7 +457,8 @@ v2QueryHandler query = do
|
|||||||
instanceId <- asks (scInstanceId . hcServerCtx)
|
instanceId <- asks (scInstanceId . hcServerCtx)
|
||||||
env <- asks (scEnvironment . hcServerCtx)
|
env <- asks (scEnvironment . hcServerCtx)
|
||||||
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
remoteSchemaPermsCtx <- asks (scRemoteSchemaPermsCtx . hcServerCtx)
|
||||||
V2Q.runQuery env instanceId userInfo schemaCache httpMgr sqlGenCtx remoteSchemaPermsCtx query
|
functionPermsCtx <- asks (scFunctionPermsCtx . hcServerCtx)
|
||||||
|
V2Q.runQuery env instanceId userInfo schemaCache httpMgr sqlGenCtx remoteSchemaPermsCtx functionPermsCtx query
|
||||||
|
|
||||||
v1Alpha1GQHandler
|
v1Alpha1GQHandler
|
||||||
:: ( HasVersion
|
:: ( HasVersion
|
||||||
@ -727,13 +732,14 @@ mkWaiApp
|
|||||||
-> RebuildableSchemaCache
|
-> RebuildableSchemaCache
|
||||||
-> EKG.Store
|
-> EKG.Store
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> WS.ConnectionOptions
|
-> WS.ConnectionOptions
|
||||||
-> KeepAliveDelay
|
-> KeepAliveDelay
|
||||||
-- ^ Metadata storage connection pool
|
-- ^ Metadata storage connection pool
|
||||||
-> m HasuraApp
|
-> m HasuraApp
|
||||||
mkWaiApp env logger sqlGenCtx enableAL httpManager mode corsCfg enableConsole consoleAssetsDir
|
mkWaiApp env logger sqlGenCtx enableAL httpManager mode corsCfg enableConsole consoleAssetsDir
|
||||||
enableTelemetry instanceId apis lqOpts _ {- planCacheOptions -} responseErrorsConfig
|
enableTelemetry instanceId apis lqOpts _ {- planCacheOptions -} responseErrorsConfig
|
||||||
liveQueryHook schemaCache ekgStore enableRSPermsCtx connectionOptions keepAliveDelay = do
|
liveQueryHook schemaCache ekgStore enableRSPermsCtx functionPermsCtx connectionOptions keepAliveDelay = do
|
||||||
|
|
||||||
-- See Note [Temporarily disabling query plan caching]
|
-- See Note [Temporarily disabling query plan caching]
|
||||||
-- (planCache, schemaCacheRef) <- initialiseCache
|
-- (planCache, schemaCacheRef) <- initialiseCache
|
||||||
@ -762,6 +768,7 @@ mkWaiApp env logger sqlGenCtx enableAL httpManager mode corsCfg enableConsole co
|
|||||||
, scEnvironment = env
|
, scEnvironment = env
|
||||||
, scResponseInternalErrorsConfig = responseErrorsConfig
|
, scResponseInternalErrorsConfig = responseErrorsConfig
|
||||||
, scRemoteSchemaPermsCtx = enableRSPermsCtx
|
, scRemoteSchemaPermsCtx = enableRSPermsCtx
|
||||||
|
, scFunctionPermsCtx = functionPermsCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
spockApp <- liftWithStateless $ \lowerIO ->
|
spockApp <- liftWithStateless $ \lowerIO ->
|
||||||
|
@ -180,8 +180,7 @@ mkServeOptions rso = do
|
|||||||
logHeadersFromEnv <- withEnvBool (rsoLogHeadersFromEnv rso) (fst logHeadersFromEnvEnv)
|
logHeadersFromEnv <- withEnvBool (rsoLogHeadersFromEnv rso) (fst logHeadersFromEnvEnv)
|
||||||
enableRemoteSchemaPerms <-
|
enableRemoteSchemaPerms <-
|
||||||
bool RemoteSchemaPermsDisabled RemoteSchemaPermsEnabled <$>
|
bool RemoteSchemaPermsDisabled RemoteSchemaPermsEnabled <$>
|
||||||
(withEnvBool (rsoEnableRemoteSchemaPermissions rso) $
|
(withEnvBool (rsoEnableRemoteSchemaPermissions rso) (fst enableRemoteSchemaPermsEnv))
|
||||||
(fst enableRemoteSchemaPermsEnv))
|
|
||||||
|
|
||||||
webSocketCompressionFromEnv <- withEnvBool (rsoWebSocketCompression rso) $
|
webSocketCompressionFromEnv <- withEnvBool (rsoWebSocketCompression rso) $
|
||||||
fst webSocketCompressionEnv
|
fst webSocketCompressionEnv
|
||||||
@ -195,12 +194,17 @@ mkServeOptions rso = do
|
|||||||
webSocketKeepAlive <- KeepAliveDelay . fromIntegral . fromMaybe 5
|
webSocketKeepAlive <- KeepAliveDelay . fromIntegral . fromMaybe 5
|
||||||
<$> withEnv (rsoWebSocketKeepAlive rso) (fst webSocketKeepAliveEnv)
|
<$> withEnv (rsoWebSocketKeepAlive rso) (fst webSocketKeepAliveEnv)
|
||||||
|
|
||||||
|
inferFunctionPerms <-
|
||||||
|
maybe FunctionPermissionsInferred (bool FunctionPermissionsManual FunctionPermissionsInferred) <$>
|
||||||
|
(withEnv (rsoInferFunctionPermissions rso) (fst inferFunctionPermsEnv))
|
||||||
|
|
||||||
return $ ServeOptions port host connParams txIso adminScrt authHook jwtSecret
|
return $ ServeOptions port host connParams txIso adminScrt authHook jwtSecret
|
||||||
unAuthRole corsCfg enableConsole consoleAssetsDir
|
unAuthRole corsCfg enableConsole consoleAssetsDir
|
||||||
enableTelemetry strfyNum enabledAPIs lqOpts enableAL
|
enableTelemetry strfyNum enabledAPIs lqOpts enableAL
|
||||||
enabledLogs serverLogLevel planCacheOptions
|
enabledLogs serverLogLevel planCacheOptions
|
||||||
internalErrorsConfig eventsHttpPoolSize eventsFetchInterval
|
internalErrorsConfig eventsHttpPoolSize eventsFetchInterval
|
||||||
logHeadersFromEnv enableRemoteSchemaPerms connectionOptions webSocketKeepAlive
|
logHeadersFromEnv enableRemoteSchemaPerms connectionOptions webSocketKeepAlive
|
||||||
|
inferFunctionPerms
|
||||||
where
|
where
|
||||||
#ifdef DeveloperAPIs
|
#ifdef DeveloperAPIs
|
||||||
defaultAPIs = [METADATA,GRAPHQL,PGDUMP,CONFIG,DEVELOPER]
|
defaultAPIs = [METADATA,GRAPHQL,PGDUMP,CONFIG,DEVELOPER]
|
||||||
@ -544,6 +548,12 @@ enableRemoteSchemaPermsEnv =
|
|||||||
, "Enables remote schema permissions (default: false)"
|
, "Enables remote schema permissions (default: false)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
inferFunctionPermsEnv :: (String, String)
|
||||||
|
inferFunctionPermsEnv =
|
||||||
|
( "HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS"
|
||||||
|
, "Infers function permissions (default: true)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
adminInternalErrorsEnv :: (String, String)
|
adminInternalErrorsEnv :: (String, String)
|
||||||
adminInternalErrorsEnv =
|
adminInternalErrorsEnv =
|
||||||
@ -871,6 +881,13 @@ parseEnableRemoteSchemaPerms =
|
|||||||
help (snd enableRemoteSchemaPermsEnv)
|
help (snd enableRemoteSchemaPermsEnv)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parseInferFunctionPerms :: Parser (Maybe Bool)
|
||||||
|
parseInferFunctionPerms = optional $
|
||||||
|
option ( eitherReader parseStrAsBool )
|
||||||
|
( long "infer-function-permissions" <>
|
||||||
|
help (snd inferFunctionPermsEnv)
|
||||||
|
)
|
||||||
|
|
||||||
mxRefetchDelayEnv :: (String, String)
|
mxRefetchDelayEnv :: (String, String)
|
||||||
mxRefetchDelayEnv =
|
mxRefetchDelayEnv =
|
||||||
( "HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL"
|
( "HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL"
|
||||||
@ -983,6 +1000,7 @@ serveOptsToLog so =
|
|||||||
, "remote_schema_permissions" J..= soEnableRemoteSchemaPermissions so
|
, "remote_schema_permissions" J..= soEnableRemoteSchemaPermissions so
|
||||||
, "websocket_compression_options" J..= show (WS.connectionCompressionOptions . soConnectionOptions $ so)
|
, "websocket_compression_options" J..= show (WS.connectionCompressionOptions . soConnectionOptions $ so)
|
||||||
, "websocket_keep_alive" J..= show (soWebsocketKeepAlive so)
|
, "websocket_keep_alive" J..= show (soWebsocketKeepAlive so)
|
||||||
|
, "infer_function_permissions" J..= soInferFunctionPermissions so
|
||||||
]
|
]
|
||||||
|
|
||||||
mkGenericStrLog :: L.LogLevel -> Text -> String -> StartupLog
|
mkGenericStrLog :: L.LogLevel -> Text -> String -> StartupLog
|
||||||
@ -1031,6 +1049,7 @@ serveOptionsParser =
|
|||||||
<*> parseEnableRemoteSchemaPerms
|
<*> parseEnableRemoteSchemaPerms
|
||||||
<*> parseWebSocketCompression
|
<*> parseWebSocketCompression
|
||||||
<*> parseWebSocketKeepAlive
|
<*> parseWebSocketKeepAlive
|
||||||
|
<*> parseInferFunctionPerms
|
||||||
|
|
||||||
-- | This implements the mapping between application versions
|
-- | This implements the mapping between application versions
|
||||||
-- and catalog schema versions.
|
-- and catalog schema versions.
|
||||||
|
@ -74,6 +74,7 @@ data RawServeOptions impl
|
|||||||
, rsoEnableRemoteSchemaPermissions :: !Bool
|
, rsoEnableRemoteSchemaPermissions :: !Bool
|
||||||
, rsoWebSocketCompression :: !Bool
|
, rsoWebSocketCompression :: !Bool
|
||||||
, rsoWebSocketKeepAlive :: !(Maybe Int)
|
, rsoWebSocketKeepAlive :: !(Maybe Int)
|
||||||
|
, rsoInferFunctionPermissions :: !(Maybe Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
-- | @'ResponseInternalErrorsConfig' represents the encoding of the internal
|
-- | @'ResponseInternalErrorsConfig' represents the encoding of the internal
|
||||||
@ -124,6 +125,7 @@ data ServeOptions impl
|
|||||||
, soEnableRemoteSchemaPermissions :: !RemoteSchemaPermsCtx
|
, soEnableRemoteSchemaPermissions :: !RemoteSchemaPermsCtx
|
||||||
, soConnectionOptions :: !WS.ConnectionOptions
|
, soConnectionOptions :: !WS.ConnectionOptions
|
||||||
, soWebsocketKeepAlive :: !KeepAliveDelay
|
, soWebsocketKeepAlive :: !KeepAliveDelay
|
||||||
|
, soInferFunctionPermissions :: !FunctionPermissionsCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
data DowngradeOptions
|
data DowngradeOptions
|
||||||
|
@ -82,7 +82,7 @@ data SchemaSyncEvent
|
|||||||
instance ToJSON SchemaSyncEvent where
|
instance ToJSON SchemaSyncEvent where
|
||||||
toJSON = \case
|
toJSON = \case
|
||||||
SSEListenStart time -> String $ "event listening started at " <> tshow time
|
SSEListenStart time -> String $ "event listening started at " <> tshow time
|
||||||
SSEPayload payload -> toJSON payload
|
SSEPayload payload -> toJSON payload
|
||||||
|
|
||||||
data ThreadError
|
data ThreadError
|
||||||
= TEPayloadParse !Text
|
= TEPayloadParse !Text
|
||||||
@ -192,12 +192,14 @@ startSchemaSyncProcessorThread
|
|||||||
-> InstanceId
|
-> InstanceId
|
||||||
-> UTC.UTCTime
|
-> UTC.UTCTime
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> ManagedT m Immortal.Thread
|
-> ManagedT m Immortal.Thread
|
||||||
startSchemaSyncProcessorThread sqlGenCtx logger httpMgr
|
startSchemaSyncProcessorThread sqlGenCtx logger httpMgr
|
||||||
schemaSyncEventRef cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx = do
|
schemaSyncEventRef cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx functionPermsCtx = do
|
||||||
-- Start processor thread
|
-- Start processor thread
|
||||||
processorThread <- C.forkManagedT "SchemeUpdate.processor" logger $
|
processorThread <- C.forkManagedT "SchemeUpdate.processor" logger $
|
||||||
processor sqlGenCtx logger httpMgr schemaSyncEventRef cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx
|
processor sqlGenCtx logger httpMgr schemaSyncEventRef
|
||||||
|
cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx functionPermsCtx
|
||||||
logThreadStarted logger instanceId TTProcessor processorThread
|
logThreadStarted logger instanceId TTProcessor processorThread
|
||||||
pure processorThread
|
pure processorThread
|
||||||
|
|
||||||
@ -258,9 +260,10 @@ processor
|
|||||||
-> InstanceId
|
-> InstanceId
|
||||||
-> UTC.UTCTime
|
-> UTC.UTCTime
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> m void
|
-> m void
|
||||||
processor sqlGenCtx logger httpMgr updateEventRef
|
processor sqlGenCtx logger httpMgr updateEventRef
|
||||||
cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx =
|
cacheRef instanceId cacheInitStartTime remoteSchemaPermsCtx functionPermsCtx =
|
||||||
-- Never exits
|
-- Never exits
|
||||||
forever $ do
|
forever $ do
|
||||||
event <- liftIO $ STM.atomically getLatestEvent
|
event <- liftIO $ STM.atomically getLatestEvent
|
||||||
@ -281,7 +284,7 @@ processor sqlGenCtx logger httpMgr updateEventRef
|
|||||||
|
|
||||||
when shouldReload $
|
when shouldReload $
|
||||||
refreshSchemaCache sqlGenCtx logger httpMgr cacheRef cacheInvalidations
|
refreshSchemaCache sqlGenCtx logger httpMgr cacheRef cacheInvalidations
|
||||||
threadType remoteSchemaPermsCtx "schema cache reloaded"
|
threadType remoteSchemaPermsCtx functionPermsCtx "schema cache reloaded"
|
||||||
where
|
where
|
||||||
-- checks if there is an event
|
-- checks if there is an event
|
||||||
-- and replaces it with Nothing
|
-- and replaces it with Nothing
|
||||||
@ -307,10 +310,11 @@ refreshSchemaCache
|
|||||||
-> CacheInvalidations
|
-> CacheInvalidations
|
||||||
-> ThreadType
|
-> ThreadType
|
||||||
-> RemoteSchemaPermsCtx
|
-> RemoteSchemaPermsCtx
|
||||||
|
-> FunctionPermissionsCtx
|
||||||
-> Text
|
-> Text
|
||||||
-> m ()
|
-> m ()
|
||||||
refreshSchemaCache sqlGenCtx logger httpManager
|
refreshSchemaCache sqlGenCtx logger httpManager
|
||||||
cacheRef invalidations threadType remoteSchemaPermsCtx msg = do
|
cacheRef invalidations threadType remoteSchemaPermsCtx functionPermsCtx msg = do
|
||||||
-- Reload schema cache from catalog
|
-- Reload schema cache from catalog
|
||||||
eitherMetadata <- runMetadataStorageT fetchMetadata
|
eitherMetadata <- runMetadataStorageT fetchMetadata
|
||||||
resE <- runExceptT $ do
|
resE <- runExceptT $ do
|
||||||
@ -325,7 +329,8 @@ refreshSchemaCache sqlGenCtx logger httpManager
|
|||||||
Left e -> logError logger threadType $ TEQueryError e
|
Left e -> logError logger threadType $ TEQueryError e
|
||||||
Right () -> logInfo logger threadType $ object ["message" .= msg]
|
Right () -> logInfo logger threadType $ object ["message" .= msg]
|
||||||
where
|
where
|
||||||
runCtx = RunCtx adminUserInfo httpManager sqlGenCtx remoteSchemaPermsCtx
|
serverConfigCtx = ServerConfigCtx functionPermsCtx remoteSchemaPermsCtx sqlGenCtx
|
||||||
|
runCtx = RunCtx adminUserInfo httpManager serverConfigCtx
|
||||||
|
|
||||||
logInfo :: (MonadIO m) => Logger Hasura -> ThreadType -> Value -> m ()
|
logInfo :: (MonadIO m) => Logger Hasura -> ThreadType -> Value -> m ()
|
||||||
logInfo logger threadType val = unLogger logger $
|
logInfo logger threadType val = unLogger logger $
|
||||||
|
@ -164,7 +164,7 @@ computeMetrics sc _mtServiceTimings _mtPgVersion =
|
|||||||
_mtEventTriggers = Map.size $ Map.filter (not . Map.null)
|
_mtEventTriggers = Map.size $ Map.filter (not . Map.null)
|
||||||
$ Map.map _tiEventTriggerInfoMap userTables
|
$ Map.map _tiEventTriggerInfoMap userTables
|
||||||
_mtRemoteSchemas = Map.size $ scRemoteSchemas sc
|
_mtRemoteSchemas = Map.size $ scRemoteSchemas sc
|
||||||
_mtFunctions = Map.size $ Map.filter (not . isSystemDefined . fiSystemDefined) pgFunctionCache
|
_mtFunctions = Map.size $ Map.filter (not . isSystemDefined . _fiSystemDefined) pgFunctionCache
|
||||||
_mtActions = computeActionsMetrics $ scActions sc
|
_mtActions = computeActionsMetrics $ scActions sc
|
||||||
|
|
||||||
in Metrics{..}
|
in Metrics{..}
|
||||||
|
@ -37,7 +37,7 @@ newtype CacheRefT m a
|
|||||||
= CacheRefT { runCacheRefT :: MVar RebuildableSchemaCache -> m a }
|
= CacheRefT { runCacheRefT :: MVar RebuildableSchemaCache -> m a }
|
||||||
deriving
|
deriving
|
||||||
( Functor, Applicative, Monad, MonadIO, MonadError e, MonadBase b, MonadBaseControl b
|
( Functor, Applicative, Monad, MonadIO, MonadError e, MonadBase b, MonadBaseControl b
|
||||||
, MonadTx, MonadUnique, UserInfoM, HTTP.HasHttpManagerM, HasSQLGenCtx)
|
, MonadTx, MonadUnique, UserInfoM, HTTP.HasHttpManagerM, HasServerConfigCtx)
|
||||||
via (ReaderT (MVar RebuildableSchemaCache) m)
|
via (ReaderT (MVar RebuildableSchemaCache) m)
|
||||||
|
|
||||||
instance MonadTrans CacheRefT where
|
instance MonadTrans CacheRefT where
|
||||||
@ -51,7 +51,7 @@ instance (MonadBase IO m) => CacheRM (CacheRefT m) where
|
|||||||
askSchemaCache = CacheRefT (fmap lastBuiltSchemaCache . readMVar)
|
askSchemaCache = CacheRefT (fmap lastBuiltSchemaCache . readMVar)
|
||||||
|
|
||||||
instance (MonadIO m, MonadBaseControl IO m, MonadTx m, HTTP.HasHttpManagerM m
|
instance (MonadIO m, MonadBaseControl IO m, MonadTx m, HTTP.HasHttpManagerM m
|
||||||
, HasSQLGenCtx m, HasRemoteSchemaPermsCtx m, MonadResolveSource m) => CacheRWM (CacheRefT m) where
|
, MonadResolveSource m, HasServerConfigCtx m) => CacheRWM (CacheRefT m) where
|
||||||
buildSchemaCacheWithOptions reason invalidations metadata = CacheRefT $ flip modifyMVar \schemaCache -> do
|
buildSchemaCacheWithOptions reason invalidations metadata = CacheRefT $ flip modifyMVar \schemaCache -> do
|
||||||
((), cache, _) <- runCacheRWT schemaCache (buildSchemaCacheWithOptions reason invalidations metadata)
|
((), cache, _) <- runCacheRWT schemaCache (buildSchemaCacheWithOptions reason invalidations metadata)
|
||||||
pure (cache, ())
|
pure (cache, ())
|
||||||
@ -72,8 +72,7 @@ spec
|
|||||||
, MonadBaseControl IO m
|
, MonadBaseControl IO m
|
||||||
, MonadError QErr m
|
, MonadError QErr m
|
||||||
, HTTP.HasHttpManagerM m
|
, HTTP.HasHttpManagerM m
|
||||||
, HasSQLGenCtx m
|
, HasServerConfigCtx m
|
||||||
, HasRemoteSchemaPermsCtx m
|
|
||||||
, MonadResolveSource m
|
, MonadResolveSource m
|
||||||
)
|
)
|
||||||
=> SourceConfiguration -> PGExecCtx -> Q.ConnInfo -> SpecWithCache m
|
=> SourceConfiguration -> PGExecCtx -> Q.ConnInfo -> SpecWithCache m
|
||||||
|
@ -97,8 +97,8 @@ buildPostgresSpecs maybeUrlTemplate = do
|
|||||||
setupCacheRef = do
|
setupCacheRef = do
|
||||||
httpManager <- HTTP.newManager HTTP.tlsManagerSettings
|
httpManager <- HTTP.newManager HTTP.tlsManagerSettings
|
||||||
let sqlGenCtx = SQLGenCtx False
|
let sqlGenCtx = SQLGenCtx False
|
||||||
cacheBuildParams = CacheBuildParams httpManager sqlGenCtx RemoteSchemaPermsDisabled
|
serverConfigCtx = ServerConfigCtx FunctionPermissionsInferred RemoteSchemaPermsDisabled sqlGenCtx
|
||||||
(mkPgSourceResolver print)
|
cacheBuildParams = CacheBuildParams httpManager (mkPgSourceResolver print) serverConfigCtx
|
||||||
|
|
||||||
run :: CacheBuild a -> IO a
|
run :: CacheBuild a -> IO a
|
||||||
run =
|
run =
|
||||||
|
@ -82,6 +82,13 @@ def pytest_addoption(parser):
|
|||||||
help="Run testcases for logging"
|
help="Run testcases for logging"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parser.addoption(
|
||||||
|
"--test-function-permissions",
|
||||||
|
action="store_true",
|
||||||
|
required=False,
|
||||||
|
help="Run manual function permission tests"
|
||||||
|
)
|
||||||
|
|
||||||
parser.addoption(
|
parser.addoption(
|
||||||
"--test-jwk-url",
|
"--test-jwk-url",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@ -283,6 +290,12 @@ def actions_fixture(hge_ctx):
|
|||||||
webhook_httpd.server_close()
|
webhook_httpd.server_close()
|
||||||
web_server.join()
|
web_server.join()
|
||||||
|
|
||||||
|
@pytest.fixture(scope='class')
|
||||||
|
def functions_permissions_fixtures(hge_ctx):
|
||||||
|
if not hge_ctx.function_permissions:
|
||||||
|
pytest.skip('These tests are meant to be run with --test-function-permissions set')
|
||||||
|
return
|
||||||
|
|
||||||
@pytest.fixture(scope='class')
|
@pytest.fixture(scope='class')
|
||||||
def scheduled_triggers_evts_webhook(request):
|
def scheduled_triggers_evts_webhook(request):
|
||||||
webhook_httpd = EvtsWebhookServer(server_address=('127.0.0.1', 5594))
|
webhook_httpd = EvtsWebhookServer(server_address=('127.0.0.1', 5594))
|
||||||
|
@ -473,6 +473,7 @@ class HGECtx:
|
|||||||
self.webhook_insecure = config.getoption('--test-webhook-insecure')
|
self.webhook_insecure = config.getoption('--test-webhook-insecure')
|
||||||
self.metadata_disabled = config.getoption('--test-metadata-disabled')
|
self.metadata_disabled = config.getoption('--test-metadata-disabled')
|
||||||
self.may_skip_test_teardown = False
|
self.may_skip_test_teardown = False
|
||||||
|
self.function_permissions = config.getoption('--test-function-permissions')
|
||||||
|
|
||||||
self.engine = create_engine(self.pg_url)
|
self.engine = create_engine(self.pg_url)
|
||||||
self.meta = MetaData()
|
self.meta = MetaData()
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
type: pg_create_function_permission
|
||||||
|
args:
|
||||||
|
function: add_to_score
|
||||||
|
role: anonymous
|
@ -0,0 +1,4 @@
|
|||||||
|
type: pg_drop_function_permission
|
||||||
|
args:
|
||||||
|
function: add_to_score
|
||||||
|
role: anonymous
|
@ -0,0 +1,20 @@
|
|||||||
|
- description: Fails as anonymous role doesn't have access to the `add_to_score` function
|
||||||
|
headers:
|
||||||
|
X-Hasura-Role: anonymous
|
||||||
|
url: /v1/graphql
|
||||||
|
status: 200
|
||||||
|
query:
|
||||||
|
query: |
|
||||||
|
mutation {
|
||||||
|
add_to_score(args: {search: "Black"}){
|
||||||
|
name
|
||||||
|
score
|
||||||
|
role_echo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
response:
|
||||||
|
errors:
|
||||||
|
- extensions:
|
||||||
|
path: $
|
||||||
|
code: validation-failed
|
||||||
|
message: no mutations exist
|
@ -0,0 +1,4 @@
|
|||||||
|
type: pg_create_function_permission
|
||||||
|
args:
|
||||||
|
role: user
|
||||||
|
function: get_articles
|
@ -0,0 +1,23 @@
|
|||||||
|
- description: Query function as a role without permission being configured for the role
|
||||||
|
url: /v1/graphql
|
||||||
|
status: 200
|
||||||
|
headers:
|
||||||
|
X-Hasura-Role: user
|
||||||
|
X-Hasura-User-Id: '1'
|
||||||
|
response:
|
||||||
|
data:
|
||||||
|
get_articles:
|
||||||
|
- title: Article 1
|
||||||
|
content: Sample article content 1
|
||||||
|
- title: Article 2
|
||||||
|
content: Sample article content 2
|
||||||
|
- title: Article 3
|
||||||
|
content: Sample article content 3
|
||||||
|
query:
|
||||||
|
query: |
|
||||||
|
query {
|
||||||
|
get_articles(args: {search: "art"}) {
|
||||||
|
title
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
description: Query function as a role without permission being configured for the role
|
||||||
|
url: /v1/graphql
|
||||||
|
status: 200
|
||||||
|
headers:
|
||||||
|
X-Hasura-Role: user
|
||||||
|
response:
|
||||||
|
errors:
|
||||||
|
- extensions:
|
||||||
|
path: $.selectionSet.get_articles
|
||||||
|
code: validation-failed
|
||||||
|
message: "field \"get_articles\" not found in type: 'query_root'"
|
||||||
|
query:
|
||||||
|
query: |
|
||||||
|
query {
|
||||||
|
get_articles(args: {search: "art"}) {
|
||||||
|
title
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
type: bulk
|
||||||
|
args:
|
||||||
|
|
||||||
|
- type: run_sql
|
||||||
|
args:
|
||||||
|
sql: |
|
||||||
|
create table author(
|
||||||
|
id serial primary key,
|
||||||
|
name text unique,
|
||||||
|
is_registered boolean not null default false,
|
||||||
|
remarks_internal text
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO author (name, remarks_internal)
|
||||||
|
VALUES
|
||||||
|
('Author 1', 'remark 1'),
|
||||||
|
('Author 2', 'remark 2'),
|
||||||
|
('Author 3', 'remark 3');
|
||||||
|
|
||||||
|
CREATE TABLE article (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
title TEXT,
|
||||||
|
content TEXT,
|
||||||
|
author_id INTEGER NOT NULL REFERENCES author(id),
|
||||||
|
is_published BOOLEAN NOT NULL default FALSE,
|
||||||
|
published_on TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO article (title, content, author_id, is_published)
|
||||||
|
VALUES
|
||||||
|
('Article 1', 'Sample article content 1', 1, false),
|
||||||
|
('Article 2', 'Sample article content 2', 1, true),
|
||||||
|
('Article 3', 'Sample article content 3', 2, true),
|
||||||
|
('Article 4', 'Sample article content 4', 3, false);
|
||||||
|
|
||||||
|
CREATE FUNCTION get_articles(search text)
|
||||||
|
RETURNS SETOF article AS $$
|
||||||
|
SELECT *
|
||||||
|
FROM article
|
||||||
|
WHERE
|
||||||
|
title ilike ('%' || search || '%')
|
||||||
|
OR content ilike ('%' || search || '%')
|
||||||
|
$$ LANGUAGE sql STABLE;
|
||||||
|
|
||||||
|
- type: track_table
|
||||||
|
args:
|
||||||
|
table: author
|
||||||
|
|
||||||
|
- type: track_table
|
||||||
|
args:
|
||||||
|
table: article
|
||||||
|
|
||||||
|
- type: track_function
|
||||||
|
args:
|
||||||
|
name: get_articles
|
||||||
|
schema: public
|
||||||
|
|
||||||
|
- type: create_select_permission
|
||||||
|
args:
|
||||||
|
table: article
|
||||||
|
role: user
|
||||||
|
permission:
|
||||||
|
columns:
|
||||||
|
- title
|
||||||
|
- content
|
||||||
|
- is_published
|
||||||
|
filter:
|
||||||
|
_or:
|
||||||
|
- id: X-HASURA-USER-ID
|
||||||
|
- is_published:
|
||||||
|
_eq: true
|
@ -0,0 +1,9 @@
|
|||||||
|
type: bulk
|
||||||
|
args:
|
||||||
|
|
||||||
|
- type: run_sql
|
||||||
|
args:
|
||||||
|
sql: |
|
||||||
|
DROP TABLE article cascade;
|
||||||
|
DROP TABLE author cascade;
|
||||||
|
cascade: true
|
@ -588,9 +588,14 @@ class TestGraphQLMutateEnums:
|
|||||||
def test_delete_where_enum_field(self, hge_ctx, transport):
|
def test_delete_where_enum_field(self, hge_ctx, transport):
|
||||||
check_query_f(hge_ctx, self.dir() + '/delete_where_enum_field.yaml', transport)
|
check_query_f(hge_ctx, self.dir() + '/delete_where_enum_field.yaml', transport)
|
||||||
|
|
||||||
|
use_function_permission_fixtures = usefixtures(
|
||||||
|
'per_class_db_schema_for_mutation_tests',
|
||||||
|
'per_method_db_data_for_mutation_tests',
|
||||||
|
'functions_permissions_fixtures'
|
||||||
|
)
|
||||||
# Tracking VOLATILE SQL functions as mutations, or queries (#1514)
|
# Tracking VOLATILE SQL functions as mutations, or queries (#1514)
|
||||||
@pytest.mark.parametrize('transport', ['http', 'websocket'])
|
@pytest.mark.parametrize('transport', ['http', 'websocket'])
|
||||||
@use_mutation_fixtures
|
@use_function_permission_fixtures
|
||||||
class TestGraphQLMutationFunctions:
|
class TestGraphQLMutationFunctions:
|
||||||
@classmethod
|
@classmethod
|
||||||
def dir(cls):
|
def dir(cls):
|
||||||
@ -609,7 +614,17 @@ class TestGraphQLMutationFunctions:
|
|||||||
def test_functions_as_mutations(self, hge_ctx, transport):
|
def test_functions_as_mutations(self, hge_ctx, transport):
|
||||||
check_query_f(hge_ctx, self.dir() + '/function_as_mutations.yaml', transport)
|
check_query_f(hge_ctx, self.dir() + '/function_as_mutations.yaml', transport)
|
||||||
|
|
||||||
|
# When graphql-engine is started with `--infer-function-permissions=false` then
|
||||||
|
# a function is only accessible to a role when the permission is granted through
|
||||||
|
# the `pg_create_function_permission` definition
|
||||||
|
def test_function_as_mutation_without_function_permission(self, hge_ctx, transport):
|
||||||
|
check_query_f(hge_ctx, self.dir() + '/function_without_function_permission.yaml')
|
||||||
|
|
||||||
# Ensure select permissions on the corresponding SETOF table apply to
|
# Ensure select permissions on the corresponding SETOF table apply to
|
||||||
# the return set of the mutation field backed by the tracked function.
|
# the return set of the mutation field backed by the tracked function.
|
||||||
def test_functions_as_mutations_permissions(self, hge_ctx, transport):
|
def test_functions_as_mutations_permissions(self, hge_ctx, transport):
|
||||||
|
st_code, resp = hge_ctx.v1metadataq_f(self.dir() + '/create_function_permission_add_to_score.yaml')
|
||||||
|
assert st_code == 200, resp
|
||||||
check_query_f(hge_ctx, self.dir() + '/function_as_mutations_permissions.yaml', transport)
|
check_query_f(hge_ctx, self.dir() + '/function_as_mutations_permissions.yaml', transport)
|
||||||
|
st_code, resp = hge_ctx.v1metadataq_f(self.dir() + '/drop_function_permission_add_to_score.yaml')
|
||||||
|
assert st_code == 200, resp
|
||||||
|
@ -806,3 +806,24 @@ def _test_relay_pagination(hge_ctx, transport, test_file_prefix, no_of_pages):
|
|||||||
page_no = i + 1
|
page_no = i + 1
|
||||||
test_file = "page_" + str(page_no) + ".yaml"
|
test_file = "page_" + str(page_no) + ".yaml"
|
||||||
check_query_f(hge_ctx, test_file_prefix + "/" + test_file, transport)
|
check_query_f(hge_ctx, test_file_prefix + "/" + test_file, transport)
|
||||||
|
|
||||||
|
use_function_permission_fixtures = pytest.mark.usefixtures(
|
||||||
|
'per_method_tests_db_state',
|
||||||
|
'functions_permissions_fixtures'
|
||||||
|
)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('transport', ['http', 'websocket'])
|
||||||
|
@use_function_permission_fixtures
|
||||||
|
class TestGraphQLQueryFunctionPermissions:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def dir(cls):
|
||||||
|
return 'queries/graphql_query/functions/permissions/'
|
||||||
|
|
||||||
|
def test_access_function_without_permission_configured(self, hge_ctx, transport):
|
||||||
|
check_query_f(hge_ctx, self.dir() + 'get_articles_without_permission_configured.yaml')
|
||||||
|
|
||||||
|
def test_access_function_with_permission_configured(self, hge_ctx, transport):
|
||||||
|
st_code, resp = hge_ctx.v1metadataq_f(self.dir() + 'add_function_permission_get_articles.yaml')
|
||||||
|
assert st_code == 200, resp
|
||||||
|
check_query_f(hge_ctx, self.dir() + 'get_articles_with_permission_configured.yaml')
|
||||||
|
Loading…
Reference in New Issue
Block a user