From 2ae9f7c6d2e60e635be149f9410ff6c50583cc95 Mon Sep 17 00:00:00 2001 From: Kos Korchak Date: Thu, 26 Oct 2023 20:26:52 -0400 Subject: [PATCH 1/5] API test for manage secrets privilege --- .../tests/privileges/test_manage_secrets.py | 181 ++++++++++++++++++ smoke-test/tests/privileges/utils.py | 171 +++++++++++++++++ 2 files changed, 352 insertions(+) create mode 100644 smoke-test/tests/privileges/test_manage_secrets.py create mode 100644 smoke-test/tests/privileges/utils.py diff --git a/smoke-test/tests/privileges/test_manage_secrets.py b/smoke-test/tests/privileges/test_manage_secrets.py new file mode 100644 index 00000000000000..fa67c4fb13745a --- /dev/null +++ b/smoke-test/tests/privileges/test_manage_secrets.py @@ -0,0 +1,181 @@ +import pytest +import tenacity + +from tests.utils import (get_frontend_session, wait_for_writes_to_sync, wait_for_healthcheck_util, + get_frontend_url, get_admin_credentials,get_sleep_info) +from tests.privileges.utils import (base_privileges_set_status, sensitive_info_prifileges_set_status, + view_entity_prifileges_set_status, create_user, remove_user,login_as) + +sleep_sec, sleep_times = get_sleep_info() + +@pytest.fixture(scope="session") +def wait_for_healthchecks(): + wait_for_healthcheck_util() + yield + + +@pytest.mark.dependency() +def test_healthchecks(wait_for_healthchecks): + # Call to wait_for_healthchecks fixture will do the actual functionality. + pass + + +@pytest.fixture(scope="session") +def admin_session(wait_for_healthchecks): + yield get_frontend_session() + + +@pytest.mark.dependency(depends=["test_healthchecks"]) +@pytest.fixture(scope="module", autouse=True) +def privileges_and_test_user_setup(admin_session): + """Fixture to execute setup before and tear down after all tests are run""" + # Disable 'All users' privileges + base_privileges_set_status("INACTIVE", admin_session) + sensitive_info_prifileges_set_status("INACTIVE", admin_session) + view_entity_prifileges_set_status("INACTIVE", admin_session) + # Sleep for eventual consistency + wait_for_writes_to_sync() + + # Create a new user + admin_session = create_user(admin_session, "user", "user") + + yield + + # Remove test user + remove_user(admin_session, "urn:li:corpuser:user") + + # Restore All users vrivileges + base_privileges_set_status("ACTIVE", admin_session) + sensitive_info_prifileges_set_status("ACTIVE", admin_session) + view_entity_prifileges_set_status("ACTIVE", admin_session) + + # Sleep for eventual consistency + wait_for_writes_to_sync() + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(10), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_can_create_secret(session, json): + create_secret_success = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_secret_success.raise_for_status() + secret_data = create_secret_success.json() + + assert secret_data + assert secret_data["data"] + assert secret_data["data"]["createSecret"] + assert secret_data["data"]["createSecret"] == "urn:li:dataHubSecret:TestSecretName" + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(sleep_times), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_cant_create_secret(session, json): + create_secret_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_secret_response.raise_for_status() + create_secret_data = create_secret_response.json() + + assert create_secret_data["errors"][0]["extensions"]["code"] == 403 + assert create_secret_data["errors"][0]["extensions"]["type"] == "UNAUTHORIZED" + assert create_secret_data["data"]["createSecret"] == None + + +@pytest.mark.dependency(depends=["test_healthchecks"]) +def test_add_and_verify_privileges_to_manage_secrets(): + + (admin_user, admin_pass) = get_admin_credentials() + admin_session = login_as(admin_user, admin_pass) + user_session = login_as("user", "user") + + # Verify new user can't create secrets + create_secret = { + "query": """mutation createSecret($input: CreateSecretInput!) {\n + createSecret(input: $input)\n}""", + "variables": { + "input":{ + "name":"TestSecretName", + "value":"Test Secret Value", + "description":"Test Secret Description" + } + }, + } + _ensure_cant_create_secret(user_session, create_secret) + + + # Assign privileges to the new user to manage secretes + manage_secrets = { + "query": """mutation createPolicy($input: PolicyUpdateInput!) {\n + createPolicy(input: $input) }""", + "variables": { + "input": { + "type": "PLATFORM", + "name": "Manage Secrets", + "description": "Manage Secrets Policy", + "state": "ACTIVE", + "resources": {"filter":{"criteria":[]}}, + "privileges": ["MANAGE_SECRETS"], + "actors": { + "users": ["urn:li:corpuser:user"], + "resourceOwners": False, + "allUsers": False, + "allGroups": False, + }, + } + }, + } + + response = admin_session.post(f"{get_frontend_url()}/api/v2/graphql", json=manage_secrets) + response.raise_for_status() + res_data = response.json() + + assert res_data + assert res_data["data"] + assert res_data["data"]["createPolicy"] + policy_urn = res_data["data"]["createPolicy"] + + + # Verify new user can create and manage secrets + # Create a secret + _ensure_can_create_secret(user_session, create_secret) + + + # Remove a secret + remove_secret = { + "query": """mutation deleteSecret($urn: String!) {\n + deleteSecret(urn: $urn)\n}""", + "variables": { + "urn": "urn:li:dataHubSecret:TestSecretName" + }, + } + + remove_secret_response = user_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_secret) + remove_secret_response.raise_for_status() + secret_data = remove_secret_response.json() + + assert secret_data + assert secret_data["data"] + assert secret_data["data"]["deleteSecret"] + assert secret_data["data"]["deleteSecret"] == "urn:li:dataHubSecret:TestSecretName" + + + # Remove the policy + remove_policy = { + "query": """mutation deletePolicy($urn: String!) {\n + deletePolicy(urn: $urn) }""", + "variables": {"urn": policy_urn}, + } + + response = admin_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_policy) + response.raise_for_status() + res_data = response.json() + + assert res_data + assert res_data["data"] + assert res_data["data"]["deletePolicy"] + assert res_data["data"]["deletePolicy"] == policy_urn + + + # Ensure user can't create secret after policy is removed + _ensure_cant_create_secret(user_session, create_secret) \ No newline at end of file diff --git a/smoke-test/tests/privileges/utils.py b/smoke-test/tests/privileges/utils.py new file mode 100644 index 00000000000000..da613dc8762df9 --- /dev/null +++ b/smoke-test/tests/privileges/utils.py @@ -0,0 +1,171 @@ +import requests_wrapper as requests +from tests.consistency_utils import wait_for_writes_to_sync +from tests.utils import (get_frontend_url, wait_for_writes_to_sync, get_admin_credentials) + + +def base_privileges_set_status(status, session): + base_platform_privileges = { + "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n + updatePolicy(urn: $urn, input: $input) }""", + "variables": { + "urn": "urn:li:dataHubPolicy:7", + "input": { + "type": "PLATFORM", + "state": status, + "name": "All Users - Base Platform Privileges", + "description": "Grants base platform privileges to ALL users of DataHub. Change this policy to alter that behavior.", + "privileges": ["MANAGE_INGESTION", + "MANAGE_SECRETS", + "MANAGE_USERS_AND_GROUPS", + "VIEW_ANALYTICS", + "GENERATE_PERSONAL_ACCESS_TOKENS", + "MANAGE_DOMAINS", + "MANAGE_GLOBAL_ANNOUNCEMENTS", + "MANAGE_TESTS", + "MANAGE_GLOSSARIES", + "MANAGE_TAGS", + "MANAGE_GLOBAL_VIEWS", + "MANAGE_GLOBAL_OWNERSHIP_TYPES"], + "actors": { + "users": [], + "groups": None, + "resourceOwners": False, + "allUsers": True, + "allGroups": False, + "resourceOwnersTypes": None, + }, + }, + }, + } + base_privileges_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=base_platform_privileges) + base_privileges_response.raise_for_status() + base_res_data = base_privileges_response.json() + assert base_res_data["data"]["updatePolicy"] == "urn:li:dataHubPolicy:7" + +def sensitive_info_prifileges_set_status(status, session): + dataset_sensitive_information = { + "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n + updatePolicy(urn: $urn, input: $input) }""", + "variables": { + "urn": "urn:li:dataHubPolicy:view-dataset-sensitive", + "input": { + "type": "METADATA", + "state": status, + "name": "All Users - View Dataset Sensitive Information", + "description": "Grants viewing privileges of usage and profile information of all datasets for all users", + "privileges": ["VIEW_DATASET_USAGE","VIEW_DATASET_PROFILE"], + "actors": { + "users": [], + "groups": None, + "resourceOwners": False, + "allUsers": True, + "allGroups": False, + "resourceOwnersTypes": None, + }, + }, + }, + } + sensitive_info_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=dataset_sensitive_information) + sensitive_info_response.raise_for_status() + sens_info_data = sensitive_info_response.json() + assert sens_info_data["data"]["updatePolicy"] == "urn:li:dataHubPolicy:view-dataset-sensitive" + +def view_entity_prifileges_set_status(status, session): + view_entity_page = { + "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n + updatePolicy(urn: $urn, input: $input) }""", + "variables": { + "urn": "urn:li:dataHubPolicy:view-entity-page-all", + "input": { + "type": "METADATA", + "state": status, + "name": "All Users - View Entity Page", + "description": "Grants entity view to all users", + "privileges": ["VIEW_ENTITY_PAGE", + "SEARCH_PRIVILEGE", + "GET_COUNTS_PRIVILEGE", + "GET_TIMESERIES_ASPECT_PRIVILEGE", + "GET_ENTITY_PRIVILEGE", + "GET_TIMELINE_PRIVILEGE"], + "actors": { + "users": [], + "groups": None, + "resourceOwners": False, + "allUsers": True, + "allGroups": False, + "resourceOwnersTypes": None, + }, + }, + }, + } + view_entity_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=view_entity_page) + view_entity_response.raise_for_status() + view_entity_data = view_entity_response.json() + assert view_entity_data["data"]["updatePolicy"] == "urn:li:dataHubPolicy:view-entity-page-all" + +def create_user(session, email, password): + # Remove user if exists + res_data = remove_user(session, f"urn:li:corpuser:{email}") + assert res_data + assert "error" not in res_data + # Get the invite token + get_invite_token_json = { + "query": """query getInviteToken($input: GetInviteTokenInput!) {\n + getInviteToken(input: $input){\n + inviteToken\n + }\n + }""", + "variables": {"input": {}}, + } + get_invite_token_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=get_invite_token_json + ) + get_invite_token_response.raise_for_status() + get_invite_token_res_data = get_invite_token_response.json() + invite_token = get_invite_token_res_data["data"]["getInviteToken"]["inviteToken"] + assert invite_token is not None + assert "error" not in invite_token + # Create a new user using the invite token + sign_up_json = { + "fullName": "Test User", + "email": email, + "password": password, + "title": "Data Engineer", + "inviteToken": invite_token, + } + sign_up_response = session.post( + f"{get_frontend_url()}/signUp", json=sign_up_json + ) + sign_up_response.raise_for_status() + assert sign_up_response + assert "error" not in sign_up_response + wait_for_writes_to_sync() + session.cookies.clear() + (admin_user, admin_pass) = get_admin_credentials() + admin_session = login_as(admin_user, admin_pass) + return admin_session + + +def login_as(username, password): + session = requests.Session() + headers = { + "Content-Type": "application/json", + } + data = '{"username":"' + username + '", "password":"' + password + '"}' + response = session.post(f"{get_frontend_url()}/logIn", headers=headers, data=data) + response.raise_for_status() + return session + +def remove_user(session, urn): + json = { + "query": """mutation removeUser($urn: String!) {\n + removeUser(urn: $urn) + }""", + "variables": {"urn": urn}, + } + response = session.post(f"{get_frontend_url()}/api/v2/graphql", json=json) + response.raise_for_status() + return response.json() \ No newline at end of file From a2b1e7e1f4c14f4adc47a470f2c5137902993226 Mon Sep 17 00:00:00 2001 From: Kos Korchak Date: Thu, 26 Oct 2023 20:33:11 -0400 Subject: [PATCH 2/5] typo fixes --- smoke-test/tests/privileges/test_manage_secrets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/smoke-test/tests/privileges/test_manage_secrets.py b/smoke-test/tests/privileges/test_manage_secrets.py index fa67c4fb13745a..66bd91467de918 100644 --- a/smoke-test/tests/privileges/test_manage_secrets.py +++ b/smoke-test/tests/privileges/test_manage_secrets.py @@ -44,7 +44,7 @@ def privileges_and_test_user_setup(admin_session): # Remove test user remove_user(admin_session, "urn:li:corpuser:user") - # Restore All users vrivileges + # Restore All users privileges base_privileges_set_status("ACTIVE", admin_session) sensitive_info_prifileges_set_status("ACTIVE", admin_session) view_entity_prifileges_set_status("ACTIVE", admin_session) @@ -104,7 +104,7 @@ def test_add_and_verify_privileges_to_manage_secrets(): _ensure_cant_create_secret(user_session, create_secret) - # Assign privileges to the new user to manage secretes + # Assign privileges to the new user to manage secrets manage_secrets = { "query": """mutation createPolicy($input: PolicyUpdateInput!) {\n createPolicy(input: $input) }""", From e7ae9cde1dc3190d203de1138ea31c6fb39ee7be Mon Sep 17 00:00:00 2001 From: Kos Korchak Date: Mon, 30 Oct 2023 16:49:08 -0400 Subject: [PATCH 3/5] Addressed review comments --- .../tests/privileges/test_manage_secrets.py | 27 ++++++++++--------- smoke-test/tests/privileges/utils.py | 6 ++--- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/smoke-test/tests/privileges/test_manage_secrets.py b/smoke-test/tests/privileges/test_manage_secrets.py index 66bd91467de918..ded253cc0bb33e 100644 --- a/smoke-test/tests/privileges/test_manage_secrets.py +++ b/smoke-test/tests/privileges/test_manage_secrets.py @@ -3,8 +3,8 @@ from tests.utils import (get_frontend_session, wait_for_writes_to_sync, wait_for_healthcheck_util, get_frontend_url, get_admin_credentials,get_sleep_info) -from tests.privileges.utils import (base_privileges_set_status, sensitive_info_prifileges_set_status, - view_entity_prifileges_set_status, create_user, remove_user,login_as) +from tests.privileges.utils import (set_base_platform_privileges_policy_status, set_view_dataset_sensitive_info_policy_status, + set_view_entity_profile_privileges_policy_status, create_user, remove_user,login_as) sleep_sec, sleep_times = get_sleep_info() @@ -30,9 +30,9 @@ def admin_session(wait_for_healthchecks): def privileges_and_test_user_setup(admin_session): """Fixture to execute setup before and tear down after all tests are run""" # Disable 'All users' privileges - base_privileges_set_status("INACTIVE", admin_session) - sensitive_info_prifileges_set_status("INACTIVE", admin_session) - view_entity_prifileges_set_status("INACTIVE", admin_session) + set_base_platform_privileges_policy_status("INACTIVE", admin_session) + set_view_dataset_sensitive_info_policy_status("INACTIVE", admin_session) + set_view_entity_profile_privileges_policy_status("INACTIVE", admin_session) # Sleep for eventual consistency wait_for_writes_to_sync() @@ -45,9 +45,9 @@ def privileges_and_test_user_setup(admin_session): remove_user(admin_session, "urn:li:corpuser:user") # Restore All users privileges - base_privileges_set_status("ACTIVE", admin_session) - sensitive_info_prifileges_set_status("ACTIVE", admin_session) - view_entity_prifileges_set_status("ACTIVE", admin_session) + set_base_platform_privileges_policy_status("ACTIVE", admin_session) + set_view_dataset_sensitive_info_policy_status("ACTIVE", admin_session) + set_view_entity_profile_privileges_policy_status("ACTIVE", admin_session) # Sleep for eventual consistency wait_for_writes_to_sync() @@ -56,7 +56,7 @@ def privileges_and_test_user_setup(admin_session): @tenacity.retry( stop=tenacity.stop_after_attempt(10), wait=tenacity.wait_fixed(sleep_sec) ) -def _ensure_can_create_secret(session, json): +def _ensure_can_create_secret(session, json, url): create_secret_success = session.post( f"{get_frontend_url()}/api/v2/graphql", json=json) create_secret_success.raise_for_status() @@ -65,7 +65,7 @@ def _ensure_can_create_secret(session, json): assert secret_data assert secret_data["data"] assert secret_data["data"]["createSecret"] - assert secret_data["data"]["createSecret"] == "urn:li:dataHubSecret:TestSecretName" + assert secret_data["data"]["createSecret"] == url @tenacity.retry( @@ -88,6 +88,7 @@ def test_add_and_verify_privileges_to_manage_secrets(): (admin_user, admin_pass) = get_admin_credentials() admin_session = login_as(admin_user, admin_pass) user_session = login_as("user", "user") + secret_urn = "urn:li:dataHubSecret:TestSecretName" # Verify new user can't create secrets create_secret = { @@ -138,7 +139,7 @@ def test_add_and_verify_privileges_to_manage_secrets(): # Verify new user can create and manage secrets # Create a secret - _ensure_can_create_secret(user_session, create_secret) + _ensure_can_create_secret(user_session, create_secret, secret_urn) # Remove a secret @@ -146,7 +147,7 @@ def test_add_and_verify_privileges_to_manage_secrets(): "query": """mutation deleteSecret($urn: String!) {\n deleteSecret(urn: $urn)\n}""", "variables": { - "urn": "urn:li:dataHubSecret:TestSecretName" + "urn": secret_urn }, } @@ -157,7 +158,7 @@ def test_add_and_verify_privileges_to_manage_secrets(): assert secret_data assert secret_data["data"] assert secret_data["data"]["deleteSecret"] - assert secret_data["data"]["deleteSecret"] == "urn:li:dataHubSecret:TestSecretName" + assert secret_data["data"]["deleteSecret"] == secret_urn # Remove the policy diff --git a/smoke-test/tests/privileges/utils.py b/smoke-test/tests/privileges/utils.py index da613dc8762df9..69d409cf46f4cd 100644 --- a/smoke-test/tests/privileges/utils.py +++ b/smoke-test/tests/privileges/utils.py @@ -3,7 +3,7 @@ from tests.utils import (get_frontend_url, wait_for_writes_to_sync, get_admin_credentials) -def base_privileges_set_status(status, session): +def set_base_platform_privileges_policy_status(status, session): base_platform_privileges = { "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n updatePolicy(urn: $urn, input: $input) }""", @@ -43,7 +43,7 @@ def base_privileges_set_status(status, session): base_res_data = base_privileges_response.json() assert base_res_data["data"]["updatePolicy"] == "urn:li:dataHubPolicy:7" -def sensitive_info_prifileges_set_status(status, session): +def set_view_dataset_sensitive_info_policy_status(status, session): dataset_sensitive_information = { "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n updatePolicy(urn: $urn, input: $input) }""", @@ -72,7 +72,7 @@ def sensitive_info_prifileges_set_status(status, session): sens_info_data = sensitive_info_response.json() assert sens_info_data["data"]["updatePolicy"] == "urn:li:dataHubPolicy:view-dataset-sensitive" -def view_entity_prifileges_set_status(status, session): +def set_view_entity_profile_privileges_policy_status(status, session): view_entity_page = { "query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n updatePolicy(urn: $urn, input: $input) }""", From fbb5b967fab05a72d8e3216363f6831f84b4e697 Mon Sep 17 00:00:00 2001 From: Kos Korchak Date: Tue, 31 Oct 2023 16:21:21 -0400 Subject: [PATCH 4/5] Refactoring and added test for ingestion source --- .../tests/privileges/test_manage_secrets.py | 182 ------------- .../tests/privileges/test_privileges.py | 242 ++++++++++++++++++ smoke-test/tests/privileges/utils.py | 49 +++- 3 files changed, 290 insertions(+), 183 deletions(-) delete mode 100644 smoke-test/tests/privileges/test_manage_secrets.py create mode 100644 smoke-test/tests/privileges/test_privileges.py diff --git a/smoke-test/tests/privileges/test_manage_secrets.py b/smoke-test/tests/privileges/test_manage_secrets.py deleted file mode 100644 index ded253cc0bb33e..00000000000000 --- a/smoke-test/tests/privileges/test_manage_secrets.py +++ /dev/null @@ -1,182 +0,0 @@ -import pytest -import tenacity - -from tests.utils import (get_frontend_session, wait_for_writes_to_sync, wait_for_healthcheck_util, - get_frontend_url, get_admin_credentials,get_sleep_info) -from tests.privileges.utils import (set_base_platform_privileges_policy_status, set_view_dataset_sensitive_info_policy_status, - set_view_entity_profile_privileges_policy_status, create_user, remove_user,login_as) - -sleep_sec, sleep_times = get_sleep_info() - -@pytest.fixture(scope="session") -def wait_for_healthchecks(): - wait_for_healthcheck_util() - yield - - -@pytest.mark.dependency() -def test_healthchecks(wait_for_healthchecks): - # Call to wait_for_healthchecks fixture will do the actual functionality. - pass - - -@pytest.fixture(scope="session") -def admin_session(wait_for_healthchecks): - yield get_frontend_session() - - -@pytest.mark.dependency(depends=["test_healthchecks"]) -@pytest.fixture(scope="module", autouse=True) -def privileges_and_test_user_setup(admin_session): - """Fixture to execute setup before and tear down after all tests are run""" - # Disable 'All users' privileges - set_base_platform_privileges_policy_status("INACTIVE", admin_session) - set_view_dataset_sensitive_info_policy_status("INACTIVE", admin_session) - set_view_entity_profile_privileges_policy_status("INACTIVE", admin_session) - # Sleep for eventual consistency - wait_for_writes_to_sync() - - # Create a new user - admin_session = create_user(admin_session, "user", "user") - - yield - - # Remove test user - remove_user(admin_session, "urn:li:corpuser:user") - - # Restore All users privileges - set_base_platform_privileges_policy_status("ACTIVE", admin_session) - set_view_dataset_sensitive_info_policy_status("ACTIVE", admin_session) - set_view_entity_profile_privileges_policy_status("ACTIVE", admin_session) - - # Sleep for eventual consistency - wait_for_writes_to_sync() - - -@tenacity.retry( - stop=tenacity.stop_after_attempt(10), wait=tenacity.wait_fixed(sleep_sec) -) -def _ensure_can_create_secret(session, json, url): - create_secret_success = session.post( - f"{get_frontend_url()}/api/v2/graphql", json=json) - create_secret_success.raise_for_status() - secret_data = create_secret_success.json() - - assert secret_data - assert secret_data["data"] - assert secret_data["data"]["createSecret"] - assert secret_data["data"]["createSecret"] == url - - -@tenacity.retry( - stop=tenacity.stop_after_attempt(sleep_times), wait=tenacity.wait_fixed(sleep_sec) -) -def _ensure_cant_create_secret(session, json): - create_secret_response = session.post( - f"{get_frontend_url()}/api/v2/graphql", json=json) - create_secret_response.raise_for_status() - create_secret_data = create_secret_response.json() - - assert create_secret_data["errors"][0]["extensions"]["code"] == 403 - assert create_secret_data["errors"][0]["extensions"]["type"] == "UNAUTHORIZED" - assert create_secret_data["data"]["createSecret"] == None - - -@pytest.mark.dependency(depends=["test_healthchecks"]) -def test_add_and_verify_privileges_to_manage_secrets(): - - (admin_user, admin_pass) = get_admin_credentials() - admin_session = login_as(admin_user, admin_pass) - user_session = login_as("user", "user") - secret_urn = "urn:li:dataHubSecret:TestSecretName" - - # Verify new user can't create secrets - create_secret = { - "query": """mutation createSecret($input: CreateSecretInput!) {\n - createSecret(input: $input)\n}""", - "variables": { - "input":{ - "name":"TestSecretName", - "value":"Test Secret Value", - "description":"Test Secret Description" - } - }, - } - _ensure_cant_create_secret(user_session, create_secret) - - - # Assign privileges to the new user to manage secrets - manage_secrets = { - "query": """mutation createPolicy($input: PolicyUpdateInput!) {\n - createPolicy(input: $input) }""", - "variables": { - "input": { - "type": "PLATFORM", - "name": "Manage Secrets", - "description": "Manage Secrets Policy", - "state": "ACTIVE", - "resources": {"filter":{"criteria":[]}}, - "privileges": ["MANAGE_SECRETS"], - "actors": { - "users": ["urn:li:corpuser:user"], - "resourceOwners": False, - "allUsers": False, - "allGroups": False, - }, - } - }, - } - - response = admin_session.post(f"{get_frontend_url()}/api/v2/graphql", json=manage_secrets) - response.raise_for_status() - res_data = response.json() - - assert res_data - assert res_data["data"] - assert res_data["data"]["createPolicy"] - policy_urn = res_data["data"]["createPolicy"] - - - # Verify new user can create and manage secrets - # Create a secret - _ensure_can_create_secret(user_session, create_secret, secret_urn) - - - # Remove a secret - remove_secret = { - "query": """mutation deleteSecret($urn: String!) {\n - deleteSecret(urn: $urn)\n}""", - "variables": { - "urn": secret_urn - }, - } - - remove_secret_response = user_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_secret) - remove_secret_response.raise_for_status() - secret_data = remove_secret_response.json() - - assert secret_data - assert secret_data["data"] - assert secret_data["data"]["deleteSecret"] - assert secret_data["data"]["deleteSecret"] == secret_urn - - - # Remove the policy - remove_policy = { - "query": """mutation deletePolicy($urn: String!) {\n - deletePolicy(urn: $urn) }""", - "variables": {"urn": policy_urn}, - } - - response = admin_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_policy) - response.raise_for_status() - res_data = response.json() - - assert res_data - assert res_data["data"] - assert res_data["data"]["deletePolicy"] - assert res_data["data"]["deletePolicy"] == policy_urn - - - # Ensure user can't create secret after policy is removed - _ensure_cant_create_secret(user_session, create_secret) \ No newline at end of file diff --git a/smoke-test/tests/privileges/test_privileges.py b/smoke-test/tests/privileges/test_privileges.py new file mode 100644 index 00000000000000..6bf557568ea444 --- /dev/null +++ b/smoke-test/tests/privileges/test_privileges.py @@ -0,0 +1,242 @@ +import pytest +import tenacity + +from tests.utils import (get_frontend_session, wait_for_writes_to_sync, wait_for_healthcheck_util, + get_frontend_url, get_admin_credentials,get_sleep_info) +from tests.privileges.utils import (set_base_platform_privileges_policy_status, set_view_dataset_sensitive_info_policy_status, create_user_policy, + set_view_entity_profile_privileges_policy_status, create_user, remove_user,login_as,remove_policy) + +sleep_sec, sleep_times = get_sleep_info() + +@pytest.fixture(scope="session") +def wait_for_healthchecks(): + wait_for_healthcheck_util() + yield + + +@pytest.mark.dependency() +def test_healthchecks(wait_for_healthchecks): + # Call to wait_for_healthchecks fixture will do the actual functionality. + pass + + +@pytest.fixture(scope="session") +def admin_session(wait_for_healthchecks): + yield get_frontend_session() + + +@pytest.mark.dependency(depends=["test_healthchecks"]) +@pytest.fixture(scope="module", autouse=True) +def privileges_and_test_user_setup(admin_session): + """Fixture to execute setup before and tear down after all tests are run""" + # Disable 'All users' privileges + set_base_platform_privileges_policy_status("INACTIVE", admin_session) + set_view_dataset_sensitive_info_policy_status("INACTIVE", admin_session) + set_view_entity_profile_privileges_policy_status("INACTIVE", admin_session) + # Sleep for eventual consistency + wait_for_writes_to_sync() + + # Create a new user + admin_session = create_user(admin_session, "user", "user") + + yield + + # Remove test user + remove_user(admin_session, "urn:li:corpuser:user") + + # Restore All users privileges + set_base_platform_privileges_policy_status("ACTIVE", admin_session) + set_view_dataset_sensitive_info_policy_status("ACTIVE", admin_session) + set_view_entity_profile_privileges_policy_status("ACTIVE", admin_session) + + # Sleep for eventual consistency + wait_for_writes_to_sync() + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(10), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_can_create_secret(session, json, urn): + create_secret_success = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_secret_success.raise_for_status() + secret_data = create_secret_success.json() + + assert secret_data + assert secret_data["data"] + assert secret_data["data"]["createSecret"] + assert secret_data["data"]["createSecret"] == urn + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(sleep_times), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_cant_create_secret(session, json): + create_secret_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_secret_response.raise_for_status() + create_secret_data = create_secret_response.json() + + assert create_secret_data["errors"][0]["extensions"]["code"] == 403 + assert create_secret_data["errors"][0]["extensions"]["type"] == "UNAUTHORIZED" + assert create_secret_data["data"]["createSecret"] == None + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(10), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_can_create_ingestion_source(session, json): + create_ingestion_success = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_ingestion_success.raise_for_status() + ingestion_data = create_ingestion_success.json() + + assert ingestion_data + assert ingestion_data["data"] + assert ingestion_data["data"]["createIngestionSource"] + assert ingestion_data["data"]["createIngestionSource"] is not None + + return ingestion_data["data"]["createIngestionSource"] + + +@tenacity.retry( + stop=tenacity.stop_after_attempt(sleep_times), wait=tenacity.wait_fixed(sleep_sec) +) +def _ensure_cant_create_ingestion_source(session, json): + create_source_response = session.post( + f"{get_frontend_url()}/api/v2/graphql", json=json) + create_source_response.raise_for_status() + create_source_data = create_source_response.json() + + assert create_source_data["errors"][0]["extensions"]["code"] == 403 + assert create_source_data["errors"][0]["extensions"]["type"] == "UNAUTHORIZED" + assert create_source_data["data"]["createIngestionSource"] == None + + +@pytest.mark.dependency(depends=["test_healthchecks"]) +def test_privilege_to_create_and_manage_secrets(): + + (admin_user, admin_pass) = get_admin_credentials() + admin_session = login_as(admin_user, admin_pass) + user_session = login_as("user", "user") + secret_urn = "urn:li:dataHubSecret:TestSecretName" + + # Verify new user can't create secrets + create_secret = { + "query": """mutation createSecret($input: CreateSecretInput!) {\n + createSecret(input: $input)\n}""", + "variables": { + "input":{ + "name":"TestSecretName", + "value":"Test Secret Value", + "description":"Test Secret Description" + } + }, + } + _ensure_cant_create_secret(user_session, create_secret) + + + # Assign privileges to the new user to manage secrets + policy_urn = create_user_policy("urn:li:corpuser:user", ["MANAGE_SECRETS"], admin_session) + + # Verify new user can create and manage secrets + # Create a secret + _ensure_can_create_secret(user_session, create_secret, secret_urn) + + + # Remove a secret + remove_secret = { + "query": """mutation deleteSecret($urn: String!) {\n + deleteSecret(urn: $urn)\n}""", + "variables": { + "urn": secret_urn + }, + } + + remove_secret_response = user_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_secret) + remove_secret_response.raise_for_status() + secret_data = remove_secret_response.json() + + assert secret_data + assert secret_data["data"] + assert secret_data["data"]["deleteSecret"] + assert secret_data["data"]["deleteSecret"] == secret_urn + + + # Remove the policy + remove_policy(policy_urn, admin_session) + + # Ensure user can't create secret after policy is removed + _ensure_cant_create_secret(user_session, create_secret) + + +@pytest.mark.dependency(depends=["test_healthchecks"]) +def test_privilege_to_create_and_manage_ingestion_source(): + + (admin_user, admin_pass) = get_admin_credentials() + admin_session = login_as(admin_user, admin_pass) + user_session = login_as("user", "user") + + # Verify new user can't create ingestion source + create_ingestion_source = { + "query": """mutation createIngestionSource($input: UpdateIngestionSourceInput!) {\n + createIngestionSource(input: $input)\n}""", + "variables": {"input":{"type":"snowflake","name":"test","config": + {"recipe": + "{\"source\":{\"type\":\"snowflake\",\"config\":{\"account_id\":null,\"include_table_lineage\":true,\"include_view_lineage\":true,\"include_tables\":true,\"include_views\":true,\"profiling\":{\"enabled\":true,\"profile_table_level_only\":true},\"stateful_ingestion\":{\"enabled\":true}}}}", + "executorId":"default","debugMode":False,"extraArgs":[]}}}, + } + + _ensure_cant_create_ingestion_source(user_session, create_ingestion_source) + + + # Assign privileges to the new user to manage ingestion source + policy_urn = create_user_policy("urn:li:corpuser:user", ["MANAGE_INGESTION"], admin_session) + + # Verify new user can create and manage ingestion source(edit, delete) + ingestion_source_urn = _ensure_can_create_ingestion_source(user_session, create_ingestion_source) + + # Edit ingestion source + update_ingestion_source = { + "query": """mutation updateIngestionSource($urn: String!, $input: UpdateIngestionSourceInput!) {\n + updateIngestionSource(urn: $urn, input: $input)\n}""", + "variables": {"urn":ingestion_source_urn, + "input":{"type":"snowflake","name":"test updated", + "config":{"recipe":"{\"source\":{\"type\":\"snowflake\",\"config\":{\"account_id\":null,\"include_table_lineage\":true,\"include_view_lineage\":true,\"include_tables\":true,\"include_views\":true,\"profiling\":{\"enabled\":true,\"profile_table_level_only\":true},\"stateful_ingestion\":{\"enabled\":true}}}}", + "executorId":"default","debugMode":False,"extraArgs":[]}}} + } + + update_ingestion_success = user_session.post( + f"{get_frontend_url()}/api/v2/graphql", json=update_ingestion_source) + update_ingestion_success.raise_for_status() + ingestion_data = update_ingestion_success.json() + + assert ingestion_data + assert ingestion_data["data"] + assert ingestion_data["data"]["updateIngestionSource"] + assert ingestion_data["data"]["updateIngestionSource"] == ingestion_source_urn + + + # Delete ingestion source + remove_ingestion_source = { + "query": """mutation deleteIngestionSource($urn: String!) {\n + deleteIngestionSource(urn: $urn)\n}""", + "variables": { + "urn": ingestion_source_urn + }, + } + + remove_ingestion_response = user_session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_ingestion_source) + remove_ingestion_response.raise_for_status() + ingestion_data = remove_ingestion_response.json() + + assert ingestion_data + assert ingestion_data["data"] + assert ingestion_data["data"]["deleteIngestionSource"] + assert ingestion_data["data"]["deleteIngestionSource"] == ingestion_source_urn + + # Remove the policy + remove_policy(policy_urn, admin_session) + + # Ensure that user can't create ingestion source after policy is removed + _ensure_cant_create_ingestion_source(user_session, create_ingestion_source) \ No newline at end of file diff --git a/smoke-test/tests/privileges/utils.py b/smoke-test/tests/privileges/utils.py index 69d409cf46f4cd..ea1f565f6f5acd 100644 --- a/smoke-test/tests/privileges/utils.py +++ b/smoke-test/tests/privileges/utils.py @@ -168,4 +168,51 @@ def remove_user(session, urn): } response = session.post(f"{get_frontend_url()}/api/v2/graphql", json=json) response.raise_for_status() - return response.json() \ No newline at end of file + return response.json() + +def create_user_policy(user_urn, privileges, session): + policy = { + "query": """mutation createPolicy($input: PolicyUpdateInput!) {\n + createPolicy(input: $input) }""", + "variables": { + "input": { + "type": "PLATFORM", + "name": "Policy Name", + "description": "Policy Description", + "state": "ACTIVE", + "resources": {"filter":{"criteria":[]}}, + "privileges": privileges, + "actors": { + "users": [user_urn], + "resourceOwners": False, + "allUsers": False, + "allGroups": False, + }, + } + }, + } + + response = session.post(f"{get_frontend_url()}/api/v2/graphql", json=policy) + response.raise_for_status() + res_data = response.json() + + assert res_data + assert res_data["data"] + assert res_data["data"]["createPolicy"] + return res_data["data"]["createPolicy"] + +def remove_policy(urn, session): + remove_policy_json = { + "query": """mutation deletePolicy($urn: String!) {\n + deletePolicy(urn: $urn) }""", + "variables": {"urn": urn}, + } + + response = session.post(f"{get_frontend_url()}/api/v2/graphql", json=remove_policy_json) + response.raise_for_status() + res_data = response.json() + + assert res_data + assert res_data["data"] + assert res_data["data"]["deletePolicy"] + assert res_data["data"]["deletePolicy"] == urn \ No newline at end of file From 140ba2ac016419a58e6678ab59f03e9451c62ae5 Mon Sep 17 00:00:00 2001 From: Kos Korchak Date: Tue, 31 Oct 2023 16:23:31 -0400 Subject: [PATCH 5/5] minor fix --- smoke-test/tests/privileges/test_privileges.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/smoke-test/tests/privileges/test_privileges.py b/smoke-test/tests/privileges/test_privileges.py index 6bf557568ea444..13d6b6cf3415a4 100644 --- a/smoke-test/tests/privileges/test_privileges.py +++ b/smoke-test/tests/privileges/test_privileges.py @@ -3,8 +3,7 @@ from tests.utils import (get_frontend_session, wait_for_writes_to_sync, wait_for_healthcheck_util, get_frontend_url, get_admin_credentials,get_sleep_info) -from tests.privileges.utils import (set_base_platform_privileges_policy_status, set_view_dataset_sensitive_info_policy_status, create_user_policy, - set_view_entity_profile_privileges_policy_status, create_user, remove_user,login_as,remove_policy) +from tests.privileges.utils import * sleep_sec, sleep_times = get_sleep_info()