def test_webhooks(self): with contextlib.ExitStack() as stack: stack.enter_context( mock.patch("cowbird.services.impl.magpie.Magpie", side_effect=utils.MockMagpieService)) data = { "event": "created", "user_name": "test_user", "callback_url": "http://magpie.domain.ca/tmp/109e1d0d-e27c-4601-9d45-984c9b61ebff" } resp = utils.test_request(self.app, "POST", "/webhooks/users", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") utils.check_response_basic_info(resp) magpie = ServiceFactory().get_service("Magpie") assert len(magpie.json()["event_users"]) == 1 assert magpie.json()["event_users"][0] == data["user_name"] data["event"] = "deleted" data.pop("callback_url") resp = utils.test_request(self.app, "POST", "/webhooks/users", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") assert len(magpie.json()["event_users"]) == 0 data = { "event": "created", "service_name": "string", "resource_id": "string", "resource_full_name": "thredds/birdhouse/file.nc", "name": "read", "access": "allow", "scope": "recursive", "user": "******", "group": "string" } resp = utils.test_request(self.app, "POST", "/webhooks/permissions", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") magpie = ServiceFactory().get_service("Magpie") assert len(magpie.json()["event_perms"]) == 1 assert magpie.json( )["event_perms"][0] == data["resource_full_name"] data["event"] = "deleted" resp = utils.test_request(self.app, "POST", "/webhooks/permissions", json=data) utils.check_response_basic_info(resp, 200, expected_method="POST") assert len(magpie.json()["event_perms"]) == 0
def test_evaluate_call_recursive_safeguard(self): """ Validate use case if internal function that handles formatting and generation of a resulting HTTP response raises itself an error (because of implementation issue), while it is processing another pre-raised error, that it does not end up into an endless recursive call stack of raised errors. """ mock_calls = {"counter": 0} def mock_raise(*_, **__): # avoid raising forever if the real safeguard fails doing its job if mock_calls["counter"] >= 2 * ax.RAISE_RECURSIVE_SAFEGUARD_MAX: return TypeError() mock_calls["counter"] += 1 raise TypeError() def mock_lambda_call(*_, **__): ax.evaluate_call(lambda: int("x")) try: app = utils.get_test_magpie_app() with mock.patch( "magpie.api.exception.generate_response_http_format", side_effect=mock_raise): with mock.patch("magpie.api.login.login.get_session", side_effect=mock_lambda_call): # Call request that ends up calling the response formatter via 'evaluate_call' itself raising to # trigger 'mock_raise' recursively within 'raise_http' function. # Since tweens are set up to format all response prior to return, the raised error will itself # call 'raise_http' again each time operation fails, creating recursive raises. # If recursive safeguard does its job, it should end up raising 'HTTPInternalServerError' directly # (without further formatting attempt when reaching the MAX value), stopping the endless loop. utils.test_request(app, "GET", "/session", expect_errors=True) except AssertionError: # Request called with above 'test_request' should catch the final 'HTTPInternalServerError' that is # raised directly instead of usual TestResponse returned. That error is again re-raised as 'AssertionError' pass except Exception as exc: self.fail( "unexpected error during request creation should not raise: {}" .format(exc)) # if our counter reached higher than the MAX (i.e.: 2*MAX from mock), the safeguard did not do its job # if it did not get called at least more than once, use cases did not really get tested utils.check_val_is_in( mock_calls["counter"], list(range(2, ax.RAISE_RECURSIVE_SAFEGUARD_MAX + 1))) # noqa
def test_response_metadata(): """ Validate that regardless of response type (success/error) and status-code, metadata details are added. note: test only locally to avoid remote server side-effects and because mock cannot be done remotely """ def raise_request(*_, **__): raise TypeError() app = utils.get_test_magpie_app() # all paths below must be publicly accessible for code, method, path, kwargs in [ (200, "GET", "/session", {}), # FIXME: sort out 400 vs 422 everywhere (https://github.com/Ouranosinc/Magpie/issues/359) # (400, "POST", "/signin", {"body": {}}), # missing credentials (401, "GET", "/services", {}), # anonymous unauthorized (404, "GET", "/random", {}), (405, "POST", "/users/{}".format("MAGPIE_LOGGED_USER"), {"body": {}}), (406, "GET", "/session", {"headers": {"Accept": "application/pdf"}}), # 409: need connection to test conflict, no route available without so (other tests validates them though) (422, "POST", "/signin", {"body": {"user_name": "!!!!"}}), # invalid format (500, "GET", "/json", {}), # see mock ]: with mock.patch("magpie.api.schemas.generate_api_schema", side_effect=raise_request): headers = {"Accept": CONTENT_TYPE_JSON, "Content-Type": CONTENT_TYPE_JSON} headers.update(kwargs.get("headers", {})) kwargs.pop("headers", None) resp = utils.test_request(app, method, path, expect_errors=True, headers=headers, **kwargs) # following util check validates all expected request metadata in response body utils.check_response_basic_info(resp, expected_code=code, expected_method=method)
def mock_request(*args, **kwargs): method, url, args = args[0], args[1], args[2:] path = url.replace(test_url, "") # because CLI utility does multiple login tests, we must force TestApp logout to forget session # otherwise, error is raised because of user session mismatch between previous login and new one requested if path.startswith("/signin"): utils.check_or_try_logout_user(test_app) return utils.test_request(test_app, method, path, *args, **kwargs)
def test_homepage(self): resp = utils.test_request(self.app, "GET", "/") body = utils.check_response_basic_info(resp) utils.check_val_is_in("name", body) utils.check_val_is_in("title", body) utils.check_val_is_in("contact", body) utils.check_val_is_in("description", body) utils.check_val_is_in("documentation", body) utils.check_val_is_in("cowbird", body["name"])
def test_response_metadata(): """ Validate that regardless of response type (success/error) and status-code, metadata details are added. note: test only locally to avoid remote server side-effects and because mock cannot be done remotely """ class MockService(object): def name(self): raise TypeError() app = utils.get_test_app() # all paths below must be publicly accessible for i, (code, method, path, kwargs) in enumerate( [ (200, "GET", "", {}), (400, "GET", "/services/!!!!", {}), # invalid format # (401, "GET", "/services", {}), # anonymous unauthorized (404, "GET", "/random", {}), (405, "POST", "/json", { "body": {} }), (406, "GET", "/api", { "headers": { "Accept": "application/pdf" } }), # 409: need connection to test conflict, no route available without so (other tests validates them though) # (422, "POST", "/services", {"body": {"name": 1}}), # invalid field type # FIXME: route impl required (500, "GET", "/services", {}), # see mock ], start=1): with contextlib.ExitStack() as stack: if code == 500: stack.enter_context( mock.patch("cowbird.services.impl.magpie.Magpie", side_effect=MockService)) headers = { "Accept": CONTENT_TYPE_JSON, "Content-Type": CONTENT_TYPE_JSON } headers.update(kwargs.get("headers", {})) kwargs.pop("headers", None) resp = utils.test_request(app, method, path, expect_errors=True, headers=headers, **kwargs) # following util check validates all expected request metadata in response body msg = "\n[Test: #{}, Code: {}]".format(i, code) utils.check_response_basic_info(resp, expected_code=code, expected_method=method, extra_message=msg)
def test_magpie_prefix_direct_request(self): base_url = "http://localhost" for url in ["http://localhost", "http://localhost/magpie"]: app = utils.get_test_magpie_app({"magpie.url": url}) path = "/version" resp = utils.test_request(app, "GET", path) utils.check_response_basic_info(resp) utils.check_val_equal( resp.request.url, base_url + path, "Proxied path should have been auto-resolved [URL: {}].". format(url))
def test_magpie_prefix_request_with_multiple_route_url(self): """ Test multiple request routing with fixed "MAGPIE_URL" within the API application. Signin with invalid credentials will call "/signin" followed by sub-request "/signin_internal" and finally "ZigguratSignInBadAuth". Both "/signin" and "ZigguratSignInBadAuth" use "get_multiformat_body". """ from magpie.api.requests import get_value_multiformat_body_checked as real_multiform_post_checked base_url = "http://localhost" def mock_get_post(real_func, *args, **kwargs): if args[1] != "password": return real_func(*args, **kwargs) request, args = args[0], args[1:] utils.check_val_equal( request.url, base_url + _paths.pop(0), "Proxied path should have been auto-resolved [URL: {}].". format(url)) return real_func(request, *args, **kwargs) for url in ["http://localhost", "http://localhost/magpie"]: # paths are reduced (pop in mock) each time a post to get the 'password' is called in 'login' module # this combination should happen twice, one in signin route and another on the redirected internal login _paths = ["/signin", "/signin_internal"] app = utils.get_test_magpie_app({"magpie.url": url}) with mock.patch( "magpie.api.requests.get_value_multiformat_body_checked", side_effect=lambda *_, **__: mock_get_post( real_multiform_post_checked, *_, **__)): data = {"user_name": "foo", "password": "******"} headers = { "Content-Type": CONTENT_TYPE_JSON, "Accept": CONTENT_TYPE_JSON } resp = utils.test_request(app, "POST", _paths[0], json=data, headers=headers, expect_errors=True) if LooseVersion(self.version) < LooseVersion("0.10.0"): # user name doesn't exist utils.check_response_basic_info(resp, expected_code=406, expected_method="POST") else: # invalid username/password credentials utils.check_response_basic_info(resp, expected_code=401, expected_method="POST")
def check_api_resource_permissions(resource_permissions): for _r_id, _r_perms in resource_permissions: urp_path = "/users/{}/resources/{}/permissions".format( self.test_user_name, _r_id) urp_resp = utils.test_request(self, "GET", urp_path) urp_body = utils.check_response_basic_info(urp_resp) ur_perms = [ perm.json() for perm in _r_perms if isinstance(perm, PermissionSet) ] for perm in ur_perms: perm["type"] = PermissionType.DIRECT.value permissions = urp_body["permissions"] for perm in permissions: perm.pop("reason", None) # >= 3.4, don't care for this test utils.check_all_equal(permissions, ur_perms, any_order=True)
def test_magpie_homepage(): from magpie.constants import get_constant as real_get_constant # pylint: disable=W0404,reimported def mock_get_constant(*args, **kwargs): if args[0] == "MAGPIE_UI_ENABLED": return False return real_get_constant(*args, **kwargs) with mock.patch("magpie.constants.get_constant", side_effect=mock_get_constant), \ mock.patch("magpie.api.home.get_constant", side_effect=mock_get_constant): app = utils.get_test_magpie_app() resp = utils.test_request(app, "GET", "/") body = utils.check_response_basic_info(resp) utils.check_val_is_in("name", body) utils.check_val_is_in("title", body) utils.check_val_is_in("contact", body) utils.check_val_is_in("description", body) utils.check_val_is_in("documentation", body) utils.check_val_is_in("magpie", body["name"])
def test_register_permissions_missing_group_create_new_entries(self): utils.TestSetup.create_TestService( self, override_service_name=self.test_perm_svc_name, override_service_type=ServiceAPI.service_type) utils.TestSetup.delete_TestGroup( self, override_group_name=self.test_perm_grp_name) session = get_db_session_from_settings(self.app.app.registry.settings) svc_perm = Permission.READ_MATCH res1_perm = Permission.READ res2_perm = Permission.WRITE res1_name = "test-resource" res2_name = "sub-test-resource" perm_config = { "permissions": [ { "service": self.test_perm_svc_name, "permission": svc_perm.value, "action": "create", "group": self.test_perm_grp_name, }, { "service": self.test_perm_svc_name, "resource": res1_name, "permission": res1_perm.value, "action": "create", "group": self.test_perm_grp_name, }, { "service": self.test_perm_svc_name, "resource": res1_name + "/" + res2_name, "permission": res2_perm.value, "action": "create", "group": self.test_perm_grp_name, }, ] } utils.check_no_raise( lambda: register.magpie_register_permissions_from_config( perm_config, db_session=session)) # check that all service, resources, group are created correctly services = utils.TestSetup.get_RegisteredServicesList(self) utils.check_val_is_in(self.test_perm_svc_name, [s["service_name"] for s in services]) groups = utils.TestSetup.get_RegisteredGroupsList(self) utils.check_val_is_in(self.test_perm_grp_name, groups) resp = utils.test_request( self.app, "GET", "/services/{}/resources".format(self.test_perm_svc_name)) body = utils.check_response_basic_info(resp) svc_res = body[self.test_perm_svc_name]["resources"] svc_res_id = body[self.test_perm_svc_name]["resource_id"] utils.check_val_is_in(res1_name, [svc_res[r]["resource_name"] for r in svc_res]) res1_id = [ svc_res[r]["resource_id"] for r in svc_res if svc_res[r]["resource_name"] == res1_name ][0] res1_sub = svc_res[str(res1_id)]["children"] utils.check_val_is_in(res2_name, [res1_sub[r]["resource_name"] for r in res1_sub]) res2_id = [ res1_sub[r]["resource_id"] for r in res1_sub if res1_sub[r]["resource_name"] == res2_name ][0] # check that all permissions are created correctly path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, svc_res_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_is_in(svc_perm.value, body["permission_names"]) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res1_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_is_in(res1_perm.value, body["permission_names"]) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res2_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_is_in(res2_perm.value, body["permission_names"])
def test_register_permissions_existing_group_without_intermediate_entries( self): utils.TestSetup.create_TestService( self, override_service_name=self.test_perm_svc_name, override_service_type=ServiceAPI.service_type) utils.TestSetup.create_TestGroup( self, override_group_name=self.test_perm_grp_name) session = get_db_session_from_settings(self.app.app.registry.settings) res1_name = "test-resource" res2_name = "sub-test-resource" res3_name = "sub-sub-test-resource" res3_perm = Permission.WRITE_MATCH perm_config = { "permissions": [ { "service": self.test_perm_svc_name, # exists "resource": res1_name + "/" + res2_name + "/" + res3_name, # none exist, all created for perm "permission": res3_perm.value, # perm only to lowest child "action": "create", "group": self.test_perm_grp_name, }, ] } utils.check_no_raise( lambda: register.magpie_register_permissions_from_config( perm_config, db_session=session)) # check that all service, resources, group are created correctly services = utils.TestSetup.get_RegisteredServicesList(self) utils.check_val_is_in(self.test_perm_svc_name, [s["service_name"] for s in services]) groups = utils.TestSetup.get_RegisteredGroupsList(self) utils.check_val_is_in(self.test_perm_grp_name, groups) resp = utils.test_request( self.app, "GET", "/services/{}/resources".format(self.test_perm_svc_name)) body = utils.check_response_basic_info(resp) svc_res = body[self.test_perm_svc_name]["resources"] svc_res_id = body[self.test_perm_svc_name]["resource_id"] utils.check_val_is_in(res1_name, [svc_res[r]["resource_name"] for r in svc_res]) res1_id = [ svc_res[r]["resource_id"] for r in svc_res if svc_res[r]["resource_name"] == res1_name ][0] res1_sub = svc_res[str(res1_id)]["children"] utils.check_val_is_in(res2_name, [res1_sub[r]["resource_name"] for r in res1_sub]) res2_id = [ res1_sub[r]["resource_id"] for r in res1_sub if res1_sub[r]["resource_name"] == res2_name ][0] res2_sub = res1_sub[str(res2_id)]["children"] utils.check_val_is_in(res3_name, [res2_sub[r]["resource_name"] for r in res2_sub]) res3_id = [ res2_sub[r]["resource_id"] for r in res2_sub if res2_sub[r]["resource_name"] == res3_name ][0] # check that all permissions are created correctly (only for final item) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, svc_res_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_equal(body["permission_names"], []) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res1_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_equal(body["permission_names"], []) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res2_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_equal(body["permission_names"], []) path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res3_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_all_equal(body["permission_names"], [res3_perm.value])
def run_batch_update_user_command(test_app, expected_users, create_command_xargs, delete_command_xargs): """ Tests batch user creation and deletion of the CLI utility. Because CLI utility employs requests that cannot be mocked if executed through sub-process, we call it directly. """ test_admin_usr = get_constant("MAGPIE_TEST_ADMIN_USERNAME", raise_not_set=False, raise_missing=False) test_admin_pwd = get_constant("MAGPIE_TEST_ADMIN_PASSWORD", raise_not_set=False, raise_missing=False) if not test_admin_usr or not test_admin_pwd: test_admin_usr = get_constant("MAGPIE_ADMIN_USER") test_admin_pwd = get_constant("MAGPIE_ADMIN_PASSWORD") test_url = "http://localhost" def mock_request(*args, **kwargs): method, url, args = args[0], args[1], args[2:] path = url.replace(test_url, "") # because CLI utility does multiple login tests, we must force TestApp logout to forget session # otherwise, error is raised because of user session mismatch between previous login and new one requested if path.startswith("/signin"): utils.check_or_try_logout_user(test_app) return utils.test_request(test_app, method, path, *args, **kwargs) def run_command(operation_name, operation_args): with tempfile2.TemporaryDirectory() as tmpdir: with mock.patch("requests.Session.request", side_effect=mock_request): with mock.patch("requests.request", side_effect=mock_request): cmd = [ test_url, test_admin_usr, test_admin_pwd, "-o", tmpdir ] + operation_args assert batch_update_users.main( cmd) == 0, "failed execution due to invalid arguments" assert len( os.listdir(tmpdir) ) == 1, "utility should have produced 1 output file" file = os.path.join(tmpdir, os.listdir(tmpdir)[0]) utils.check_val_is_in(operation_name, file) assert os.path.isfile(file) with open(file, "r") as fd: file_text = fd.read() assert all([test_user in file_text for test_user in expected_users]), \ "all users should have been processed and logged in output result file" # cleanup in case of previous failure _, test_admin_cookies = utils.check_or_try_login_user( test_app, username=test_admin_usr, password=test_admin_pwd) for user in expected_users: utils.TestSetup.delete_TestUser( test_app, override_user_name=user, # noqa override_headers={}, override_cookies=test_admin_cookies) utils.check_or_try_logout_user(test_app) # test user creation and validate that users were all created run_command("create", create_command_xargs) utils.check_or_try_logout_user(test_app) _, test_admin_cookies = utils.check_or_try_login_user( test_app, username=test_admin_usr, password=test_admin_pwd) resp = utils.test_request(test_app, "GET", "/users", cookies=test_admin_cookies) body = utils.check_response_basic_info(resp) for user in expected_users: utils.check_val_is_in(user, body["user_names"]) # test user deletion and validate users are all deleted run_command("delete", ["-d"] + delete_command_xargs) utils.check_or_try_logout_user(test_app) _, test_admin_cookies = utils.check_or_try_login_user( test_app, username=test_admin_usr, password=test_admin_pwd) resp = utils.test_request(test_app, "GET", "/users", cookies=test_admin_cookies) body = utils.check_response_basic_info(resp) for user in expected_users: utils.check_val_not_in(user, body["user_names"])
def test_EditUser_ApplyPermissions(self): """ Verifies that UI button operations are working for the following workflow: 0. Goto Edit User page. 1. Change ``service-type`` tab to display services of type :class:`ServiceAPI`. 2. Set new permissions onto an existing resources and submit them with ``Apply`` button. 3. Verify the permissions are selected and displayed on page reload. 4. Remove and modify permission from existing resource and submit. 5. Validate that changes are reflected. Note: Only implemented locally with form submission of ``TestApp``. """ utils.warn_version(self, "update permission modifiers with option select", "3.0", skip=True) # make sure any sub-resource are all deleted to avoid conflict, then recreate service to add sub-resource utils.TestSetup.delete_TestService(self) body = utils.TestSetup.create_TestService(self) info = utils.TestSetup.get_ResourceInfo(self, override_body=body) svc_id, svc_name = info["resource_id"], info["service_name"] res_name = "resource1" sub_name = "resource2" body = utils.TestSetup.create_TestResource( self, parent_resource_id=svc_id, override_resource_name=res_name) info = utils.TestSetup.get_ResourceInfo(self, override_body=body) res_id = info["resource_id"] body = utils.TestSetup.create_TestResource( self, parent_resource_id=res_id, override_resource_name=sub_name) info = utils.TestSetup.get_ResourceInfo(self, override_body=body) sub_id = info["resource_id"] utils.TestSetup.create_TestGroup(self) utils.TestSetup.create_TestUser(self) # utilities for later tests def to_ui_permission(permission): # type: (Union[Str, PermissionSet]) -> Str return permission.explicit_permission if isinstance( permission, PermissionSet) else "" def check_ui_resource_permissions(perm_form, resource_id, permissions): select_res_id = "permission_resource_{}".format(resource_id) select_options = perm_form.fields[ select_res_id] # contains multiple select, one per applicable permission utils.check_val_equal( len(select_options), 2, msg="Always 2 select combobox expected (read and write).") select_values = [select.value for select in select_options] if not permissions: utils.check_all_equal( ["", ""], select_values, msg="When no permission is selected, values are empty") return # value must match exactly the *explicit* permission representation for r_perm in permissions: utils.check_val_is_in(r_perm, select_values) def check_api_resource_permissions(resource_permissions): for _r_id, _r_perms in resource_permissions: urp_path = "/users/{}/resources/{}/permissions".format( self.test_user_name, _r_id) urp_resp = utils.test_request(self, "GET", urp_path) urp_body = utils.check_response_basic_info(urp_resp) ur_perms = [ perm.json() for perm in _r_perms if isinstance(perm, PermissionSet) ] for perm in ur_perms: perm["type"] = PermissionType.DIRECT.value permissions = urp_body["permissions"] for perm in permissions: perm.pop("reason", None) # >= 3.4, don't care for this test utils.check_all_equal(permissions, ur_perms, any_order=True) # 0. goto user edit page (default first service selected) path = "/ui/users/{}/default".format(self.test_user_name) resp = utils.test_request(self, "GET", path) # 1. change to 'api' service-type, validate created test resources are all displayed in resource tree resp = resp.click(self.test_service_type ) # tabs are '<a href=...>{service-type}</a>' body = utils.check_ui_response_basic_info(resp) text = body.replace("\n", "").replace(" ", "") # ignore HTML formatting tree_keys = re.findall(r"<div class=\"tree-key\">(.*?)</div>", text) for r_name in [svc_name, res_name, sub_name]: utils.check_val_is_in( r_name, tree_keys, msg="Resource '{}' expected to be listed in tree.".format( r_name)) # 2. apply new permissions # 2.1 validate all initial values of permissions are empty res_form_name = "resources_permissions" # form that wraps the displayed resource tree res_form_submit = "edit_permissions" # id of the Apply 'submit' input of the form res_perm_form = resp.forms[res_form_name] for r_id in [svc_id, res_id, sub_id]: check_ui_resource_permissions(res_perm_form, r_id, []) # 2.2 apply new # NOTE: because tree-view is created by reversed order of permissions, we must provide them as [WRITE, READ] svc_perm1 = PermissionSet(Permission.READ, Access.ALLOW, Scope.RECURSIVE) svc_perms = ["", svc_perm1] res_perm1 = PermissionSet(Permission.READ, Access.DENY, Scope.RECURSIVE) res_perm2 = PermissionSet(Permission.WRITE, Access.ALLOW, Scope.RECURSIVE) res_perms = [res_perm2, res_perm1] sub_perm1 = PermissionSet(Permission.READ, Access.ALLOW, Scope.MATCH) sub_perms = ["", sub_perm1] data = { # set value for following correspond to 'select' option that was chosen "permission_resource_{}".format(svc_id): [to_ui_permission(perm) for perm in svc_perms], "permission_resource_{}".format(res_id): [to_ui_permission(perm) for perm in res_perms], "permission_resource_{}".format(sub_id): [to_ui_permission(perm) for perm in sub_perms], } resp = utils.TestSetup.check_FormSubmit(self, previous_response=resp, form_match=res_perm_form, form_submit=res_form_submit, form_data=data) # 3. validate result # 3.1 validate displayed UI permissions res_perm_form = resp.forms[res_form_name] check_ui_resource_permissions( res_perm_form, svc_id, [to_ui_permission(perm) for perm in svc_perms]) check_ui_resource_permissions( res_perm_form, res_id, [to_ui_permission(perm) for perm in res_perms]) check_ui_resource_permissions( res_perm_form, sub_id, [to_ui_permission(perm) for perm in sub_perms]) # 3.2 validate applied using API (to make sure that changes are not only reflected in UI) check_api_resource_permissions([(svc_id, svc_perms), (res_id, res_perms), (sub_id, sub_perms)]) # 4. remove permission: this is equivalent to re-apply the ones we want to keep without the one to remove # modify permission: this is detected by combo of (res-id, perm-name) with different (access, scope) svc_perms_mod = ["", ""] # remove the previous (READ, ALLOW, RECURSIVE) res_perm1_mod = PermissionSet(Permission.READ, Access.ALLOW, Scope.RECURSIVE) # DENY -> ALLOW res_perms_mod = [res_perm2, res_perm1_mod] # second WRITE permission is unchanged sub_perms_mod = sub_perms # all unchanged for this resource data = { "permission_resource_{}".format(svc_id): [to_ui_permission(perm) for perm in svc_perms_mod], "permission_resource_{}".format(res_id): [to_ui_permission(perm) for perm in res_perms_mod], "permission_resource_{}".format(sub_id): [to_ui_permission(perm) for perm in sub_perms_mod], } resp = utils.TestSetup.check_FormSubmit(self, previous_response=resp, form_match=res_perm_form, form_submit=res_form_submit, form_data=data) # 5. validate applied permissions modifications res_perm_form = resp.forms[res_form_name] check_ui_resource_permissions( res_perm_form, svc_id, [to_ui_permission(perm) for perm in svc_perms_mod]) check_ui_resource_permissions( res_perm_form, res_id, [to_ui_permission(perm) for perm in res_perms_mod]) check_ui_resource_permissions( res_perm_form, sub_id, [to_ui_permission(perm) for perm in sub_perms_mod]) check_api_resource_permissions([(svc_id, svc_perms_mod), (res_id, res_perms_mod), (sub_id, sub_perms_mod)])
def test_register_permissions_multiple_resource_type_possible(self): """ Verify that service that allows multiple resource-types children retrieves ``type`` field for their creation. """ svc_type = ServiceTHREDDS.service_type svc_name = "unittest-service-thredds-register-children-resources" utils.TestSetup.create_TestService(self, override_service_name=svc_name, override_service_type=svc_type) utils.TestSetup.create_TestGroup( self, override_group_name=self.test_perm_grp_name) session = get_db_session_from_settings(self.app.app.registry.settings) res1_name = "test-resource" res2_name = "sub-test-resource" res3_name = "sub-sub-test-resource" res3_perm = "write-deny-recursive" res3_path = res1_name + "/" + res2_name + "/" + res3_name # none exist, all created for final permission perm_config = { "permissions": [ { "service": svc_name, # exists "resource": res3_path, "permission": res3_perm, # perm only on lowest child "type": Directory. resource_type_name, # without this, fails because cannot infer Directory/File "action": "create", "group": self.test_perm_grp_name, }, ] } utils.check_no_raise( lambda: register.magpie_register_permissions_from_config( perm_config, db_session=session)) # check that all service, resources, group are created correctly services = utils.TestSetup.get_RegisteredServicesList(self) utils.check_val_is_in(svc_name, [s["service_name"] for s in services]) groups = utils.TestSetup.get_RegisteredGroupsList(self) utils.check_val_is_in(self.test_perm_grp_name, groups) resp = utils.test_request(self.app, "GET", "/services/{}/resources".format(svc_name)) body = utils.check_response_basic_info(resp) svc_res = body[svc_name]["resources"] # type: JSON utils.check_val_is_in(res1_name, [svc_res[r]["resource_name"] for r in svc_res]) res1_id = [ svc_res[r]["resource_id"] for r in svc_res if svc_res[r]["resource_name"] == res1_name ][0] res1_sub = svc_res[str(res1_id)]["children"] # type: JSON utils.check_val_is_in(res2_name, [res1_sub[r]["resource_name"] for r in res1_sub]) res2_id = [ res1_sub[r]["resource_id"] for r in res1_sub if res1_sub[r]["resource_name"] == res2_name ][0] res2_sub = res1_sub[str(res2_id)]["children"] # type: JSON utils.check_val_is_in(res3_name, [res2_sub[r]["resource_name"] for r in res2_sub]) res3_id = [ res2_sub[r]["resource_id"] for r in res2_sub if res2_sub[r]["resource_name"] == res3_name ][0] path = "/groups/{}/resources/{}/permissions".format( self.test_perm_grp_name, res3_id) resp = utils.test_request(self.app, "GET", path) body = utils.check_response_basic_info(resp) utils.check_val_is_in(res3_perm, body["permission_names"]) # validate that without the 'type', similar resource would not be created due to missing information res_missing_type_name = "missing-type" res_missing_type_path = res3_path.replace("/" + res3_name, "/" + res_missing_type_name) missing_type_perm_config = copy.deepcopy(perm_config) # type: JSON missing_type_perm_config["permissions"][0].pop("type") missing_type_perm_config["permissions"][0][ "resource"] = res_missing_type_path utils.check_no_raise( lambda: register.magpie_register_permissions_from_config( missing_type_perm_config, db_session=session)) resp = utils.test_request(self.app, "GET", "/resources/{}".format(res2_id)) body = utils.check_response_basic_info(resp) res_children = body["resource"]["children"] res_found = [ res for _, res in res_children.items() if res["resource_name"] == res_missing_type_name ] utils.check_val_equal(res_found, [])