这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions server/src-lib/Hasura/Server/App.hs
Original file line number Diff line number Diff line change
Expand Up @@ -131,17 +131,17 @@ withSCUpdate scr logger action = do

data ServerCtx
= ServerCtx
{ scPGExecCtx :: PGExecCtx
, scConnInfo :: Q.ConnInfo
, scLogger :: L.Logger
, scCacheRef :: SchemaCacheRef
, scAuthMode :: AuthMode
, scManager :: HTTP.Manager
, scSQLGenCtx :: SQLGenCtx
, scEnabledAPIs :: S.HashSet API
, scInstanceId :: InstanceId
, scPlanCache :: E.PlanCache
, scLQState :: EL.LiveQueriesState
{ scPGExecCtx :: PGExecCtx
, scConnInfo :: Q.ConnInfo
, scLogger :: L.Logger
, scCacheRef :: SchemaCacheRef
, scAuthMode :: AuthMode
, scManager :: HTTP.Manager
, scSQLGenCtx :: SQLGenCtx
, scEnabledAPIs :: S.HashSet API
, scInstanceId :: InstanceId
, scPlanCache :: E.PlanCache
, scLQState :: EL.LiveQueriesState
}

data HandlerCtx
Expand Down Expand Up @@ -232,7 +232,7 @@ mkSpockAction qErrEncoder qErrModifier serverCtx handler = do
manager = scManager serverCtx

userInfoE <- liftIO $ runExceptT $ getUserInfo logger manager headers authMode
userInfo <- either (logAndThrow req reqBody False) return userInfoE
userInfo <- either (logAndThrow req reqBody False . qErrModifier) return userInfoE

let handlerState = HandlerCtx serverCtx reqBody userInfo headers

Expand Down
54 changes: 40 additions & 14 deletions server/tests-py/test_jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,22 @@ def mk_claims(conf, claims):
else:
return claims

class TestJWTBasic:
@pytest.mark.parametrize('endpoint', ['/v1/graphql', '/v1alpha1/graphql'])
class TestJWTBasic():

def test_jwt_valid_claims_success(self, hge_ctx):
def test_jwt_valid_claims_success(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-allowed-roles': ['user', 'editor'],
'x-hasura-default-role': 'user'
})
token = jwt.encode(self.claims, hge_ctx.hge_jwt_key, algorithm='RS512').decode('utf-8')
self.conf['headers']['Authorization'] = 'Bearer ' + token
self.conf['url'] = endpoint
self.conf['status'] = 200
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_invalid_role_in_request_header(self, hge_ctx):
def test_jwt_invalid_role_in_request_header(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-allowed-roles': ['contractor', 'editor'],
Expand All @@ -67,10 +69,14 @@ def test_jwt_invalid_role_in_request_header(self, hge_ctx):
'message': 'Your current role is not in allowed roles'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_no_allowed_roles_in_claim(self, hge_ctx):
def test_jwt_no_allowed_roles_in_claim(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-default-role': 'user'
Expand All @@ -86,10 +92,14 @@ def test_jwt_no_allowed_roles_in_claim(self, hge_ctx):
'message': 'JWT claim does not contain x-hasura-allowed-roles'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_invalid_allowed_roles_in_claim(self, hge_ctx):
def test_jwt_invalid_allowed_roles_in_claim(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-allowed-roles': 'user',
Expand All @@ -106,10 +116,14 @@ def test_jwt_invalid_allowed_roles_in_claim(self, hge_ctx):
'message': 'invalid x-hasura-allowed-roles; should be a list of roles'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_no_default_role(self, hge_ctx):
def test_jwt_no_default_role(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-allowed-roles': ['user'],
Expand All @@ -125,10 +139,14 @@ def test_jwt_no_default_role(self, hge_ctx):
'message': 'JWT claim does not contain x-hasura-default-role'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_expired(self, hge_ctx):
def test_jwt_expired(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-default-role': 'user',
Expand All @@ -148,10 +166,14 @@ def test_jwt_expired(self, hge_ctx):
'message': 'Could not verify JWT: JWTExpired'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

def test_jwt_invalid_signature(self, hge_ctx):
def test_jwt_invalid_signature(self, hge_ctx, endpoint):
self.claims['https://hasura.io/jwt/claims'] = mk_claims(hge_ctx.hge_jwt_conf, {
'x-hasura-user-id': '1',
'x-hasura-default-role': 'user',
Expand All @@ -170,7 +192,11 @@ def test_jwt_invalid_signature(self, hge_ctx):
'message': 'Could not verify JWT: JWSError JWSInvalidSignature'
}]
}
self.conf['status'] = 400
self.conf['url'] = endpoint
if endpoint == '/v1/graphql':
self.conf['status'] = 200
if endpoint == '/v1alpha1/graphql':
self.conf['status'] = 400
check_query(hge_ctx, self.conf, add_auth=False)

@pytest.fixture(autouse=True)
Expand Down
29 changes: 29 additions & 0 deletions server/tests-py/test_v1alpha1_endpoint.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import yaml
import pytest
#from validate import check_query, test_forbidden_when_admin_secret_reqd, test_forbidden_webhook
from validate import check_query
import validate
from super_classes import DefaultTestSelectQueries
from context import GQLWsClient

Expand All @@ -11,6 +13,33 @@ class TestV1Alpha1GraphQLErrors(DefaultTestSelectQueries):
def dir(cls):
return 'queries/graphql_query/v1alpha1/errors'

def test_v1alpha1_authorization_error(self, hge_ctx):
gql_query = """
query {
author {
id
name
}
}
"""
http_conf = {
'url': '/v1alpha1/graphql',
'status': 200,
'query': {'query': gql_query},
}

if hge_ctx.hge_key is not None and hge_ctx.hge_webhook is None and hge_ctx.hge_jwt_key is None:
# Test whether it is forbidden when incorrect/no admin_secret is specified
validate.test_forbidden_when_admin_secret_reqd(hge_ctx, http_conf)

elif hge_ctx.hge_webhook is not None:
if not hge_ctx.webhook_insecure:
# Check whether the output is also forbidden when webhook returns forbidden
validate.test_forbidden_webhook(hge_ctx, http_conf)
else:
assert True


@pytest.mark.parametrize('transport', ['http', 'websocket'])
def test_v1alpha1_validation_error(self, hge_ctx, transport):
gql_query = """
Expand Down
25 changes: 22 additions & 3 deletions server/tests-py/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,22 @@ def check_event(hge_ctx, evts_webhook, trig_name, table, operation, exp_ev_data,


def test_forbidden_when_admin_secret_reqd(hge_ctx, conf):
if conf['url'] == '/v1/graphql':
if conf['status'] == 404:
status = [404]
else:
status = [200]
else:
status = [401, 404]

headers = {}
if 'headers' in conf:
headers = conf['headers']

# Test without admin secret
code, resp = hge_ctx.anyq(conf['url'], conf['query'], headers)
assert code in [401,404], "\n" + yaml.dump({
#assert code in [401,404], "\n" + yaml.dump({
assert code in status, "\n" + yaml.dump({
"expected": "Should be access denied as admin secret is not provided",
"actual": {
"code": code,
Expand All @@ -76,7 +85,8 @@ def test_forbidden_when_admin_secret_reqd(hge_ctx, conf):
# Test with random admin secret
headers['X-Hasura-Admin-Secret'] = base64.b64encode(os.urandom(30))
code, resp = hge_ctx.anyq(conf['url'], conf['query'], headers)
assert code in [401,404], "\n" + yaml.dump({
#assert code in [401,404], "\n" + yaml.dump({
assert code in status, "\n" + yaml.dump({
"expected": "Should be access denied as an incorrect admin secret is provided",
"actual": {
"code": code,
Expand All @@ -86,9 +96,18 @@ def test_forbidden_when_admin_secret_reqd(hge_ctx, conf):


def test_forbidden_webhook(hge_ctx, conf):
if conf['url'] == '/v1/graphql':
if conf['status'] == 404:
status = [404]
else:
status = [200]
else:
status = [401, 404]

h = {'Authorization': 'Bearer ' + base64.b64encode(base64.b64encode(os.urandom(30))).decode('utf-8')}
code, resp = hge_ctx.anyq(conf['url'], conf['query'], h)
assert code in [401,404], "\n" + yaml.dump({
#assert code in [401,404], "\n" + yaml.dump({
assert code in status, "\n" + yaml.dump({
"expected": "Should be access denied as it is denied from webhook",
"actual": {
"code": code,
Expand Down