Skip to content

Commit 62e03b3

Browse files
committed
load certs from oc secrets
1 parent 9123b69 commit 62e03b3

8 files changed

Lines changed: 104 additions & 107 deletions

File tree

config/settings.yaml.tpl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,25 @@ default:
9292
token: # token for openshift where the testenv tools are deployed (unnecessary for for default openshift)
9393
private_base_url:
9494
default: echo_api # tool name to be used by default for backend
95+
shared_certs:
96+
# config for obtaining certificates which are signed by same CA as tools.
97+
# If this config is not set, testsuite will use same openshift and namespace as tools
98+
namespace: tools # openshift namespace/project where the secrets with certificates are deployed
99+
server_url: # openshift url where the secrets with certificates are deployed
100+
token: # token for openshift where the secrets with certificates are deployed
101+
# certs can be also added manually
102+
client_certs:
103+
valid:
104+
- name: client1
105+
crt: "cert1"
106+
key: "key1"
107+
- name: client2
108+
crt: "cert2"
109+
key: "key2"
110+
invalid:
111+
- name: client3
112+
crt: "cert3"
113+
key: "key3"
95114
warn_and_skip:
96115
# section to control how warn_and_skip should behave for particular tests
97116
# works just for tests and fixture that use warn_and_skip

testsuite/dynaconf_loader.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,43 @@ def _rhsso_password(tools_config, rhsso_config):
145145
return None
146146

147147

148+
def _shared_tool_certs(tools_config, shared_certs_config):
149+
"""Search for SSO clients certificates"""
150+
if not shared_certs_config:
151+
shared_certs_config = tools_config
152+
server_url = shared_certs_config.get("server_url")
153+
project = shared_certs_config.get("namespace")
154+
token = shared_certs_config.get("token")
155+
client = OpenShiftClient(project_name=project, server_url=server_url, token=token)
156+
try:
157+
iter(client.secrets)
158+
except OpenShiftPythonException:
159+
return {"valid": [], "invalid": []}
160+
161+
valid_certs = []
162+
invalid_certs = []
163+
for secret in client.secrets:
164+
try:
165+
name = secret["metadata"]["name"]
166+
annotations = secret["metadata"]["annotations"]
167+
shared_cert = annotations["shared_cert"]
168+
valid = annotations["valid"]
169+
except KeyError:
170+
continue
171+
if shared_cert != "True":
172+
continue
173+
cert = {
174+
"name": name,
175+
"crt": client.secrets[name]["tls.crt"].decode("utf-8"),
176+
"key": client.secrets[name]["tls.key"].decode("utf-8"),
177+
}
178+
if valid == "True":
179+
valid_certs.append(cert)
180+
if valid == "False":
181+
invalid_certs.append(cert)
182+
return {"valid": valid_certs, "invalid": invalid_certs}
183+
184+
148185
def _threescale_operator_ocp(ocp):
149186
try:
150187
ocp.threescale_operator # pylint: disable=pointless-statement
@@ -247,6 +284,7 @@ def load(obj, env=None, silent=None, key=None):
247284
ocp_tools_setup = ocp_setup
248285

249286
rhsso_setup = obj.get("rhsso", {})
287+
shared_certs_setup = obj.get("shared_certs", {})
250288

251289
ocp = OpenShiftClient(
252290
project_name=project, server_url=ocp_setup.get("server_url"), token=ocp_setup.get("token")
@@ -337,6 +375,7 @@ def load(obj, env=None, silent=None, key=None):
337375
"apicast": {"openshift": apicast_operator_ocp},
338376
},
339377
"rhsso": {"password": _rhsso_password(ocp_tools_setup, rhsso_setup)},
378+
"shared_certs": {"client_certs": _shared_tool_certs(ocp_tools_setup, shared_certs_setup)},
340379
}
341380

342381
# this overwrites what's already in settings to ensure NAMESPACE is propagated

testsuite/rhsso/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Utility classes for working with RHSSO server"""
22

33
import functools
4+
from typing import cast
45

56
import backoff
67
from keycloak import KeycloakOpenID
@@ -26,13 +27,13 @@ def __init__(self, rhsso: RHSSO, realm: Realm, client: Client, user, username, p
2627
self.client = client
2728
self.username = username
2829
self.password = password
29-
self._oidc_client = None
30+
self._oidc_client: KeycloakOpenID | None = None
3031

3132
@property
3233
def oidc_client(self) -> KeycloakOpenID:
3334
"""OIDCClient for the created client"""
3435
if not self._oidc_client:
35-
self._oidc_client = self.client.oidc_client
36+
self._oidc_client = cast(KeycloakOpenID, self.client.oidc_client)
3637
return self._oidc_client
3738

3839
def issuer_url(self) -> str:

testsuite/rhsso/objects.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""This module contains object wrappers on top of python-keycloak API, since that is not object based"""
2-
from enum import verify
2+
33
from urllib.parse import urlparse
44

55
from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakPostError

testsuite/tests/apicast/policy/fapi/__init__.py

Whitespace-only changes.

testsuite/tests/apicast/policy/fapi/certificates/client.crt

Lines changed: 0 additions & 25 deletions
This file was deleted.

testsuite/tests/apicast/policy/fapi/certificates/client.key

