Skip to content

Commit

Permalink
fix(tokens) Adds non-admin tests for access tokens (datahub-project#5174
Browse files Browse the repository at this point in the history
)
  • Loading branch information
pedro93 authored and maggiehays committed Aug 1, 2022
1 parent e51c721 commit c56541b
Show file tree
Hide file tree
Showing 8 changed files with 454 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public static Authentication getAuthentication(DataFetchingEnvironment environme
return ((QueryContext) environment.getContext()).getAuthentication();
}

/**
* @apiNote DO NOT use this method if the facet filters do not include `.keyword` suffix to ensure
* that it is matched against a keyword filter in ElasticSearch.
*
* @param facetFilterInputs The list of facet filters inputs
* @param validFacetFields The set of valid fields against which to filter for.
* @return A map of filter definitions to be used in ElasticSearch.
*/
@Nonnull
public static Map<String, String> buildFacetFilters(@Nullable List<FacetFilterInput> facetFilterInputs,
@Nonnull Set<String> validFacetFields) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
package com.linkedin.datahub.graphql.resolvers.auth;

import com.google.common.collect.ImmutableSet;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.authorization.AuthorizationUtils;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.AccessTokenMetadata;
import com.linkedin.datahub.graphql.generated.EntityType;
import com.linkedin.datahub.graphql.generated.FacetFilterInput;
import com.linkedin.datahub.graphql.generated.ListAccessTokenInput;
import com.linkedin.datahub.graphql.generated.ListAccessTokenResult;
import com.linkedin.datahub.graphql.generated.AccessTokenMetadata;
import com.linkedin.entity.client.EntityClient;
import com.linkedin.metadata.Constants;
import com.linkedin.metadata.query.filter.SortCriterion;
import com.linkedin.metadata.query.filter.SortOrder;
import com.linkedin.metadata.search.SearchResult;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

import lombok.extern.slf4j.Slf4j;

import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*;
Expand All @@ -35,8 +31,6 @@
public class ListAccessTokensResolver implements DataFetcher<CompletableFuture<ListAccessTokenResult>> {

private static final String EXPIRES_AT_FIELD_NAME = "expiresAt";
private static final Set<String> FACET_FIELDS =
ImmutableSet.of("ownerUrn", "actorUrn", "name", "createdAt", "expiredAt", "description");

private final EntityClient _entityClient;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,14 @@ public void revokeAccessToken(@Nonnull String hashedToken) throws TokenException
throw new TokenException("Access token no longer exists");
}

public boolean isTokenRevoked(@Nonnull String hashToken) {
try {
return _revokedTokenCache.get(hashToken);
} catch (ExecutionException e) {
return false;
}
}

