Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added identical hostnames test for AuthPolicy #439

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions testsuite/policy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,8 @@ def wait_for_ready(self):
"""Wait for a Policy to be Enforced"""
success = self.wait_until(has_condition("Enforced", "True"))
assert success, f"{self.kind()} did not get ready in time"

def wait_for_accepted(self):
"""Wait for a Policy to be Accepted"""
success = self.wait_until(has_condition("Accepted", "True"))
assert success, f"{self.kind()} did not get accepted in time"
2 changes: 1 addition & 1 deletion testsuite/policy/authorization/auth_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from . import Rule


class AuthPolicy(AuthConfig, Policy):
class AuthPolicy(Policy, AuthConfig):
"""AuthPolicy object, it serves as Kuadrants AuthConfig"""

@property
Expand Down
Empty file.
23 changes: 23 additions & 0 deletions testsuite/tests/kuadrant/identical_hostnames/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""Conftest for "identical hostname" tests"""

import pytest

from testsuite.gateway import GatewayRoute
from testsuite.gateway.gateway_api.route import HTTPRoute


@pytest.fixture(scope="module", autouse=True)
def route2(request, gateway, blame, hostname, backend, module_label) -> GatewayRoute:
"""HTTPRoute object serving as a 2nd route declaring identical hostname but different path"""
route = HTTPRoute.create_instance(gateway.openshift, blame("route"), gateway, {"app": module_label})
route.add_hostname(hostname.hostname)
route.add_backend(backend, "/anything/")
request.addfinalizer(route.delete)
route.commit()
return route


@pytest.fixture(scope="module")
def authorization_name2(blame):
"""Name of the 2nd Authorization resource"""
return blame("authz2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""
Tests behavior of using one HTTPRoute declaring the same hostname as parent Gateway related to AuthPolicy.
https://github.com/Kuadrant/kuadrant-operator/blob/main/doc/auth.md#limitation-multiple-network-resources-with-identical-hostnames
(second topology mentioned there)
"""

import pytest

from testsuite.policy.authorization.auth_policy import AuthPolicy

pytestmark = [pytest.mark.kuadrant_only]


@pytest.fixture(scope="class", autouse=True)
def authorization2(request, gateway, authorization_name2, openshift, label):
"""2nd Authorization object"""
auth_policy = AuthPolicy.create_instance(openshift, authorization_name2, gateway, labels={"testRun": label})
auth_policy.authorization.add_opa_policy("rego", "allow = false")
request.addfinalizer(auth_policy.delete)
auth_policy.commit()
auth_policy.wait_for_ready()
return auth_policy


def test_identical_hostnames_auth_on_gw_and_route_ignored(client, authorization, hostname):
"""
Tests that Gateway-attached AuthPolicy is ignored on 'route2' if both 'route' and 'route2' declare
identical hostname and there is another AuthPolicy already successfully enforced on 'route'.
Setup:
- Two HTTPRoutes declaring identical hostnames but different paths ('/' and '/anything/')
- Empty AuthPolicy enforced on the '/' HTTPRoute
- 'deny-all' AuthPolicy (created after Empty AuthPolicy) enforced on the Gateway
Test:
- Send a request via 'route' and assert that response status code is 200
- Send a request via 'route2' and assert that response status code is 200
- Delete the Empty AuthPolicy
- Send a request via both routes
- Assert that both response status codes are 403 (Forbidden)
"""

# Verify that the Empty AuthPolicy is still enforced despite 'deny-all' AuthPolicy being enforced too now
authorization.wait_for_ready()

# Access via 'route' is allowed due to Empty AuthPolicy
response = client.get("/get")
assert response.status_code == 200

# Despite 'deny-all' Gateway AuthPolicy reporting being successfully enforced
# it is still allowed to access the resources via 'route2'
response = client.get("/anything/get")
assert response.status_code == 200

# Deletion of Empty AuthPolicy should make the 'deny-all' Gateway AuthPolicy effectively enforced on both routes.
# It might take some time hence the use of retry client.
authorization.delete()
with hostname.client(retry_codes={200}) as retry_client:
response = retry_client.get("/get")
assert response.status_code == 403

response = client.get("/anything/get")
assert response.status_code == 403
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
Tests behavior of using two HTTPRoutes declaring the same hostname related to AuthPolicy.
https://github.com/Kuadrant/kuadrant-operator/blob/main/doc/auth.md#limitation-multiple-network-resources-with-identical-hostnames
(the first topology mentioned there)
"""

import pytest

from testsuite.policy import has_condition
from testsuite.policy.authorization.auth_policy import AuthPolicy

pytestmark = [pytest.mark.kuadrant_only]


@pytest.fixture(scope="class")
def authorization2(request, route2, authorization_name2, openshift, label):
"""2nd Authorization object"""
auth_policy = AuthPolicy.create_instance(openshift, authorization_name2, route2, labels={"testRun": label})
auth_policy.authorization.add_opa_policy("rego", "allow = false")
request.addfinalizer(auth_policy.delete)
auth_policy.commit()
auth_policy.wait_for_accepted()
return auth_policy


def test_identical_hostnames_auth_on_routes_rejected(client, authorization, authorization2):
"""
Tests that 2nd AuthPolicy is rejected on 'route2' declaring identical hostname as 'route' with another
AuthPolicy already successfully enforced on 'route'.
Setup:
- Two HTTPRoutes declaring identical hostnames but different paths ('/' and '/anything/')
- Empty AuthPolicy enforced on the '/' HTTPRoute
- 'deny-all' AuthPolicy (created after Empty AuthPolicy) accepted on the '/anything/' HTTPRoute
Test:
- Assert that 'deny-all' AuthPolicy reports an error
- Send a request via 'route' and assert that response status code is 200
- Send a request via 'route2' and assert that response status code is 200
- Delete the Empty AuthPolicy
- Change 'deny-all' AuthPolicy to trigger its reconciliation
- Send a request via both routes
- Assert that access via 'route' is 200 (OK)
- Assert that access via 'route2 is 403 (Forbidden)
"""
assert authorization2.wait_until(
has_condition(
"Enforced",
"False",
"Unknown",
"AuthPolicy has encountered some issues: AuthScheme is not ready yet",
),
timelimit=20,
), (
f"AuthPolicy did not reach expected status (Enforced False), "
f"instead it was: {authorization2.refresh().model.status.conditions}"
)

response = client.get("/get")
assert response.status_code == 200

response = client.get("/anything/get")
assert response.status_code == 200

# Deletion of Empty AuthPolicy should allow for 'deny-all' AuthPolicy to be enforced successfully.
authorization.delete()

# 2nd AuthPolicy only recovers from the "AuthScheme is not ready yet" error if reconciliation is explicitly
# triggered, e.g. by changing the AuthPolicy CR content (changing AllValues to True in this particular case)
# Reported as bug https://github.com/Kuadrant/kuadrant-operator/issues/702
authorization2.authorization.add_opa_policy("rego", "allow = false", True)
authorization2.refresh()
authorization2.wait_for_ready()

# Access via 'route' is still allowed
response = client.get("/get")
assert response.status_code == 200

# Access via 'route2' is now not allowed due to 'deny-all' AuthPolicy being enforced on 'route2'
response = client.get("/anything/get")
assert response.status_code == 403
Loading