1
+ import secrets
1
2
from unittest import mock
2
3
3
4
import pytest
4
5
import requests
6
+ import yaml
5
7
from 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
+ )
8
18
from suite .utils .vs_vsr_resources_utils import (
9
19
create_virtual_server_from_yaml ,
10
20
delete_and_create_vs_from_yaml ,
11
21
delete_virtual_server ,
12
22
)
13
23
24
+ username = "nginx-user-" + secrets .token_hex (4 )
25
+ password = secrets .token_hex (8 )
26
+ realm_name = "jwks-example"
14
27
std_vs_src = f"{ TEST_DATA } /virtual-server/standard/virtual-server.yaml"
15
28
jwt_pol_valid_src = f"{ TEST_DATA } /jwt-policy-jwksuri/policies/jwt-policy-valid.yaml"
16
29
jwt_pol_invalid_src = f"{ TEST_DATA } /jwt-policy-jwksuri/policies/jwt-policy-invalid.yaml"
27
40
f"{ TEST_DATA } /jwt-policy-jwksuri/virtual-server/virtual-server-invalid-policy-route-subpath.yaml"
28
41
)
29
42
jwt_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 "
32
45
33
46
34
- def get_token ( request ) :
47
+ class KeycloakSetup :
35
48
"""
36
- get jwt token from azure ad endpoint
49
+ Attributes:
50
+ token (str):
37
51
"""
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
+
38
139
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 ,
43
146
}
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" },
46
151
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 ,
49
153
)
50
154
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 )
54
170
55
171
56
172
@pytest .mark .skip_for_nginx_oss
57
- @pytest .mark .skip (reason = "issues with IdP communication" )
58
173
@pytest .mark .policies
59
174
@pytest .mark .parametrize (
60
175
"crd_ingress_controller, virtual_server_setup" ,
@@ -85,6 +200,7 @@ def test_jwt_policy_jwksuri(
85
200
crd_ingress_controller ,
86
201
virtual_server_setup ,
87
202
test_namespace ,
203
+ keycloak_setup ,
88
204
jwt_virtual_server ,
89
205
):
90
206
"""
@@ -96,7 +212,12 @@ def test_jwt_policy_jwksuri(
96
212
ingress_controller_prerequisites .namespace ,
97
213
jwt_cm_src ,
98
214
)
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 } " )
100
221
wait_before_test ()
101
222
102
223
print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -106,6 +227,7 @@ def test_jwt_policy_jwksuri(
106
227
jwt_virtual_server ,
107
228
virtual_server_setup .namespace ,
108
229
)
230
+ wait_before_test ()
109
231
resp_no_token = mock .Mock ()
110
232
resp_no_token .status_code == 502
111
233
counter = 0
@@ -118,14 +240,13 @@ def test_jwt_policy_jwksuri(
118
240
wait_before_test ()
119
241
counter += 1
120
242
121
- token = get_token ( request )
243
+ token = keycloak_setup . token
122
244
123
245
resp_valid_token = requests .get (
124
246
virtual_server_setup .backend_1_url ,
125
247
headers = {"host" : virtual_server_setup .vs_host , "token" : token },
126
248
timeout = 5 ,
127
249
)
128
-
129
250
delete_policy (kube_apis .custom_objects , pol_name , test_namespace )
130
251
wait_before_test ()
131
252
@@ -148,6 +269,7 @@ def test_jwt_invalid_policy_jwksuri(
148
269
crd_ingress_controller ,
149
270
virtual_server_setup ,
150
271
test_namespace ,
272
+ keycloak_setup ,
151
273
jwt_virtual_server ,
152
274
):
153
275
"""
@@ -159,7 +281,12 @@ def test_jwt_invalid_policy_jwksuri(
159
281
ingress_controller_prerequisites .namespace ,
160
282
jwt_cm_src ,
161
283
)
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 } " )
163
290
wait_before_test ()
164
291
165
292
print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -176,7 +303,7 @@ def test_jwt_invalid_policy_jwksuri(
176
303
headers = {"host" : virtual_server_setup .vs_host },
177
304
)
178
305
179
- token = get_token ( request )
306
+ token = keycloak_setup . token
180
307
181
308
resp2 = requests .get (
182
309
virtual_server_setup .backend_1_url ,
@@ -204,6 +331,7 @@ def test_jwt_policy_subroute_jwksuri(
204
331
crd_ingress_controller ,
205
332
virtual_server_setup ,
206
333
test_namespace ,
334
+ keycloak_setup ,
207
335
jwt_virtual_server ,
208
336
):
209
337
"""
@@ -215,7 +343,12 @@ def test_jwt_policy_subroute_jwksuri(
215
343
ingress_controller_prerequisites .namespace ,
216
344
jwt_cm_src ,
217
345
)
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 } " )
219
352
wait_before_test ()
220
353
221
354
print (f"Patch vs with policy: { jwt_virtual_server } " )
@@ -237,7 +370,7 @@ def test_jwt_policy_subroute_jwksuri(
237
370
wait_before_test ()
238
371
counter += 1
239
372
240
- token = get_token ( request )
373
+ token = keycloak_setup . token
241
374
242
375
resp_valid_token = requests .get (
243
376
virtual_server_setup .backend_1_url + "/subpath1" ,
@@ -265,6 +398,7 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
265
398
ingress_controller_prerequisites ,
266
399
crd_ingress_controller ,
267
400
virtual_server_setup ,
401
+ keycloak_setup ,
268
402
test_namespace ,
269
403
):
270
404
"""
@@ -276,7 +410,12 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
276
410
ingress_controller_prerequisites .namespace ,
277
411
jwt_cm_src ,
278
412
)
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 } " )
280
419
wait_before_test ()
281
420
282
421
print (f"Patch first vs with policy: { jwt_vs_route_subpath_src } " )
@@ -321,7 +460,7 @@ def test_jwt_policy_subroute_jwksuri_multiple_vs(
321
460
wait_before_test ()
322
461
counter += 1
323
462
324
- token = get_token ( request )
463
+ token = keycloak_setup . token
325
464
326
465
resp_1_valid_token = requests .get (
327
466
virtual_server_setup .backend_1_url + "/subpath1" ,
0 commit comments