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_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 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 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 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_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, [])