From 8143c6855a03efb77a881abe95e5d43287979985 Mon Sep 17 00:00:00 2001 From: Martin Hesko Date: Wed, 25 Sep 2024 14:45:06 +0200 Subject: [PATCH] Add test for SubjectAccessReview authorization. Signed-off-by: Martin Hesko --- .../authorino/identity/conftest.py | 15 ++++++ .../subject_access_review/__init__.py | 0 .../subject_access_review/conftest.py | 49 +++++++++++++++++++ .../test_subject_access_review.py | 47 ++++++++++++++++++ .../identity/token_review/conftest.py | 14 ------ 5 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 testsuite/tests/singlecluster/authorino/identity/subject_access_review/__init__.py create mode 100644 testsuite/tests/singlecluster/authorino/identity/subject_access_review/conftest.py create mode 100644 testsuite/tests/singlecluster/authorino/identity/subject_access_review/test_subject_access_review.py diff --git a/testsuite/tests/singlecluster/authorino/identity/conftest.py b/testsuite/tests/singlecluster/authorino/identity/conftest.py index 70dedc2c..8a102052 100644 --- a/testsuite/tests/singlecluster/authorino/identity/conftest.py +++ b/testsuite/tests/singlecluster/authorino/identity/conftest.py @@ -2,6 +2,21 @@ import pytest +from testsuite.kubernetes.service_account import ServiceAccount + + +@pytest.fixture(scope="module") +def create_service_account(request, cluster, blame, module_label): + """Creates and returns service account""" + + def _create_service_account(name): + service_account = ServiceAccount.create_instance(cluster, blame(name), labels={"app": module_label}) + request.addfinalizer(service_account.delete) + service_account.commit() + return service_account + + return _create_service_account + @pytest.fixture(scope="module") def authorization(authorization): diff --git a/testsuite/tests/singlecluster/authorino/identity/subject_access_review/__init__.py b/testsuite/tests/singlecluster/authorino/identity/subject_access_review/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/testsuite/tests/singlecluster/authorino/identity/subject_access_review/conftest.py b/testsuite/tests/singlecluster/authorino/identity/subject_access_review/conftest.py new file mode 100644 index 00000000..3b4f166e --- /dev/null +++ b/testsuite/tests/singlecluster/authorino/identity/subject_access_review/conftest.py @@ -0,0 +1,49 @@ +"""Conftest for SubjectAccessReview related tests.""" + +import pytest + +from testsuite.httpx.auth import HeaderApiKeyAuth +from testsuite.kubernetes.cluster_role import ClusterRole, ClusterRoleBinding + + +@pytest.fixture(scope="module") +def create_cluster_role(request, cluster, blame, module_label): + """Creates and returns a ClusterRole""" + + def _create_cluster_role(rules): + cluster_role = ClusterRole.create_instance(cluster, blame("cr"), rules, labels={"app": module_label}) + request.addfinalizer(cluster_role.delete) + cluster_role.commit() + return cluster_role + + return _create_cluster_role + + +@pytest.fixture(scope="module") +def create_cluster_role_binding(request, cluster, blame, module_label): + """Creates and returns a ClusterRoleBinding""" + + def _create_cluster_role_binding(cluster_role, service_accounts): + cluster_role_binding = ClusterRoleBinding.create_instance( + cluster, blame("crb"), cluster_role, service_accounts, labels={"app": module_label} + ) + request.addfinalizer(cluster_role_binding.delete) + cluster_role_binding.commit() + return cluster_role_binding + + return _create_cluster_role_binding + + +@pytest.fixture(scope="module") +def bound_service_account(create_cluster_role, create_service_account, create_cluster_role_binding, audience): + """Create a ServiceAccount and bind it to a ClusterRole with given permissions""" + cluster_role = create_cluster_role([{"nonResourceURLs": ["/get"], "verbs": ["get"]}]) + service_account = create_service_account("tkn-auth") + create_cluster_role_binding(cluster_role.model.metadata.name, [service_account.model.metadata.name]) + return service_account.get_auth_token(audience) + + +@pytest.fixture(scope="module") +def auth(bound_service_account): + """Create request auth with service account token as API key""" + return HeaderApiKeyAuth(bound_service_account, "Bearer") diff --git a/testsuite/tests/singlecluster/authorino/identity/subject_access_review/test_subject_access_review.py b/testsuite/tests/singlecluster/authorino/identity/subject_access_review/test_subject_access_review.py new file mode 100644 index 00000000..e2e36b65 --- /dev/null +++ b/testsuite/tests/singlecluster/authorino/identity/subject_access_review/test_subject_access_review.py @@ -0,0 +1,47 @@ +"""Test kubernetes SubjectAccessReview authorization by verifying only a + ServiceAccount bound to a ClusterRole is authorized to access a resource""" + +import pytest + +from testsuite.httpx.auth import HeaderApiKeyAuth +from testsuite.kuadrant.policy.authorization import ValueFrom + +pytestmark = [pytest.mark.authorino] + + +@pytest.fixture(scope="module") +def authorization(authorization): + """Add kubernetes token-review and subject-access-review identity""" + authorization.identity.add_kubernetes("token-review-host") + user = ValueFrom("auth.identity.user.username") + authorization.authorization.add_kubernetes("subject-access-review-host", user, None) + return authorization + + +@pytest.fixture(scope="module") +def audience(hostname): + """Return hostname as only audience for the service account bound token""" + return [hostname.hostname] + + +@pytest.fixture(scope="module") +def service_account_token(create_service_account, audience): + """Create a non-authorized service account and request its bound token with the hostname as audience""" + service_account = create_service_account("tkn-non-auth") + return service_account.get_auth_token(audience) + + +@pytest.fixture(scope="module") +def auth2(service_account_token): + """Create request auth with service account token as API key""" + return HeaderApiKeyAuth(service_account_token, "Bearer") + + +def test_host_audience(client, auth, auth2): + """Test Kubernetes SubjectAccessReview functionality by setting up authentication and authorization for an endpoint + and querying it with non-authorized and authorized ServiceAccount.""" + response = client.get("/anything/get", auth=auth2) + assert response.status_code == 403 + + response = client.get("/get", auth=auth) + assert response.status_code == 200 diff --git a/testsuite/tests/singlecluster/authorino/identity/token_review/conftest.py b/testsuite/tests/singlecluster/authorino/identity/token_review/conftest.py index 3744c2f5..b5a2da60 100644 --- a/testsuite/tests/singlecluster/authorino/identity/token_review/conftest.py +++ b/testsuite/tests/singlecluster/authorino/identity/token_review/conftest.py @@ -3,20 +3,6 @@ import pytest from testsuite.httpx.auth import HeaderApiKeyAuth -from testsuite.kubernetes.service_account import ServiceAccount - - -@pytest.fixture(scope="module") -def create_service_account(request, cluster, blame, module_label): - """Creates and returns service account""" - - def _create_service_account(name): - service_account = ServiceAccount.create_instance(cluster, blame(name), labels={"app": module_label}) - request.addfinalizer(service_account.delete) - service_account.commit() - return service_account - - return _create_service_account @pytest.fixture(scope="module")