Skip to content

Commit

Permalink
Add interface for defaults/overrides
Browse files Browse the repository at this point in the history
  • Loading branch information
averevki committed Jul 22, 2024
1 parent d6499c9 commit 20a8ffd
Show file tree
Hide file tree
Showing 80 changed files with 205 additions and 142 deletions.
5 changes: 5 additions & 0 deletions testsuite/kuadrant/policy/authorization/auth_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ def auth_section(self):
"""Returns objects where all auth related things should be added"""
return self.model.spec

@property
def rules(self):
"""Returns `self`. Required for compatibility with AuthPolicy class"""
return self

@cached_property
def authorization(self) -> AuthorizationSection:
"""Gives access to authorization settings"""
Expand Down
36 changes: 32 additions & 4 deletions testsuite/kuadrant/policy/authorization/auth_policy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
class AuthPolicy(Policy, AuthConfig):
"""AuthPolicy object, it serves as Kuadrants AuthConfig"""

@property
def auth_section(self):
return self.model.spec.setdefault("rules", {})
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.spec_section = None

@classmethod
def create_instance(
Expand All @@ -35,7 +35,6 @@ def create_instance(
"metadata": {"name": name, "namespace": cluster.project, "labels": labels},
"spec": {
"targetRef": target.reference,
"rules": {},
},
}

Expand All @@ -46,3 +45,32 @@ def add_rule(self, when: list["Rule"]):
"""Add rule for the skip of entire AuthPolicy"""
self.model.spec.setdefault("when", [])
self.model.spec["when"].extend([asdict(x) for x in when])

@property
def auth_section(self):
if self.spec_section is None:
raise AttributeError(
"AuthPolicy spec_section is empty "
"(e.g. authorization.rules, authorization.defaults, authorization.overrides)"
)
spec_section = self.spec_section
self.spec_section = None
return spec_section.setdefault("rules", {})

@property
def rules(self):
"""Add new rule into the `rules` AuthPolicy section"""
self.spec_section = self.model.spec
return self

@property
def defaults(self):
"""Add new rule into the `defaults` AuthPolicy section"""
self.spec_section = self.model.spec.setdefault("defaults", {})
return self

@property
def overrides(self):
"""Add new rule into the `overrides` AuthPolicy section"""
self.spec_section = self.model.spec.setdefault("overrides", {})
return self
32 changes: 30 additions & 2 deletions testsuite/kuadrant/policy/rate_limit.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ def __init__(self, *matches: RouteMatch, hostnames: Optional[List[str]] = None):
class RateLimitPolicy(Policy):
"""RateLimitPolicy (or RLP for short) object, used for applying rate limiting rules to a Gateway/HTTPRoute"""

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.spec_section = None

@classmethod
def create_instance(cls, cluster: KubernetesClient, name, target: Referencable, labels: dict[str, str] = None):
"""Creates new instance of RateLimitPolicy"""
Expand All @@ -50,7 +54,6 @@ def create_instance(cls, cluster: KubernetesClient, name, target: Referencable,
"metadata": {"name": name, "labels": labels},
"spec": {
"targetRef": target.reference,
"limits": {},
},
}

Expand All @@ -75,7 +78,32 @@ def add_limit(
limit["counters"] = counters
if route_selectors:
limit["routeSelectors"] = [asdict(rule) for rule in route_selectors]
self.model.spec.limits[name] = limit

if self.spec_section is None:
raise AttributeError(
"RateLimitPolicy spec_section is empty "
"(e.g. rate_limit.limits, rate_limit.defaults, rate_limit.overrides)"
)
self.spec_section.setdefault("limits", {})[name] = limit
self.spec_section = None

@property
def limits(self):
"""Add new rule into the `limits` RateLimitPolicy section"""
self.spec_section = self.model.spec
return self

@property
def defaults(self):
"""Add new rule into the `defaults` RateLimitPolicy section"""
self.spec_section = self.model.spec.setdefault("defaults", {})
return self

@property
def overrides(self):
"""Add new rule into the `overrides` RateLimitPolicy section"""
self.spec_section = self.model.spec.setdefault("overrides", {})
return self

def wait_for_ready(self):
"""Wait for RLP to be enforced"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ def authorization(authorization, opa_policy_expectation):
Adds OPA policy. Rego query is located on external registry (Mockserver).
Policy accepts requests that contain `header`.
"""
authorization.authorization.add_external_opa_policy("opa", opa_policy_expectation, 1)
authorization.rules.authorization.add_external_opa_policy("opa", opa_policy_expectation, 1)
return authorization
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,8 @@ def authorization(authorization, rego_policy):
allValues set to 'true' so that values of all rules declared in the Rego policy - including value in rpt variable -
are returned after policy evaluation so that the value from rpt variable can be added to the success header.
"""
authorization.authorization.add_opa_policy("opa", rego_policy, all_values=True)
authorization.responses.add_success_header(
authorization.rules.authorization.add_opa_policy("opa", rego_policy, all_values=True)
authorization.rules.responses.add_success_header(
"x-keycloak",
JsonResponse({"rpt": ValueFrom("auth.authorization.opa.rpt")}),
when=[Pattern("auth.identity.authorization.permissions", "eq", "")],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def header():
@pytest.fixture(scope="module")
def authorization(authorization, header):
"""Adds OPA policy that accepts all requests that contain `header`"""
authorization.authorization.add_opa_policy("opa", rego_allow_header(*header))
authorization.rules.authorization.add_opa_policy("opa", rego_allow_header(*header))
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ def expectation_path(mockserver, module_label):
@pytest.fixture(scope="module")
def authorization(authorization):
"""Adds `aut.metadata` to the AuthJson"""
authorization.responses.add_simple("auth.metadata")
authorization.rules.responses.add_simple("auth.metadata")
return authorization
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
def authorization(authorization, module_label, expectation_path):
"""Adds Cached Metadata to the AuthConfig"""
meta_cache = Cache(5, ValueFrom("context.request.http.path"))
authorization.metadata.add_http(module_label, expectation_path, "GET", cache=meta_cache)
authorization.rules.metadata.add_http(module_label, expectation_path, "GET", cache=meta_cache)
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
@pytest.fixture(scope="module")
def authorization(authorization, module_label, expectation_path):
"""Adds simple Metadata to the AuthConfig"""
authorization.metadata.add_http(module_label, expectation_path, "GET")
authorization.rules.metadata.add_http(module_label, expectation_path, "GET")
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ def cache_ttl():
def authorization(authorization, module_label, expectation_path, cache_ttl):
"""Adds Cached Metadata to the AuthConfig"""
meta_cache = Cache(cache_ttl, ValueFrom("context.request.http.path"))
authorization.metadata.add_http(module_label, expectation_path, "GET", cache=meta_cache)
authorization.rules.metadata.add_http(module_label, expectation_path, "GET", cache=meta_cache)
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
def authorization(authorization):
"""Add to the AuthConfig authorization with opa policy that will always reject POST requests"""
when_post = [Pattern("context.request.http.method", "eq", "POST")]
authorization.authorization.add_opa_policy("opa", "allow { false }", when=when_post)
authorization.rules.authorization.add_opa_policy("opa", "allow { false }", when=when_post)
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def auth(api_key):
def authorization(authorization, api_key):
"""Add to the AuthConfig API key identity, which can only be used on requests to the /get path"""
when_get = [Pattern("context.request.http.path", "eq", "/get")]
authorization.identity.add_api_key("api-key", selector=api_key.selector, when=when_get)
authorization.rules.identity.add_api_key("api-key", selector=api_key.selector, when=when_get)
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def authorization(authorization, mockserver_expectation):
which will be only triggered on POST requests to the endpoint
"""
when_post = [Pattern("context.request.http.method", "eq", "POST")]
authorization.metadata.add_http("mock", mockserver_expectation, "GET", when=when_post)
authorization.rules.metadata.add_http("mock", mockserver_expectation, "GET", when=when_post)
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
@pytest.fixture(scope="module")
def authorization(authorization):
"""Add to the AuthConfig response, which will only trigger on POST requests"""
authorization.responses.add_success_header(
authorization.rules.responses.add_success_header(
"simple", JsonResponse({"data": Value("response")}), when=[Pattern("context.request.http.method", "eq", "POST")]
)
return authorization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def authorization(authorization):
}
)

authorization.authorization.add_auth_rules(
authorization.rules.authorization.add_auth_rules(
"auth_rules",
[
AnyPattern(
Expand Down
2 changes: 1 addition & 1 deletion testsuite/tests/singlecluster/authorino/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def authorization(authorization, oidc_provider, route, authorization_name, clust
"""In case of Authorino, AuthConfig used for authorization"""
if authorization is None:
authorization = AuthConfig.create_instance(cluster, authorization_name, route, labels={"testRun": label})
authorization.identity.add_oidc("default", oidc_provider.well_known["issuer"])
authorization.rules.identity.add_oidc("default", oidc_provider.well_known["issuer"])
return authorization


Expand Down
36 changes: 18 additions & 18 deletions testsuite/tests/singlecluster/authorino/dinosaur/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,29 +131,29 @@ def authorization(authorization, keycloak, terms_and_conditions, cluster_info, a
)
authorization.add_rule([PatternRef("api-route"), PatternRef("v1-route")])

authorization.identity.clear_all()
authorization.identity.add_oidc(
authorization.rules.identity.clear_all()
authorization.rules.identity.add_oidc(
"user-sso",
keycloak.well_known["issuer"],
ttl=3600,
defaults_properties={"org_id": ValueFrom("auth.identity.family_name")},
)
authorization.identity.add_oidc(
authorization.rules.identity.add_oidc(
"admin-sso", admin_rhsso.well_known["issuer"], ttl=3600, when=[PatternRef("admin-route")]
)

authorization.metadata.add_http(
authorization.rules.metadata.add_http(
"terms-and-conditions", terms_and_conditions("false"), "GET", when=[PatternRef("create-dinosaur-route")]
)
authorization.metadata.add_http(
authorization.rules.metadata.add_http(
"cluster-info", cluster_info(keycloak.client_name), "GET", when=[PatternRef("agent-clusters-route")]
)
authorization.metadata.add_http(
authorization.rules.metadata.add_http(
"resource-info", resource_info("123", keycloak.client_name), "GET", when=[PatternRef("dinosaur-resource-route")]
)

authorization.authorization.add_auth_rules("bearer-token", [Pattern("auth.identity.typ", "eq", "Bearer")])
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_auth_rules("bearer-token", [Pattern("auth.identity.typ", "eq", "Bearer")])
authorization.rules.authorization.add_opa_policy(
"deny-list",
"""list := [
"[email protected]"
Expand All @@ -163,7 +163,7 @@ def authorization(authorization, keycloak, terms_and_conditions, cluster_info, a
""",
when=[PatternRef("acl-required")],
)
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_opa_policy(
"allow-list",
"""list := [
"123"
Expand All @@ -172,25 +172,25 @@ def authorization(authorization, keycloak, terms_and_conditions, cluster_info, a
""",
when=[PatternRef("acl-required")],
)
authorization.authorization.add_auth_rules(
authorization.rules.authorization.add_auth_rules(
"terms-and-conditions",
[Pattern("auth.metadata.terms-and-conditions.terms_required", "eq", "false")],
when=[PatternRef("create-dinosaur-route")],
)
for name in ["dinosaurs", "metrics-federate", "service-accounts", "supported-instance-types"]:
authorization.authorization.add_auth_rules(
authorization.rules.authorization.add_auth_rules(
name, [PatternRef("user-sso"), PatternRef("require-org-id")], when=[PatternRef(f"{name}-route")]
)

authorization.authorization.add_auth_rules(
authorization.rules.authorization.add_auth_rules(
"agent-clusters", [PatternRef("user-sso")], when=[PatternRef("agent-clusters-route")]
)
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_opa_policy(
"cluster-id",
"""allow { input.auth.identity.azp == object.get(input.auth.metadata, "cluster-info", {}).client_id }""",
when=[PatternRef("agent-clusters-route")],
)
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_opa_policy(
"owner",
"""org_id := input.auth.identity.org_id
filter_by_org { org_id }
Expand All @@ -208,7 +208,7 @@ def authorization(authorization, keycloak, terms_and_conditions, cluster_info, a
""",
when=[PatternRef("dinosaur-resource-route")],
)
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_opa_policy(
"admin-rbac",
"""method := input.context.request.http.method
roles := input.auth.identity.realm_access.roles
Expand All @@ -221,16 +221,16 @@ def authorization(authorization, keycloak, terms_and_conditions, cluster_info, a
""",
when=[PatternRef("admin-route"), PatternRef("admin-sso")],
)
authorization.authorization.add_opa_policy(
authorization.rules.authorization.add_opa_policy(
"require-admin-sso",
"""allow { false }""",
when=[PatternRef("admin-route"), PatternRef("user-sso")],
)
authorization.authorization.add_auth_rules(
authorization.rules.authorization.add_auth_rules(
"internal-endpoints", [Pattern(path_fourth_element, "neq", "authz-metadata")]
)

authorization.responses.set_unauthorized(
authorization.rules.responses.set_unauthorized(
DenyResponse(
headers={"content-type": Value("application/json")},
body=Value(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
@pytest.fixture(scope="module")
def authorization(authorization):
"""Setup AuthConfig for test"""
authorization.identity.add_anonymous("anonymous")
authorization.responses.add_simple("auth.identity.anonymous")
authorization.rules.identity.add_anonymous("anonymous")
authorization.rules.responses.add_simple("auth.identity.anonymous")
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@pytest.fixture(scope="module")
def authorization(authorization, keycloak):
"""Add Keycloak identity"""
authorization.identity.add_oidc("keycloak", keycloak.well_known["issuer"])
authorization.rules.identity.add_oidc("keycloak", keycloak.well_known["issuer"])
return authorization


Expand All @@ -31,7 +31,7 @@ def test_anonymous_identity(client, auth, authorization):
response = client.get("/get")
assert response.status_code == 401

authorization.identity.add_anonymous("anonymous")
authorization.rules.identity.add_anonymous("anonymous")
authorization.wait_for_ready()

response = client.get("/get")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
@pytest.fixture(scope="module")
def authorization(authorization, api_key):
"""Setup AuthConfig for test"""
authorization.identity.add_api_key("api_key", selector=api_key.selector)
authorization.responses.add_simple("auth.identity")
authorization.rules.identity.add_api_key("api_key", selector=api_key.selector)
authorization.rules.responses.add_simple("auth.identity")
return authorization


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def credentials(request):
@pytest.fixture(scope="module")
def authorization(authorization, api_key, credentials):
"""Add API key identity to AuthConfig"""
authorization.identity.add_api_key(
authorization.rules.identity.add_api_key(
"api_key", credentials=Credentials(credentials, "APIKEY"), selector=api_key.selector
)
return authorization
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def valid_label_selectors(module_label):
def authorization(authorization, valid_label_selectors):
"""Creates AuthConfig with API key identity"""
selector = Selector(matchExpressions=[MatchExpression("In", valid_label_selectors)])
authorization.identity.add_api_key("api_key", selector=selector)
authorization.rules.identity.add_api_key("api_key", selector=selector)
return authorization


Expand Down
Loading

0 comments on commit 20a8ffd

Please sign in to comment.