1+ import secrets
12from unittest import mock
23
34import pytest
45import requests
6+ import yaml
57from settings import TEST_DATA
6- from suite .utils .policy_resources_utils import create_policy_from_yaml , delete_policy
7- from suite .utils .resources_utils import replace_configmap_from_yaml , wait_before_test
8+ from suite .utils .policy_resources_utils import delete_policy
9+ from suite .utils .resources_utils import (
10+ create_example_app ,
11+ create_secret_from_yaml ,
12+ delete_common_app ,
13+ delete_secret ,
14+ replace_configmap_from_yaml ,
15+ wait_before_test ,
16+ wait_until_all_pods_are_ready ,
17+ )
818from suite .utils .vs_vsr_resources_utils import (
919 create_virtual_server_from_yaml ,
1020 delete_and_create_vs_from_yaml ,
1121 delete_virtual_server ,
1222)
1323
24+ username = "nginx-user-" + secrets .token_hex (4 )
25+ password = secrets .token_hex (8 )
26+ realm_name = "jwks-example"
1427std_vs_src = f"{ TEST_DATA } /virtual-server/standard/virtual-server.yaml"
1528jwt_pol_valid_src = f"{ TEST_DATA } /jwt-policy-jwksuri/policies/jwt-policy-valid.yaml"
1629jwt_pol_invalid_src = f"{ TEST_DATA } /jwt-policy-jwksuri/policies/jwt-policy-invalid.yaml"
2740 f"{ TEST_DATA } /jwt-policy-jwksuri/virtual-server/virtual-server-invalid-policy-route-subpath.yaml"
2841)
2942jwt_cm_src = f"{ TEST_DATA } /jwt-policy-jwksuri/configmap/nginx-config.yaml"
30- ad_tenant = "dd3dfd2f-6a3b-40d1-9be0-bf8327d81c50 "
31- client_id = "8a172a83-a630-41a4-9ca6-1e5ef03cd7e7 "
43+ keycloak_src = f" { TEST_DATA } /oidc/keycloak.yaml "
44+ keycloak_vs_src = f" { TEST_DATA } /oidc/virtual-server-idp.yaml "
3245
3346
34- def get_token ( request ) :
47+ class KeycloakSetup :
3548 """
36- get jwt token from azure ad endpoint
49+ Attributes:
50+ token (str):
3751 """
52+
53+ def __init__ (self , token ):
54+ self .token = token
55+
56+
57+ @pytest .fixture (scope = "class" )
58+ def keycloak_setup (request , kube_apis , test_namespace , ingress_controller_endpoint ):
59+
60+ # Create Keycloak resources and setup Keycloak idp
61+
62+ secret_name = create_secret_from_yaml (
63+ kube_apis .v1 , test_namespace , f"{ TEST_DATA } /virtual-server-tls/tls-secret.yaml"
64+ )
65+ keycloak_address = "keycloak.example.com"
66+ create_example_app (kube_apis , "keycloak" , test_namespace )
67+ wait_before_test ()
68+ wait_until_all_pods_are_ready (kube_apis .v1 , test_namespace )
69+ keycloak_vs_name = create_virtual_server_from_yaml (kube_apis .custom_objects , keycloak_vs_src , test_namespace )
70+ wait_before_test ()
71+
72+ # Get token
73+ url = f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /realms/master/protocol/openid-connect/token"
74+ headers = {"Host" : keycloak_address , "Content-Type" : "application/x-www-form-urlencoded" }
75+ data = {"username" : "admin" , "password" : "admin" , "grant_type" : "password" , "client_id" : "admin-cli" }
76+
77+ response = requests .post (url , headers = headers , data = data , verify = False )
78+ admin_token = response .json ()["access_token" ]
79+
80+ # Create realm "jwks-example"
81+ create_realm_url = (
82+ f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /admin/realms"
83+ )
84+ headers = {"Content-Type" : "application/json" , "Authorization" : f"Bearer { admin_token } " , "Host" : keycloak_address }
85+ payload = {
86+ "realm" : realm_name ,
87+ "enabled" : True ,
88+ }
89+ response = requests .post (create_realm_url , headers = headers , json = payload , verify = False )
90+
91+ # Create a user and set credentials
92+ create_user_url = f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /admin/realms/{ realm_name } /users"
93+ headers = {"Content-Type" : "application/json" , "Authorization" : f"Bearer { admin_token } " , "Host" : keycloak_address }
94+ user_payload = {
95+ "username" : username ,
96+ "enabled" : True ,
97+ 98+ "emailVerified" : True ,
99+ "firstName" : "Jwks" ,
100+ "lastName" : "User" ,
101+ "credentials" : [{"type" : "password" , "value" : password , "temporary" : False }],
102+ "requiredActions" : [],
103+ }
104+ response = requests .post (create_user_url , headers = headers , json = user_payload , verify = False )
105+
106+ # Create a client
107+ headers = {"Content-Type" : "application/json" , "Authorization" : f"Bearer { admin_token } " , "Host" : keycloak_address }
108+ create_client_url = f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /admin/realms/{ realm_name } /clients"
109+ client_payload = {
110+ "clientId" : "jwks-client" ,
111+ "enabled" : True ,
112+ "protocol" : "openid-connect" ,
113+ "publicClient" : False ,
114+ "directAccessGrantsEnabled" : True ,
115+ "standardFlowEnabled" : True ,
116+ "implicitFlowEnabled" : False ,
117+ "serviceAccountsEnabled" : True ,
118+ "authorizationServicesEnabled" : True ,
119+ "clientAuthenticatorType" : "client-secret" ,
120+ "redirectUris" : ["*" ],
121+ "webOrigins" : ["*" ],
122+ "attributes" : {
123+ "access.token.lifespan" : "3600" ,
124+ "id.token.lifespan" : "3600" ,
125+ "service.accounts.enabled" : "true" ,
126+ },
127+ }
128+ client_resp = requests .post (create_client_url , headers = headers , json = client_payload , verify = False )
129+ if client_resp .status_code not in (200 , 201 ):
130+ pytest .fail (f"Failed to create client: { client_resp .text } " )
131+ location = client_resp .headers ["Location" ]
132+ client_id = location .split ("/" )[- 1 ]
133+
134+ # Get the client secret
135+ get_secret_url = f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /admin/realms/{ realm_name } /clients/{ client_id } /client-secret"
136+ secret_resp = requests .get (get_secret_url , headers = headers , verify = False )
137+ secret = secret_resp .json ()["value" ]
138+
38139 data = {
39- "client_id" : f"{ client_id } " ,
40- "scope" : ".default" ,
41- "client_secret" : request .config .getoption ("--ad-secret" ),
42- "grant_type" : "client_credentials" ,
140+ "grant_type" : "password" ,
141+ "scope" : "openid" ,
142+ "client_id" : "jwks-client" ,
143+ "client_secret" : secret ,
144+ "username" : username ,
145+ "password" : password ,
43146 }
44- ad_response = requests .get (
45- f"https://login.microsoftonline.com/{ ad_tenant } /oauth2/token" ,
147+ url = f"https://{ ingress_controller_endpoint .public_ip } :{ ingress_controller_endpoint .port_ssl } /realms/{ realm_name } /protocol/openid-connect/token"
148+ response = requests .post (
149+ url ,
150+ headers = {"Host" : keycloak_address , "Content-Type" : "application/x-www-form-urlencoded" },
46151 data = data ,
47- timeout = 5 ,
48- headers = {"User-Agent" : "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:47.0) Gecko/20100101 Chrome/76.0.3809.100" },
152+ verify = False ,
49153 )
50154
51- if ad_response .status_code == 200 :
52- return ad_response .json ()["access_token" ]
53- pytest .fail ("Unable to request Azure token endpoint" )
155+ if response .status_code != 200 :
156+ pytest .fail (f"Failed to get token from Keycloak: { response .text } " )
157+
158+ token = response .json ().get ("access_token" )
159+
160+ def fin ():
161+ if request .config .getoption ("--skip-fixture-teardown" ) == "no" :
162+ print ("Delete Keycloak resources" )
163+ delete_virtual_server (kube_apis .custom_objects , keycloak_vs_name , test_namespace )
164+ delete_common_app (kube_apis , "keycloak" , test_namespace )
165+ delete_secret (kube_apis .v1 , secret_name , test_namespace )
166+
167+ request .addfinalizer (fin )
168+
169+ return KeycloakSetup (token )
54170
55171
56172@pytest .mark .skip_for_nginx_oss
57- @pytest .mark .skip (reason = "issues with IdP communication" )
58173@pytest .mark .policies
59174@pytest .mark .parametrize (
60175 "crd_ingress_controller, virtual_server_setup" ,
@@ -85,6 +200,7 @@ def test_jwt_policy_jwksuri(
85200 crd_ingress_controller ,
86201 virtual_server_setup ,
87202 test_namespace ,
203+ keycloak_setup ,
88204 jwt_virtual_server ,
89205 ):
90206 """
@@ -96,7 +212,12 @@ def test_jwt_policy_jwksuri(
96212 ingress_controller_prerequisites .namespace ,
97213 jwt_cm_src ,
98214 )
99- pol_name = create_policy_from_yaml (kube_apis .custom_objects , jwt_pol_valid_src , test_namespace )
215+ with open (jwt_pol_valid_src ) as f :
216+ doc = yaml .safe_load (f )
217+ pol_name = doc ["metadata" ]["name" ]
218+ doc ["spec" ]["jwt" ]["jwksURI" ] = doc ["spec" ]["jwt" ]["jwksURI" ].replace ("default" , test_namespace )
219+ kube_apis .custom_objects .create_namespaced_custom_object ("k8s.nginx.org" , "v1" , test_namespace , "policies" , doc )
220+ print (f"Policy created with name { pol_name } " )
100221 wait_before_test ()
101222
102223 print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -106,6 +227,7 @@ def test_jwt_policy_jwksuri(
106227 jwt_virtual_server ,
107228 virtual_server_setup .namespace ,
108229 )
230+ wait_before_test ()
109231 resp_no_token = mock .Mock ()
110232 resp_no_token .status_code == 502
111233 counter = 0
@@ -118,14 +240,13 @@ def test_jwt_policy_jwksuri(
118240 wait_before_test ()
119241 counter += 1
120242
121- token = get_token ( request )
243+ token = keycloak_setup . token
122244
123245 resp_valid_token = requests .get (
124246 virtual_server_setup .backend_1_url ,
125247 headers = {"host" : virtual_server_setup .vs_host , "token" : token },
126248 timeout = 5 ,
127249 )
128-
129250 delete_policy (kube_apis .custom_objects , pol_name , test_namespace )
130251 wait_before_test ()
131252
@@ -148,6 +269,7 @@ def test_jwt_invalid_policy_jwksuri(
148269 crd_ingress_controller ,
149270 virtual_server_setup ,
150271 test_namespace ,
272+ keycloak_setup ,
151273 jwt_virtual_server ,
152274 ):
153275 """
@@ -159,7 +281,12 @@ def test_jwt_invalid_policy_jwksuri(
159281 ingress_controller_prerequisites .namespace ,
160282 jwt_cm_src ,
161283 )
162- pol_name = create_policy_from_yaml (kube_apis .custom_objects , jwt_pol_invalid_src , test_namespace )
284+ with open (jwt_pol_invalid_src ) as f :
285+ doc = yaml .safe_load (f )
286+ pol_name = doc ["metadata" ]["name" ]
287+ doc ["spec" ]["jwt" ]["jwksURI" ] = doc ["spec" ]["jwt" ]["jwksURI" ].replace ("default" , test_namespace )
288+ kube_apis .custom_objects .create_namespaced_custom_object ("k8s.nginx.org" , "v1" , test_namespace , "policies" , doc )
289+ print (f"Policy created with name { pol_name } " )
163290 wait_before_test ()
164291
165292 print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -176,7 +303,7 @@ def test_jwt_invalid_policy_jwksuri(
176303 headers = {"host" : virtual_server_setup .vs_host },
177304 )
178305
179- token = get_token ( request )
306+ token = keycloak_setup . token
180307
181308 resp2 = requests .get (
182309 virtual_server_setup .backend_1_url ,
@@ -204,6 +331,7 @@ def test_jwt_policy_subroute_jwksuri(
204331 crd_ingress_controller ,
205332 virtual_server_setup ,
206333 test_namespace ,
334+ keycloak_setup ,
207335 jwt_virtual_server ,
208336 ):
209337 """
@@ -215,7 +343,12 @@ def test_jwt_policy_subroute_jwksuri(
215343 ingress_controller_prerequisites .namespace ,
216344 jwt_cm_src ,
217345 )
218- pol_name = create_policy_from_yaml (kube_apis .custom_objects , jwt_pol_valid_src , test_namespace )
346+ with open (jwt_pol_valid_src ) as f :
347+ doc = yaml .safe_load (f )
348+ pol_name = doc ["metadata" ]["name" ]
349+ doc ["spec" ]["jwt" ]["jwksURI" ] = doc ["spec" ]["jwt" ]["jwksURI" ].replace ("default" , test_namespace )
350+ kube_apis .custom_objects .create_namespaced_custom_object ("k8s.nginx.org" , "v1" , test_namespace , "policies" , doc )
351+ print (f"Policy created with name { pol_name } " )
219352 wait_before_test ()
220353
221354 print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -237,7 +370,7 @@ def test_jwt_policy_subroute_jwksuri(
237370 wait_before_test ()
238371 counter += 1
239372
240- token = get_token ( request )
373+ token = keycloak_setup . token
241374
242375 resp_valid_token = requests .get (
243376 virtual_server_setup .backend_1_url + "/subpath1" ,
@@ -265,6 +398,7 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
265398 ingress_controller_prerequisites ,
266399 crd_ingress_controller ,
267400 virtual_server_setup ,
401+ keycloak_setup ,
268402 test_namespace ,
269403 ):
270404 """
@@ -276,7 +410,12 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
276410 ingress_controller_prerequisites .namespace ,
277411 jwt_cm_src ,
278412 )
279- pol_name = create_policy_from_yaml (kube_apis .custom_objects , jwt_pol_valid_src , test_namespace )
413+ with open (jwt_pol_valid_src ) as f :
414+ doc = yaml .safe_load (f )
415+ pol_name = doc ["metadata" ]["name" ]
416+ doc ["spec" ]["jwt" ]["jwksURI" ] = doc ["spec" ]["jwt" ]["jwksURI" ].replace ("default" , test_namespace )
417+ kube_apis .custom_objects .create_namespaced_custom_object ("k8s.nginx.org" , "v1" , test_namespace , "policies" , doc )
418+ print (f"Policy created with name { pol_name } " )
280419 wait_before_test ()
281420
282421 print (f"Patch first vs with policy: { jwt_vs_route_subpath_src } " )
@@ -321,7 +460,7 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
321460 wait_before_test ()
322461 counter += 1
323462
324- token = get_token ( request )
463+ token = keycloak_setup . token
325464
326465 resp_1_valid_token = requests .get (
327466 virtual_server_setup .backend_1_url + "/subpath1" ,
0 commit comments