2022-08-24 11:30:55 +03:00
import collections
2022-08-15 17:29:02 +03:00
import os
2018-09-18 09:21:57 +03:00
import pytest
2022-08-15 17:29:02 +03:00
import re
import sqlalchemy
2019-04-08 10:22:38 +03:00
import sys
2022-08-15 17:29:02 +03:00
import threading
import time
2022-09-28 12:19:47 +03:00
from typing import Optional
2022-08-15 17:29:02 +03:00
2022-09-29 13:42:47 +03:00
import auth_webhook_server
2022-09-15 15:30:01 +03:00
from context import HGECtx , HGECtxGQLServer , ActionsWebhookServer , EvtsWebhookServer , GQLWsClient , PytestConf , GraphQLWSClient
import fixtures . hge
2022-09-15 00:41:28 +03:00
import graphql_server
import ports
2018-10-30 12:21:58 +03:00
2018-09-18 09:21:57 +03:00
def pytest_addoption ( parser ) :
2022-09-15 15:30:01 +03:00
parser . addoption (
" --hge-bin " ,
metavar = " HGE_BIN " ,
required = False ,
help = " Hasura GraphQL Engine binary executable " ,
)
2018-09-18 09:21:57 +03:00
parser . addoption (
2019-04-08 10:22:38 +03:00
" --hge-urls " ,
metavar = " HGE_URLS " ,
help = " csv list of urls for graphql-engine " ,
required = False ,
nargs = ' + '
2018-09-18 09:21:57 +03:00
)
parser . addoption (
2019-04-08 10:22:38 +03:00
" --pg-urls " , metavar = " PG_URLS " ,
help = " csv list of urls for connecting to Postgres directly " ,
required = False ,
nargs = ' + '
2018-09-18 09:21:57 +03:00
)
2018-10-28 21:27:49 +03:00
parser . addoption (
2019-02-14 12:37:47 +03:00
" --hge-key " , metavar = " HGE_KEY " , help = " admin secret key for graphql-engine " , required = False
2018-10-28 21:27:49 +03:00
)
parser . addoption (
" --hge-webhook " , metavar = " HGE_WEBHOOK " , help = " url for graphql-engine ' s access control webhook " , required = False
)
parser . addoption (
" --test-webhook-insecure " , action = " store_true " ,
help = " Run Test cases for insecure https webhook "
)
2021-02-03 10:10:39 +03:00
parser . addoption (
" --test-webhook-request-context " , action = " store_true " ,
help = " Run Test cases for testing webhook request context "
)
2018-10-28 21:27:49 +03:00
parser . addoption (
2021-10-12 20:58:46 +03:00
" --hge-jwt-key-file " , metavar = " HGE_JWT_KEY_FILE " , help = " File containing the private key used to encode jwt tokens using RS512 algorithm " , required = False
2018-10-28 21:27:49 +03:00
)
2019-02-05 15:04:16 +03:00
parser . addoption (
" --hge-jwt-conf " , metavar = " HGE_JWT_CONF " , help = " The JWT conf " , required = False
)
2018-09-18 09:21:57 +03:00
2019-03-04 10:46:53 +03:00
parser . addoption (
" --test-ws-init-cookie " ,
metavar = " read|noread " ,
required = False ,
help = " Run testcases for testing cookie sending over websockets "
)
2019-03-12 08:46:27 +03:00
parser . addoption (
" --test-hge-scale-url " ,
metavar = " <url> " ,
required = False ,
help = " Run testcases for horizontal scaling "
)
2019-07-11 08:37:06 +03:00
parser . addoption (
" --test-logging " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run testcases for logging "
)
2022-02-09 08:13:32 +03:00
parser . addoption (
" --test-startup-db-calls " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run testcases for startup database calls "
)
2022-04-22 22:53:12 +03:00
parser . addoption (
" --test-streaming-subscriptions " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run streaming subscription tests "
)
2019-12-03 23:56:59 +03:00
parser . addoption (
" --test-jwk-url " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run testcases for JWK url behaviour "
)
2019-09-04 18:02:35 +03:00
parser . addoption (
" --accept " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Accept any failing test cases from YAML files as correct, and write the new files out to disk. "
)
2020-02-13 12:14:02 +03:00
parser . addoption (
" --skip-schema-teardown " ,
action = " store_true " ,
default = False ,
required = False ,
help = """
Skip tearing down the schema / Hasura metadata after tests . This option may result in test failures if the schema
has to change between the list of tests to be run
"""
)
parser . addoption (
" --skip-schema-setup " ,
action = " store_true " ,
default = False ,
required = False ,
help = """
Skip setting up schema / Hasura metadata before tests .
This option may result in test failures if the schema has to change between the list of tests to be run
"""
)
2019-09-04 18:02:35 +03:00
2020-02-13 12:14:02 +03:00
parser . addoption (
" --avoid-error-message-checks " ,
action = " store_true " ,
default = False ,
required = False ,
help = """
This option when set will ignore disparity in error messages between expected and response outputs .
Used basically in version upgrade / downgrade tests where the error messages may change
"""
)
parser . addoption (
" --collect-upgrade-tests-to-file " ,
metavar = " <path> " ,
required = False ,
help = " When used along with collect-only, it will write the list of upgrade tests into the file specified "
)
2019-07-11 08:37:06 +03:00
2020-05-05 22:57:17 +03:00
parser . addoption (
" --test-unauthorized-role " ,
action = " store_true " ,
help = " Run testcases for unauthorized role " ,
)
2021-12-08 21:28:36 +03:00
parser . addoption (
" --test-no-cookie-and-unauth-role " ,
action = " store_true " ,
help = " Run testcases for no unauthorized role and no cookie jwt header set (cookie auth is set as part of jwt config upon engine startup) " ,
)
2021-02-13 03:05:23 +03:00
parser . addoption (
" --redis-url " ,
metavar = " REDIS_URL " ,
help = " redis url for cache server " ,
default = False
)
2021-03-11 21:17:41 +03:00
parser . addoption (
" --backend " ,
help = " run integration tests using a particular backend " ,
default = " postgres "
)
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
2021-05-05 15:25:27 +03:00
parser . addoption (
" --pro-tests " ,
action = " store_true " ,
default = False ,
help = " Flag to specify if the pro tests are to be run "
)
2021-11-09 15:00:21 +03:00
parser . addoption (
" --test-auth-webhook-header " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run testcases for auth webhook header forwarding "
)
2022-02-21 12:59:02 +03:00
parser . addoption (
" --test-read-only-source " ,
action = " store_true " ,
default = False ,
required = False ,
help = " Run testcases with a read-only database source "
)
2021-05-05 15:25:27 +03:00
2019-04-08 10:22:38 +03:00
#By default,
2022-09-15 15:30:01 +03:00
#1) Set test grouping to by class (--dist=loadfile)
#2) Set default parallelism to one
2019-04-08 10:22:38 +03:00
def pytest_cmdline_preparse ( config , args ) :
worker = os . environ . get ( ' PYTEST_XDIST_WORKER ' )
if ' xdist ' in sys . modules and not worker : # pytest-xdist plugin
num = 1
2022-09-15 15:30:01 +03:00
args [ : ] = [ ' --dist=loadfile ' , f ' -n { num } ' ] + args
2019-04-08 10:22:38 +03:00
def pytest_configure ( config ) :
2019-11-29 08:14:26 +03:00
# Pytest has removed the global pytest.config
# As a solution we are going to store it in PytestConf.config
PytestConf . config = config
2020-02-13 12:14:02 +03:00
if is_help_option_present ( config ) :
return
2019-04-08 10:22:38 +03:00
if is_master ( config ) :
2022-09-15 15:30:01 +03:00
assert not config . getoption ( ' --exitfirst ' ) , ' The " --exitfirst " / " -x " option does not work with xdist. \n See: https://github.com/pytest-dev/pytest-xdist/issues/54 '
if not ( config . getoption ( ' --hge-bin ' ) or config . getoption ( ' --hge-urls ' ) ) :
print ( " either --hge-bin or --hge-urls should be specified " )
if config . getoption ( ' --hge-bin ' ) and config . getoption ( ' --hge-urls ' ) :
print ( " only one of --hge-bin or --hge-urls should be specified " )
2019-04-08 10:22:38 +03:00
if not config . getoption ( ' --pg-urls ' ) :
print ( " pg-urls should be specified " )
config . hge_url_list = config . getoption ( ' --hge-urls ' )
2021-02-13 03:05:23 +03:00
config . pg_url_list = config . getoption ( ' --pg-urls ' )
2019-04-08 10:22:38 +03:00
if config . getoption ( ' -n ' , default = None ) :
xdist_threads = config . getoption ( ' -n ' )
2022-09-15 15:30:01 +03:00
assert config . getoption ( ' --hge-bin ' ) or xdist_threads < = len ( config . hge_url_list ) , " Not enough hge_urls specified, Required " + str ( xdist_threads ) + " , got " + str ( len ( config . hge_url_list ) )
2019-04-08 10:22:38 +03:00
assert xdist_threads < = len ( config . pg_url_list ) , " Not enough pg_urls specified, Required " + str ( xdist_threads ) + " , got " + str ( len ( config . pg_url_list ) )
2020-02-13 12:14:02 +03:00
@pytest.hookimpl ( )
def pytest_report_collectionfinish ( config , startdir , items ) :
"""
Collect server upgrade tests to the given file
"""
tests_file = config . getoption ( ' --collect-upgrade-tests-to-file ' )
2022-08-24 11:30:55 +03:00
tests = collections . OrderedDict ( )
2020-02-13 12:14:02 +03:00
if tests_file :
def is_upgrade_test ( item ) :
# Check if allow_server_upgrade_tests marker are present
# skip_server_upgrade_tests marker is not present
return item . get_closest_marker ( ' allow_server_upgrade_test ' ) \
and not item . get_closest_marker ( ' skip_server_upgrade_test ' )
with open ( tests_file , ' w ' ) as f :
upgrade_items = filter ( is_upgrade_test , items )
for item in upgrade_items :
# This test should be run separately,
# since its schema setup has function scope
if ' per_method_tests_db_state ' in item . fixturenames :
tests [ item . nodeid ] = True
elif any ( [ ( x in item . fixturenames )
for x in
[ ' per_class_tests_db_state ' ,
' per_class_db_schema_for_mutation_tests '
]
] ) :
# For this test, schema setup has class scope
# We can run a class of these tests at a time
tests [ item . parent . nodeid ] = True
# Assume tests can only be run separately
else :
tests [ item . nodeid ] = True
for test in tests . keys ( ) :
f . write ( test + ' \n ' )
return ' '
2019-04-08 10:22:38 +03:00
@pytest.hookimpl ( optionalhook = True )
def pytest_configure_node ( node ) :
2020-02-13 12:14:02 +03:00
if is_help_option_present ( node . config ) :
return
2022-09-15 15:30:01 +03:00
if not node . config . getoption ( ' --hge-bin ' ) :
node . workerinput [ " hge-url " ] = node . config . hge_url_list . pop ( )
2022-01-17 10:39:59 +03:00
node . workerinput [ " pg-url " ] = node . config . pg_url_list . pop ( )
2019-04-08 10:22:38 +03:00
2022-08-02 22:32:46 +03:00
def run_on_current_backend ( request : pytest . FixtureRequest ) :
current_backend = request . config . getoption ( ' --backend ' )
# Currently, we default all tests to run on Postgres with or without a --backend flag.
# As our test suite develops, we may consider running backend-agnostic tests on all
# backends, unless a specific `--backend` flag is passed.
desired_backends = set ( name for marker in request . node . iter_markers ( ' backend ' ) for name in marker . args ) or set ( [ ' postgres ' ] )
return current_backend in desired_backends
def per_backend_tests_fixture ( request : pytest . FixtureRequest ) :
"""
This fixture ignores backend - specific tests unless the relevant - - backend flag has been passed .
"""
if not run_on_current_backend ( request ) :
desired_backends = set ( name for marker in request . node . iter_markers ( ' backend ' ) for name in marker . args )
pytest . skip ( ' Skipping test. This test can run on ' + ' , ' . join ( desired_backends ) + ' . ' )
@pytest.fixture ( scope = ' class ' , autouse = True )
def per_backend_test_class ( request : pytest . FixtureRequest ) :
return per_backend_tests_fixture ( request )
@pytest.fixture ( scope = ' function ' , autouse = True )
def per_backend_test_function ( request : pytest . FixtureRequest ) :
return per_backend_tests_fixture ( request )
2022-09-28 12:19:47 +03:00
@pytest.fixture ( scope = ' session ' )
def pg_version ( request ) - > int :
pg_url : str = request . config . workerinput [ " pg-url " ]
with sqlalchemy . create_engine ( pg_url ) . connect ( ) as connection :
row = connection . execute ( ' show server_version_num ' ) . fetchone ( )
if not row :
raise Exception ( ' Could not get the PostgreSQL version. ' )
return int ( row [ ' server_version_num ' ] ) / / 10000
2022-08-15 17:29:02 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-15 15:30:01 +03:00
def pg_url ( request ) - > str :
return request . config . workerinput [ " pg-url " ]
2022-09-28 12:19:47 +03:00
# Any test caught by this would also be caught by `hge_skip_function`, but
# this is faster.
@pytest.fixture ( scope = ' class ' , autouse = True )
def hge_skip_class ( request : pytest . FixtureRequest , hge_server : Optional [ str ] ) :
hge_skip ( request , hge_server )
@pytest.fixture ( scope = ' function ' , autouse = True )
def hge_skip_function ( request : pytest . FixtureRequest , hge_server : Optional [ str ] ) :
hge_skip ( request , hge_server )
def hge_skip ( request : pytest . FixtureRequest , hge_server : Optional [ str ] ) :
# Let `hge_server` manage this stuff.
2022-09-15 15:30:01 +03:00
if hge_server :
2022-09-28 12:19:47 +03:00
return
# Ensure that the correct environment variables have been set for the given test.
hge_marker_env : dict [ str , str ] = { marker . args [ 0 ] : marker . args [ 1 ] for marker in request . node . iter_markers ( ' hge_env ' ) }
incorrect_env = { name : value for name , value in hge_marker_env . items ( ) if os . getenv ( name ) != value }
if len ( incorrect_env ) > 0 :
pytest . skip (
' This test expects the following environment variables: '
+ ' , ' . join ( [ f ' { name !r} = { value !r} ' for name , value in incorrect_env . items ( ) ] ) )
2022-09-15 15:30:01 +03:00
@pytest.fixture ( scope = ' class ' )
def postgis ( pg_url ) :
with sqlalchemy . create_engine ( pg_url ) . connect ( ) as connection :
2022-08-15 17:29:02 +03:00
connection . execute ( ' CREATE EXTENSION IF NOT EXISTS postgis ' )
connection . execute ( ' CREATE EXTENSION IF NOT EXISTS postgis_topology ' )
2022-09-15 15:30:01 +03:00
result = connection . execute ( ' SELECT PostGIS_lib_version() as postgis_version ' ) . fetchone ( )
if not result :
raise Exception ( ' Could not detect the PostGIS version. ' )
postgis_version : str = result [ ' postgis_version ' ]
2022-08-15 17:29:02 +03:00
if re . match ( ' ^3 \\ . ' , postgis_version ) :
connection . execute ( ' CREATE EXTENSION IF NOT EXISTS postgis_raster ' )
2022-09-28 12:19:47 +03:00
@pytest.fixture ( scope = ' session ' )
def hge_bin ( request : pytest . FixtureRequest ) - > Optional [ str ] :
return request . config . getoption ( ' --hge-bin ' ) # type: ignore
2022-08-15 17:29:02 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-28 12:19:47 +03:00
def hge_port ( ) - > int :
2022-09-15 15:30:01 +03:00
return fixtures . hge . hge_port ( )
2022-09-28 12:19:47 +03:00
@pytest.fixture ( scope = ' class ' )
def hge_url ( request : pytest . FixtureRequest , hge_bin : Optional [ str ] , hge_port : int ) - > str :
if hge_bin :
return f ' http://localhost: { hge_port } '
else :
return request . config . workerinput [ ' hge-url ' ] # type: ignore
# A hack to inject environment variables from other fixtures into HGE.
# All of this is because we cannot cleanly express dependencies between
# fixtures of the form "this loads before that, IF that is loaded".
#
# This is marked as "early" so the `fixture-order` plugin ensures that it is
# loaded _before_ `hge_server`. Similarly, any fixture using it must be at
# the same scope level and also marked as "early", to ensure that it is
# mutated before `hge_server` uses the data.
#
# In short, we use `early` to ensure that writes happen before reads.
@pytest.fixture ( scope = ' class ' )
@pytest.mark.early
def hge_fixture_env ( ) - > dict [ str , str ] :
return { }
@pytest.fixture ( scope = ' class ' )
def hge_key ( request ) - > Optional [ str ] :
return request . config . getoption ( ' --hge-key ' )
2022-09-15 15:30:01 +03:00
@pytest.fixture ( scope = ' class ' )
def hge_server (
request : pytest . FixtureRequest ,
2022-09-28 12:19:47 +03:00
hge_bin : Optional [ str ] ,
2022-09-15 15:30:01 +03:00
hge_port : int ,
2022-09-28 12:19:47 +03:00
hge_url : str ,
hge_fixture_env : dict [ str , str ] ,
2022-09-15 15:30:01 +03:00
pg_url : str ,
2022-09-28 12:19:47 +03:00
) - > Optional [ str ] :
if not hge_bin :
return None
return fixtures . hge . hge_server ( request , hge_bin , hge_port , hge_url , hge_fixture_env , pg_url )
2022-09-15 15:30:01 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-28 12:19:47 +03:00
def hge_ctx ( request , hge_url , pg_url , hge_server ) :
2022-09-15 15:30:01 +03:00
hge_ctx = HGECtx ( hge_url , pg_url , request . config )
yield hge_ctx
2019-04-08 10:22:38 +03:00
2018-09-18 09:21:57 +03:00
hge_ctx . teardown ( )
2022-09-15 15:30:01 +03:00
time . sleep ( 1 ) # TODO why do we sleep here?
2019-04-08 10:22:38 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-28 12:19:47 +03:00
@pytest.mark.early
def evts_webhook ( hge_fixture_env : dict [ str , str ] ) :
webhook_httpd = EvtsWebhookServer ( server_address = ( ' localhost ' , 5592 ) )
2019-04-08 10:22:38 +03:00
web_server = threading . Thread ( target = webhook_httpd . serve_forever )
web_server . start ( )
2022-09-28 12:19:47 +03:00
hge_fixture_env [ ' EVENT_WEBHOOK_HANDLER ' ] = webhook_httpd . url
2019-04-08 10:22:38 +03:00
yield webhook_httpd
webhook_httpd . shutdown ( )
webhook_httpd . server_close ( )
web_server . join ( )
2022-08-15 17:29:02 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-28 12:19:47 +03:00
@pytest.mark.early
def actions_fixture ( pg_version : int , hge_url : str , hge_key : Optional [ str ] , hge_fixture_env : dict [ str , str ] ) :
if pg_version < 10 :
pytest . skip ( ' Actions are not supported on Postgres version < 10 ' )
2020-03-20 09:46:45 +03:00
# Start actions' webhook server
2022-09-28 12:19:47 +03:00
webhook_httpd = ActionsWebhookServer ( hge_url , hge_key , server_address = ( ' localhost ' , 5593 ) )
2020-02-13 20:38:23 +03:00
web_server = threading . Thread ( target = webhook_httpd . serve_forever )
web_server . start ( )
2022-09-28 12:19:47 +03:00
hge_fixture_env [ ' ACTION_WEBHOOK_HANDLER ' ] = webhook_httpd . url
2020-02-13 20:38:23 +03:00
yield webhook_httpd
webhook_httpd . shutdown ( )
webhook_httpd . server_close ( )
web_server . join ( )
2021-08-09 13:20:04 +03:00
use_action_fixtures = pytest . mark . usefixtures (
2022-09-29 13:42:47 +03:00
' actions_fixture ' ,
2021-08-09 13:20:04 +03:00
' per_class_db_schema_for_mutation_tests ' ,
' per_method_db_data_for_mutation_tests '
)
2021-01-29 08:48:17 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-29 13:42:47 +03:00
@pytest.mark.early
def auth_hook ( hge_fixture_env : dict [ str , str ] ) :
server = auth_webhook_server . create_server ( )
server_thread = threading . Thread ( target = server . serve_forever )
server_thread . start ( )
hge_fixture_env [ ' HASURA_GRAPHQL_AUTH_HOOK ' ] = ' http://localhost:9876/auth '
ports . wait_for_port ( server . server_port )
yield server
auth_webhook_server . stop_server ( server )
2021-08-09 13:20:04 +03:00
2022-04-22 22:53:12 +03:00
@pytest.fixture ( scope = ' class ' )
def streaming_subscriptions_fixtures ( hge_ctx ) :
if not hge_ctx . streaming_subscriptions :
pytest . skip ( ' These tests are meant to be run with --test-streaming-subscriptions set with pytest ' )
return
2021-05-05 15:25:27 +03:00
@pytest.fixture ( scope = ' class ' )
def pro_tests_fixtures ( hge_ctx ) :
if not hge_ctx . pro_tests :
pytest . skip ( ' These tests are meant to be run with --pro-tests set ' )
return
2020-05-13 15:33:16 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-28 12:19:47 +03:00
@pytest.mark.early
def scheduled_triggers_evts_webhook ( hge_fixture_env : dict [ str , str ] ) :
webhook_httpd = EvtsWebhookServer ( server_address = ( ' localhost ' , 5594 ) )
2020-05-13 15:33:16 +03:00
web_server = threading . Thread ( target = webhook_httpd . serve_forever )
web_server . start ( )
2022-09-28 12:19:47 +03:00
hge_fixture_env [ ' SCHEDULED_TRIGGERS_WEBHOOK_DOMAIN ' ] = webhook_httpd . url
2020-05-13 15:33:16 +03:00
yield webhook_httpd
webhook_httpd . shutdown ( )
webhook_httpd . server_close ( )
web_server . join ( )
2020-03-26 14:52:20 +03:00
@pytest.fixture ( scope = ' class ' )
2022-09-15 00:41:28 +03:00
@pytest.mark.early
2022-09-28 12:19:47 +03:00
def gql_server ( request , hge_fixture_env : dict [ str , str ] ) :
2022-09-15 00:41:28 +03:00
port = 5000
hge_urls : list [ str ] = request . config . getoption ( ' --hge-urls ' )
graphql_server . set_hge_urls ( hge_urls )
server = HGECtxGQLServer ( port = port )
server . start_server ( )
2022-09-28 12:19:47 +03:00
hge_fixture_env [ ' REMOTE_SCHEMAS_WEBHOOK_DOMAIN ' ] = server . url
2022-09-15 00:41:28 +03:00
ports . wait_for_port ( port )
2020-03-26 14:52:20 +03:00
yield server
2022-09-15 00:41:28 +03:00
server . stop_server ( )
2020-05-13 15:33:16 +03:00
2019-04-08 10:22:38 +03:00
@pytest.fixture ( scope = ' class ' )
def ws_client ( request , hge_ctx ) :
2020-02-13 12:14:02 +03:00
"""
This fixture provides an Apollo GraphQL websockets client
"""
2019-05-10 09:05:11 +03:00
client = GQLWsClient ( hge_ctx , ' /v1/graphql ' )
2019-04-08 10:22:38 +03:00
time . sleep ( 0.1 )
yield client
client . teardown ( )
2018-10-28 21:27:49 +03:00
2021-08-24 19:25:12 +03:00
@pytest.fixture ( scope = ' class ' )
def ws_client_graphql_ws ( request , hge_ctx ) :
"""
This fixture provides an GraphQL - WS client
"""
client = GraphQLWSClient ( hge_ctx , ' /v1/graphql ' )
time . sleep ( 0.1 )
yield client
client . teardown ( )
2018-10-28 21:27:49 +03:00
@pytest.fixture ( scope = ' class ' )
2020-02-13 12:14:02 +03:00
def per_class_tests_db_state ( request , hge_ctx ) :
"""
Set up the database state for select queries .
Has a class level scope , since select queries does not change database state
Expects either ` dir ( ) ` method which provides the directory
with ` setup . yaml ` and ` teardown . yaml ` files
Or class variables ` setup_files ` and ` teardown_files ` that provides
2021-07-01 12:20:53 +03:00
the list of setup and teardown files respectively .
By default , for a postgres backend the setup and teardown is done via
the ` / v1 / query ` endpoint , to setup using the ` / v1 / metadata ` ( metadata setup )
and ` / v2 / query ` ( DB setup ) , set the ` setup_metadata_api_version ` to " v2 "
2018-10-28 21:27:49 +03:00
"""
2020-02-13 12:14:02 +03:00
yield from db_state_context ( request , hge_ctx )
@pytest.fixture ( scope = ' function ' )
def per_method_tests_db_state ( request , hge_ctx ) :
2018-10-28 21:27:49 +03:00
"""
2020-02-13 12:14:02 +03:00
This fixture sets up the database state for metadata operations
Has a function level scope , since metadata operations may change both the schema and data
Class method / variable requirements are similar to that of per_class_tests_db_state fixture
"""
yield from db_state_context ( request , hge_ctx )
@pytest.fixture ( scope = ' class ' )
def per_class_db_schema_for_mutation_tests ( request , hge_ctx ) :
"""
This fixture sets up the database schema for mutations .
It has a class level scope , since mutations does not change schema .
Expects either ` dir ( ) ` class method which provides the directory with ` schema_setup . yaml ` and ` schema_teardown . yaml ` files ,
or variables ` schema_setup_files ` and ` schema_teardown_files `
that provides the list of setup and teardown files respectively
"""
2021-08-09 13:20:04 +03:00
# setting the default metadata API version to v1
setup_metadata_api_version = getattr ( request . cls , ' setup_metadata_api_version ' , " v1 " )
2021-10-29 17:42:07 +03:00
( setup , teardown , schema_setup , schema_teardown , pre_setup , post_teardown ) = [
2021-10-01 15:52:19 +03:00
hge_ctx . backend_suffix ( filename ) + " .yaml "
2021-10-29 17:42:07 +03:00
for filename in [ ' setup ' , ' teardown ' , ' schema_setup ' , ' schema_teardown ' , ' pre_setup ' , ' post_teardown ' ]
2021-10-01 15:52:19 +03:00
]
if hge_ctx . is_default_backend :
if setup_metadata_api_version == " v1 " :
db_context = db_context_with_schema_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
' schema_setup_files ' , ' schema_setup.yaml ' ,
' schema_teardown_files ' , ' schema_teardown.yaml ' ,
2021-10-01 15:52:19 +03:00
)
else :
2022-08-02 22:32:46 +03:00
db_context = db_context_with_schema_common_new (
request , hge_ctx ,
' schema_setup_files ' , setup ,
' schema_teardown_files ' , teardown ,
schema_setup , schema_teardown ,
pre_setup , post_teardown ,
2021-10-01 15:52:19 +03:00
)
2021-08-09 13:20:04 +03:00
else :
2022-08-02 22:32:46 +03:00
db_context = db_context_with_schema_common_new (
request , hge_ctx ,
' schema_setup_files ' , setup ,
' schema_teardown_files ' , teardown ,
schema_setup , schema_teardown ,
pre_setup , post_teardown ,
2021-08-09 13:20:04 +03:00
)
2021-10-01 15:52:19 +03:00
yield from db_context
2020-02-13 12:14:02 +03:00
@pytest.fixture ( scope = ' function ' )
def per_method_db_data_for_mutation_tests ( request , hge_ctx , per_class_db_schema_for_mutation_tests ) :
"""
This fixture sets up the data for mutations .
Has a function level scope , since mutations may change data .
Having just the setup file ( s ) , or the teardown file ( s ) is allowed .
Expects either ` dir ( ) ` class method which provides the directory with ` values_setup . yaml ` and / or ` values_teardown . yaml ` files .
The class may provide ` values_setup_files ` variables which contains the list of data setup files ,
Or the ` values_teardown_files ` variable which provides the list of data teardown files .
"""
2021-10-01 15:52:19 +03:00
# Non-default (Postgres) backend tests expect separate setup and schema_setup
# files for v1/metadata and v2/query requests, respectively.
( values_setup , values_teardown ) = [
hge_ctx . backend_suffix ( filename ) + " .yaml "
for filename in [ ' values_setup ' , ' values_teardown ' ]
]
2020-02-13 12:14:02 +03:00
yield from db_context_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
' values_setup_files ' , values_setup ,
2021-10-01 15:52:19 +03:00
' values_teardown_files ' , values_teardown ,
2022-08-02 22:32:46 +03:00
skip_setup = False , skip_teardown = False
2020-02-13 12:14:02 +03:00
)
def db_state_context ( request , hge_ctx ) :
2021-03-11 21:17:41 +03:00
# Non-default (Postgres) backend tests expect separate setup and schema_setup
# files for v1/metadata and v2/query requests, respectively.
2021-10-29 17:42:07 +03:00
( setup , teardown , schema_setup , schema_teardown , pre_setup , post_teardown ) = [
2021-03-11 21:17:41 +03:00
hge_ctx . backend_suffix ( filename ) + " .yaml "
2021-10-29 17:42:07 +03:00
for filename in [ ' setup ' , ' teardown ' , ' schema_setup ' , ' schema_teardown ' , ' pre_setup ' , ' post_teardown ' ]
2021-03-11 21:17:41 +03:00
]
2020-02-13 12:14:02 +03:00
2021-07-01 12:20:53 +03:00
# setting the default metadata API version to v1
setup_metadata_api_version = getattr ( request . cls , ' setup_metadata_api_version ' , " v1 " )
2021-05-25 16:54:18 +03:00
if hge_ctx . is_default_backend :
2021-07-01 12:20:53 +03:00
if setup_metadata_api_version == " v1 " :
# setup the metadata and DB schema using the `/v1/query` endpoint
db_context = db_context_with_schema_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
' setup_files ' , ' setup.yaml ' ,
' teardown_files ' , ' teardown.yaml ' ,
)
2021-07-01 12:20:53 +03:00
elif setup_metadata_api_version == " v2 " :
# setup the metadata using the "/v1/metadata" and the DB schema using the `/v2/query` endpoints
2022-08-02 22:32:46 +03:00
db_context = db_context_with_schema_common_new (
request , hge_ctx ,
' setup_files ' , setup ,
' teardown_files ' , teardown ,
schema_setup , schema_teardown ,
pre_setup , post_teardown ,
2021-07-01 12:20:53 +03:00
)
2022-07-05 21:00:08 +03:00
else :
raise NotImplementedError ( ' Invalid API version. ' )
2021-03-11 21:17:41 +03:00
else :
2021-07-01 12:20:53 +03:00
# setup the metadata using the "/v1/metadata" and the DB schema using the `/v2/query` endpoints
2022-08-02 22:32:46 +03:00
db_context = db_context_with_schema_common_new (
request , hge_ctx ,
' setup_files ' , setup ,
' teardown_files ' , teardown ,
schema_setup , schema_teardown ,
pre_setup , post_teardown ,
2021-03-11 21:17:41 +03:00
)
yield from db_context
2020-02-13 12:14:02 +03:00
def db_context_with_schema_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
setup_files_attr , setup_default_file ,
teardown_files_attr , teardown_default_file ,
) :
2020-02-13 12:14:02 +03:00
( skip_setup , skip_teardown ) = [
request . config . getoption ( ' -- ' + x )
for x in [ ' skip-schema-setup ' , ' skip-schema-teardown ' ]
]
yield from db_context_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
setup_files_attr , setup_default_file ,
2020-02-13 12:14:02 +03:00
teardown_files_attr , teardown_default_file ,
2022-08-02 22:32:46 +03:00
skip_setup , skip_teardown
2020-02-13 12:14:02 +03:00
)
2022-08-02 22:32:46 +03:00
def db_context_with_schema_common_new (
request , hge_ctx ,
setup_files_attr , setup_default_file ,
teardown_files_attr , teardown_default_file ,
setup_sql_file , teardown_sql_file ,
pre_setup_file , post_teardown_file ,
) :
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
( skip_setup , skip_teardown ) = [
request . config . getoption ( ' -- ' + x )
for x in [ ' skip-schema-setup ' , ' skip-schema-teardown ' ]
]
2022-08-02 22:32:46 +03:00
yield from db_context_common_new (
request , hge_ctx ,
setup_files_attr , setup_default_file , setup_sql_file ,
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
teardown_files_attr , teardown_default_file , teardown_sql_file ,
2021-10-29 17:42:07 +03:00
pre_setup_file , post_teardown_file ,
2022-08-02 22:32:46 +03:00
skip_setup , skip_teardown
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
)
2020-02-13 12:14:02 +03:00
def db_context_common (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
setup_files_attr , setup_default_file ,
2020-02-13 12:14:02 +03:00
teardown_files_attr , teardown_default_file ,
2022-08-02 22:32:46 +03:00
skip_setup = True , skip_teardown = True
) :
2020-02-13 12:14:02 +03:00
def get_files ( attr , default_file ) :
files = getattr ( request . cls , attr , None )
if not files :
files = os . path . join ( request . cls . dir ( ) , default_file )
return files
setup = get_files ( setup_files_attr , setup_default_file )
teardown = get_files ( teardown_files_attr , teardown_default_file )
2021-10-01 15:52:19 +03:00
if hge_ctx . is_default_backend :
2022-08-02 22:32:46 +03:00
yield from setup_and_teardown_v1q (
request , hge_ctx ,
setup , teardown ,
skip_setup , skip_teardown
)
2021-10-01 15:52:19 +03:00
else :
2022-08-02 22:32:46 +03:00
yield from setup_and_teardown_v2q (
request , hge_ctx ,
setup , teardown ,
skip_setup , skip_teardown
)
2021-10-01 15:52:19 +03:00
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
def db_context_common_new (
2022-08-02 22:32:46 +03:00
request , hge_ctx ,
setup_files_attr , setup_default_file , setup_default_sql_file ,
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
teardown_files_attr , teardown_default_file , teardown_default_sql_file ,
2021-10-29 17:42:07 +03:00
pre_setup_file , post_teardown_file ,
2022-08-02 22:32:46 +03:00
skip_setup = True , skip_teardown = True
) :
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
def get_files ( attr , default_file ) :
files = getattr ( request . cls , attr , None )
if not files :
files = os . path . join ( request . cls . dir ( ) , default_file )
return files
setup = get_files ( setup_files_attr , setup_default_file )
teardown = get_files ( teardown_files_attr , teardown_default_file )
setup_default_sql_file = os . path . join ( request . cls . dir ( ) , setup_default_sql_file )
teardown_default_sql_file = os . path . join ( request . cls . dir ( ) , teardown_default_sql_file )
2021-10-29 17:42:07 +03:00
pre_setup_default_file = os . path . join ( request . cls . dir ( ) , pre_setup_file )
post_teardown_default_file = os . path . join ( request . cls . dir ( ) , post_teardown_file )
2022-08-02 22:32:46 +03:00
yield from setup_and_teardown (
request , hge_ctx ,
setup , teardown ,
setup_default_sql_file , teardown_default_sql_file ,
pre_setup_default_file , post_teardown_default_file ,
skip_setup , skip_teardown
)
def setup_and_teardown_v1q (
request , hge_ctx ,
setup_files , teardown_files ,
skip_setup = False , skip_teardown = False
) :
2022-07-05 21:00:08 +03:00
def v1q_f ( filepath ) :
if os . path . isfile ( filepath ) :
return hge_ctx . v1q_f ( filepath )
2020-02-13 12:14:02 +03:00
if not skip_setup :
run_on_elem_or_list ( v1q_f , setup_files )
yield
# Teardown anyway if any of the tests have failed
if request . session . testsfailed > 0 or not skip_teardown :
run_on_elem_or_list ( v1q_f , teardown_files )
2022-08-02 22:32:46 +03:00
def setup_and_teardown_v2q (
request , hge_ctx ,
setup_files , teardown_files ,
skip_setup = False , skip_teardown = False
) :
2022-07-05 21:00:08 +03:00
def v2q_f ( filepath ) :
if os . path . isfile ( filepath ) :
return hge_ctx . v2q_f ( filepath )
2021-10-01 15:52:19 +03:00
if not skip_setup :
run_on_elem_or_list ( v2q_f , setup_files )
yield
# Teardown anyway if any of the tests have failed
if request . session . testsfailed > 0 or not skip_teardown :
run_on_elem_or_list ( v2q_f , teardown_files )
2022-08-02 22:32:46 +03:00
def setup_and_teardown (
request , hge_ctx ,
setup_files , teardown_files ,
sql_schema_setup_file , sql_schema_teardown_file ,
pre_setup_file , post_teardown_file ,
skip_setup = False , skip_teardown = False
) :
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
def v2q_f ( f ) :
if os . path . isfile ( f ) :
2022-07-05 21:00:08 +03:00
try :
hge_ctx . v2q_f ( f )
except AssertionError :
try :
run_on_elem_or_list ( pre_post_metadataq_f , post_teardown_file )
except :
pass
raise
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
def metadataq_f ( f ) :
if os . path . isfile ( f ) :
2022-07-05 21:00:08 +03:00
try :
hge_ctx . v1metadataq_f ( f )
except AssertionError :
try :
# drop the sql setup, if the metadata calls fail
run_on_elem_or_list ( v2q_f , sql_schema_teardown_file )
run_on_elem_or_list ( pre_post_metadataq_f , post_teardown_file )
except :
pass
raise
2021-10-29 17:42:07 +03:00
def pre_post_metadataq_f ( f ) :
if os . path . isfile ( f ) :
2022-07-05 21:00:08 +03:00
hge_ctx . v1metadataq_f ( f )
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
if not skip_setup :
2021-10-29 17:42:07 +03:00
run_on_elem_or_list ( pre_post_metadataq_f , pre_setup_file )
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
run_on_elem_or_list ( v2q_f , sql_schema_setup_file )
run_on_elem_or_list ( metadataq_f , setup_files )
yield
# Teardown anyway if any of the tests have failed
if request . session . testsfailed > 0 or not skip_teardown :
run_on_elem_or_list ( metadataq_f , teardown_files )
run_on_elem_or_list ( v2q_f , sql_schema_teardown_file )
2021-10-29 17:42:07 +03:00
run_on_elem_or_list ( pre_post_metadataq_f , post_teardown_file )
[Preview] Inherited roles for postgres read queries
fixes #3868
docker image - `hasura/graphql-engine:inherited-roles-preview-48b73a2de`
Note:
To be able to use the inherited roles feature, the graphql-engine should be started with the env variable `HASURA_GRAPHQL_EXPERIMENTAL_FEATURES` set to `inherited_roles`.
Introduction
------------
This PR implements the idea of multiple roles as presented in this [paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/FGALanguageICDE07.pdf). The multiple roles feature in this PR can be used via inherited roles. An inherited role is a role which can be created by combining multiple singular roles. For example, if there are two roles `author` and `editor` configured in the graphql-engine, then we can create a inherited role with the name of `combined_author_editor` role which will combine the select permissions of the `author` and `editor` roles and then make GraphQL queries using the `combined_author_editor`.
How are select permissions of different roles are combined?
------------------------------------------------------------
A select permission includes 5 things:
1. Columns accessible to the role
2. Row selection filter
3. Limit
4. Allow aggregation
5. Scalar computed fields accessible to the role
Suppose there are two roles, `role1` gives access to the `address` column with row filter `P1` and `role2` gives access to both the `address` and the `phone` column with row filter `P2` and we create a new role `combined_roles` which combines `role1` and `role2`.
Let's say the following GraphQL query is queried with the `combined_roles` role.
```graphql
query {
employees {
address
phone
}
}
```
This will translate to the following SQL query:
```sql
select
(case when (P1 or P2) then address else null end) as address,
(case when P2 then phone else null end) as phone
from employee
where (P1 or P2)
```
The other parameters of the select permission will be combined in the following manner:
1. Limit - Minimum of the limits will be the limit of the inherited role
2. Allow aggregations - If any of the role allows aggregation, then the inherited role will allow aggregation
3. Scalar computed fields - same as table column fields, as in the above example
APIs for inherited roles:
----------------------
1. `add_inherited_role`
`add_inherited_role` is the [metadata API](https://hasura.io/docs/1.0/graphql/core/api-reference/index.html#schema-metadata-api) to create a new inherited role. It accepts two arguments
`role_name`: the name of the inherited role to be added (String)
`role_set`: list of roles that need to be combined (Array of Strings)
Example:
```json
{
"type": "add_inherited_role",
"args": {
"role_name":"combined_user",
"role_set":[
"user",
"user1"
]
}
}
```
After adding the inherited role, the inherited role can be used like single roles like earlier
Note:
An inherited role can only be created with non-inherited/singular roles.
2. `drop_inherited_role`
The `drop_inherited_role` API accepts the name of the inherited role and drops it from the metadata. It accepts a single argument:
`role_name`: name of the inherited role to be dropped
Example:
```json
{
"type": "drop_inherited_role",
"args": {
"role_name":"combined_user"
}
}
```
Metadata
---------
The derived roles metadata will be included under the `experimental_features` key while exporting the metadata.
```json
{
"experimental_features": {
"derived_roles": [
{
"role_name": "manager_is_employee_too",
"role_set": [
"employee",
"manager"
]
}
]
}
}
```
Scope
------
Only postgres queries and subscriptions are supported in this PR.
Important points:
-----------------
1. All columns exposed to an inherited role will be marked as `nullable`, this is done so that cell value nullification can be done.
TODOs
-------
- [ ] Tests
- [ ] Test a GraphQL query running with a inherited role without enabling inherited roles in experimental features
- [] Tests for aggregate queries, limit, computed fields, functions, subscriptions (?)
- [ ] Introspection test with a inherited role (nullability changes in a inherited role)
- [ ] Docs
- [ ] Changelog
Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com>
GitOrigin-RevId: 3b8ee1e11f5ceca80fe294f8c074d42fbccfec63
2021-03-08 14:14:13 +03:00
2020-02-13 12:14:02 +03:00
def run_on_elem_or_list ( f , x ) :
if isinstance ( x , str ) :
return [ f ( x ) ]
elif isinstance ( x , list ) :
return [ f ( e ) for e in x ]
def is_help_option_present ( config ) :
return any ( [
config . getoption ( x )
for x in [ ' --fixtures ' , ' --help ' , ' --collect-only ' ]
] )
2019-04-08 10:22:38 +03:00
def is_master ( config ) :
""" True if the code running the given pytest.config object is running in a xdist master
node or not running xdist at all .
"""
2022-01-17 10:39:59 +03:00
return not hasattr ( config , ' workerinput ' )