Lines changed: 0 additions & 52 deletions
This file was deleted.

testsuite/tests/apicast/policy/fapi/test_advanced_profile.py

Lines changed: 42 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,42 @@
11
"""Test Financial-Grade API policy with oauth2 certificate bound access token (advanced profile)"""
22

3-
import os
43
import pytest
54
import requests
65

76
from testsuite import rawobj
8-
from testsuite.utils import blame
7+
from testsuite.utils import blame, warn_and_skip
98
from testsuite.rhsso import OIDCClientAuth, OIDCClientAuthHook
10-
# pylint: disable=reimported,unused-import
11-
from testsuite.tests.apicast.policy.tls.conftest import (manager, superdomain, server_authority, staging_gateway,
12-
gateway_options, gateway_environment)
13-
# pylint: disable=reimported,unused-import
14-
from testsuite.tests.apicast.policy.tls.conftest import (certificate, superdomain, valid_authority, create_cert,
15-
superdomain, manager)
9+
from testsuite.certificates import Certificate
10+
11+
# pylint: disable=reimported, unused-import
12+
# flake8: noqa
13+
from testsuite.tests.apicast.policy.tls.conftest import (
14+
certificate,
15+
manager,
16+
superdomain,
17+
server_authority,
18+
staging_gateway,
19+
gateway_options,
20+
gateway_environment,
21+
valid_authority,
22+
create_cert,
23+
)
1624

1725

18-
@pytest.fixture()
19-
def mtls_client_cert():
26+
@pytest.fixture(scope="module")
27+
def mtls_client_cert(request, testconfig):
2028
"""Clients certificate. CA of this cert needs to be trusted by the sso"""
21-
# todo create certs dynamically
22-
script_dir = os.path.dirname(os.path.abspath(__file__))
23-
crt_path = os.path.join(script_dir, "certificates", "client.crt" )
24-
key_path = os.path.join(script_dir, "certificates", "client.key" )
25-
return crt_path, key_path
29+
try:
30+
crt = testconfig["shared_certs"]["client_certs"]["valid"][0]
31+
except IndexError:
32+
warn_and_skip("Valid tools cert is not available, skipping fapi advanced tests")
33+
cert = Certificate(crt["key"], crt["crt"])
34+
if not testconfig["skip_cleanup"]:
35+
request.addfinalizer(cert.delete_files)
36+
return cert.files["certificate"], cert.files["key"]
2637

2738

39+
# flake8: noqa
2840
@pytest.fixture()
2941
def unknown_cert(certificate):
3042
"""Cert which won't be used for obtaining the token"""
@@ -40,10 +52,13 @@ def service(service):
4052
3scale APIcast
4153
Fapi
4254
"""
43-
fapi_policy = rawobj.PolicyConfig("fapi", configuration = {
44-
"validate_x_fapi_customer_ip_address": True,
45-
"validate_oauth2_certificate_bound_access_token": True
46-
})
55+
fapi_policy = rawobj.PolicyConfig(
56+
"fapi",
57+
configuration={
58+
"validate_x_fapi_customer_ip_address": True,
59+
"validate_oauth2_certificate_bound_access_token": True,
60+
},
61+
)
4762
service.proxy.list().policies.insert(1, fapi_policy)
4863
return service
4964

@@ -56,16 +71,15 @@ def fapi_sso_client_id():
5671

5772
# pylint: disable=too-many-arguments
5873
@pytest.fixture(scope="module")
59-
def application(service, custom_application, custom_app_plan, lifecycle_hooks, request, fapi_sso_client_id,
60-
rhsso_service_info):
74+
def application(
75+
service, custom_application, custom_app_plan, lifecycle_hooks, request, fapi_sso_client_id, rhsso_service_info
76+
):
6177
"""application bound to the account and service existing over whole testing session"""
6278
plan = custom_app_plan(rawobj.ApplicationPlan(blame(request, blame(request, "fapi"))), service)
6379
app = custom_application(
64-
rawobj.Application(
65-
name=fapi_sso_client_id,
66-
app_id=fapi_sso_client_id,
67-
application_plan=plan),
68-
hooks=lifecycle_hooks)
80+
rawobj.Application(name=fapi_sso_client_id, app_id=fapi_sso_client_id, application_plan=plan),
81+
hooks=lifecycle_hooks,
82+
)
6983
service.proxy.deploy()
7084
app.register_auth("oidc", OIDCClientAuth.partial(rhsso_service_info))
7185
return app
@@ -88,14 +102,15 @@ def fapi_sso_client(rhsso_service_info, fapi_sso_client_id):
88102
"tls.client.certificate.bound.access.tokens": "true",
89103
"client.authentication.type": "tls",
90104
"x509.allow.regex.pattern.comparison": "true",
91-
}
105+
},
92106
}
93107
return rhsso_service_info.realm.create_client(fapi_sso_client_id, **client_config)
94108

95109

96110
# pylint: disable=too-few-public-methods
97111
class FapiClient:
98112
"""Temporary class for sending requests to apicast. This should be replaced by using fixture api_client"""
113+
99114
def __init__(self, base_url, verify):
100115
self.base_url = base_url
101116
self.http_session = requests.Session()

0 commit comments

Comments
 (0)