/**
* Hashes the input after salting it.
*/
Expand Down
3 changes: 2 additions & 1 deletion metadata-service/war/src/main/resources/boot/policies.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@
"actors":{
"resourceOwners":false,
"allUsers":true,
"allGroups":false
"allGroups":false,
"users":[]
},
"privileges":[
"MANAGE_POLICIES",
Expand Down
24 changes: 20 additions & 4 deletions smoke-test/tests/delete/delete_test.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,28 @@
import os
import json
import pytest
from time import sleep
from datahub.cli import delete_cli, ingest_cli
from datahub.cli.docker import check_local_docker_containers
from datahub.cli.cli_utils import guess_entity_type, post_entity, get_aspects_for_entity
from datahub.cli.ingest_cli import get_session_and_host
from datahub.cli.delete_cli import guess_entity_type, delete_one_urn_cmd, delete_references
from tests.utils import ingest_file_via_rest, delete_urns_from_file

# Disable telemetry
os.putenv("DATAHUB_TELEMETRY_ENABLED", "false")

@pytest.fixture(scope="session")
def wait_for_healthchecks():
# Simply assert that everything is healthy, but don't wait.
assert not check_local_docker_containers()
yield

@pytest.mark.dependency()
def test_healthchecks(wait_for_healthchecks):
# Call to wait_for_healthchecks fixture will do the actual functionality.
pass

@pytest.fixture(autouse=True)
def test_setup():
"""Fixture to execute asserts before and after a test is run"""
Expand All @@ -24,21 +40,21 @@ def test_setup():

ingested_dataset_run_id = ingest_file_via_rest("tests/delete/cli_test_data.json").config.run_id

sleep(2)
sleep(3)

assert "browsePaths" in get_aspects_for_entity(entity_urn=dataset_urn, aspects=["browsePaths"], typed=False)

yield
rollback_url = f"{gms_host}/runs?action=rollback"
session.post(rollback_url, data=json.dumps({"runId": ingested_dataset_run_id, "dryRun": False, "hardDelete": True, "safe": False}))

sleep(2)
sleep(3)

assert "browsePaths" not in get_aspects_for_entity(entity_urn=dataset_urn, aspects=["browsePaths"], typed=False)
assert "editableDatasetProperties" not in get_aspects_for_entity(entity_urn=dataset_urn, aspects=["editableDatasetProperties"], typed=False)

@pytest.mark.dependency()
def test_delete_reference():
def test_delete_reference(depends=["test_healthchecks"]):
platform = "urn:li:dataPlatform:kafka"
dataset_name = "test-delete"

Expand All @@ -58,7 +74,7 @@ def test_delete_reference():
# Delete references to the tag
delete_references(tag_urn, dry_run=False, cached_session_host=(session, gms_host))

sleep(2)
sleep(3)

# Validate that references no longer exist
references_count, related_aspects = delete_references(tag_urn, dry_run=True, cached_session_host=(session, gms_host))
Expand Down
Empty file.
223 changes: 223 additions & 0 deletions smoke-test/tests/policies/test_policies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
import time
import pytest
import requests
from tests.utils import get_frontend_url
from datahub.cli.docker import check_local_docker_containers

TEST_POLICY_NAME = "Updated Platform Policy"

@pytest.fixture(scope="session")
def wait_for_healthchecks():
# Simply assert that everything is healthy, but don't wait.
assert not check_local_docker_containers()
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 frontend_session(wait_for_healthchecks):
session = requests.Session()

headers = {
"Content-Type": "application/json",
}
data = '{"username":"datahub", "password":"datahub"}'
response = session.post(f"{get_frontend_url()}/logIn", headers=headers, data=data)
response.raise_for_status()

yield session

@pytest.mark.dependency(depends=["test_healthchecks"])
@pytest.fixture(scope='class', autouse=True)
def test_frontend_list_policies(frontend_session):
"""Fixture to execute setup before and tear down after all tests are run"""
res_data = listPolicies(frontend_session)

assert res_data
assert res_data["data"]
assert res_data["data"]["listPolicies"]
assert res_data["data"]["listPolicies"]["start"] == 0
assert res_data["data"]["listPolicies"]["count"] > 0
assert len(res_data["data"]["listPolicies"]["policies"]) > 0

# Verify that policy to be created does not exist before the test.
# If it does, this test class's state is tainted
result = filter(
lambda x: x["name"] == TEST_POLICY_NAME,
res_data["data"]["listPolicies"]["policies"],
)
assert len(list(result)) == 0

# Run remaining tests.
yield

res_data = listPolicies(frontend_session)

assert res_data
assert res_data["data"]
assert res_data["data"]["listPolicies"]

# Verify that policy that was created is no longer in the list
result = filter(
lambda x: x["name"] == TEST_POLICY_NAME,
res_data["data"]["listPolicies"]["policies"],
)
assert len(list(result)) == 0

@pytest.mark.dependency(depends=["test_healthchecks"])
def test_frontend_policy_operations(frontend_session):

json = {
"query": """mutation createPolicy($input: PolicyUpdateInput!) {\n
createPolicy(input: $input) }""",
"variables": {
"input": {
"type": "METADATA",
"name": "Test Metadata Policy",
"description": "My Metadaata Policy",
"state": "ACTIVE",
"resources": {"type": "dataset", "allResources": True},
"privileges": ["EDIT_ENTITY_TAGS"],
"actors": {
"users": ["urn:li:corpuser:datahub"],
"resourceOwners": False,
"allUsers": False,
"allGroups": False,
},
}
},
}

response = frontend_session.post(f"{get_frontend_url()}/api/v2/graphql", json=json)
response.raise_for_status()
res_data = response.json()

assert res_data
assert res_data["data"]
assert res_data["data"]["createPolicy"]

new_urn = res_data["data"]["createPolicy"]

# Sleep for eventual consistency
time.sleep(3)

update_json = {
"query": """mutation updatePolicy($urn: String!, $input: PolicyUpdateInput!) {\n
updatePolicy(urn: $urn, input: $input) }""",
"variables": {
"urn": new_urn,
"input": {
"type": "METADATA",
"state": "ACTIVE",
"name": "Test Metadata Policy",
"description": "Updated Metadaata Policy",
"privileges": ["EDIT_ENTITY_TAGS", "EDIT_ENTITY_GLOSSARY_TERMS"],
"actors": {
"resourceOwners": False,
"allUsers": True,
"allGroups": False,
},
},
},
}

response = frontend_session.post(f"{get_frontend_url()}/api/v2/graphql", json=update_json)
response.raise_for_status()
res_data = response.json()

# Check updated was submitted successfully
assert res_data
assert res_data["data"]
assert res_data["data"]["updatePolicy"]
assert res_data["data"]["updatePolicy"] == new_urn

# Sleep for eventual consistency
time.sleep(3)

res_data = listPolicies(frontend_session)

assert res_data
assert res_data["data"]
assert res_data["data"]["listPolicies"]

# Verify that the updated policy appears in the list and has the appropriate changes
result = list(filter(
lambda x: x["urn"] == new_urn, res_data["data"]["listPolicies"]["policies"]
))
print(result)

assert len(result) == 1
assert result[0]["description"] == "Updated Metadaata Policy"
assert result[0]["privileges"] == ["EDIT_ENTITY_TAGS", "EDIT_ENTITY_GLOSSARY_TERMS"]
assert result[0]["actors"]["allUsers"] == True

# Now test that the policy can be deleted
json = {
"query": """mutation deletePolicy($urn: String!) {\n
deletePolicy(urn: $urn) }""",
"variables": {"urn": new_urn},
}

response = frontend_session.post(f"{get_frontend_url()}/api/v2/graphql", json=json)
response.raise_for_status()
res_data = response.json()

res_data = listPolicies(frontend_session)

assert res_data
assert res_data["data"]
assert res_data["data"]["listPolicies"]

# Verify that the URN is no longer in the list
result = filter(
lambda x: x["urn"] == new_urn,
res_data["data"]["listPolicies"]["policies"],
)
assert len(list(result)) == 0

def listPolicies(session):
json = {
"query": """query listPolicies($input: ListPoliciesInput!) {\n
listPolicies(input: $input) {\n
start\n
count\n
total\n
policies {\n
urn\n
type\n
name\n
description\n
state\n
resources {\n
type\n
allResources\n
resources\n
}\n
privileges\n
actors {\n
users\n
groups\n
allUsers\n
allGroups\n
resourceOwners\n
}\n
editable\n
}\n
}\n
}""",
"variables": {
"input": {
"start": "0",
"count": "20",
}
},
}
response = session.post(f"{get_frontend_url()}/api/v2/graphql", json=json)
response.raise_for_status()

return response.json()
Loading

0 comments on commit c56541b

Please sign in to comment.