]> git.proxmox.com Git - ceph.git/blame - ceph/qa/tasks/keycloak.py
import quincy beta 17.1.0
[ceph.git] / ceph / qa / tasks / keycloak.py
CommitLineData
f67539c2
TL
1"""
2Deploy and configure Keycloak for Teuthology
3"""
4import contextlib
5import logging
6import os
7
8from teuthology import misc as teuthology
9from teuthology import contextutil
10from teuthology.orchestra import run
11from teuthology.exceptions import ConfigError
12
13log = logging.getLogger(__name__)
14
15def get_keycloak_version(config):
16 for client, client_config in config.items():
17 if 'keycloak_version' in client_config:
18 keycloak_version = client_config.get('keycloak_version')
19 return keycloak_version
20
21def get_keycloak_dir(ctx, config):
22 keycloak_version = get_keycloak_version(config)
23 current_version = 'keycloak-'+keycloak_version
24 return '{tdir}/{ver}'.format(tdir=teuthology.get_testdir(ctx),ver=current_version)
25
26def run_in_keycloak_dir(ctx, client, config, args, **kwargs):
27 return ctx.cluster.only(client).run(
28 args=[ 'cd', get_keycloak_dir(ctx,config), run.Raw('&&'), ] + args,
29 **kwargs
30 )
31
32def get_toxvenv_dir(ctx):
33 return ctx.tox.venv_path
34
35def toxvenv_sh(ctx, remote, args, **kwargs):
36 activate = get_toxvenv_dir(ctx) + '/bin/activate'
37 return remote.sh(['source', activate, run.Raw('&&')] + args, **kwargs)
38
39@contextlib.contextmanager
40def install_packages(ctx, config):
41 """
42 Downloading the two required tar files
43 1. Keycloak
44 2. Wildfly (Application Server)
45 """
46 assert isinstance(config, dict)
47 log.info('Installing packages for Keycloak...')
48
49 for (client, _) in config.items():
50 (remote,) = ctx.cluster.only(client).remotes.keys()
51 test_dir=teuthology.get_testdir(ctx)
52 current_version = get_keycloak_version(config)
53 link1 = 'https://downloads.jboss.org/keycloak/'+current_version+'/keycloak-'+current_version+'.tar.gz'
54 toxvenv_sh(ctx, remote, ['wget', link1])
55
56 file1 = 'keycloak-'+current_version+'.tar.gz'
57 toxvenv_sh(ctx, remote, ['tar', '-C', test_dir, '-xvzf', file1])
58
59 link2 ='https://downloads.jboss.org/keycloak/'+current_version+'/adapters/keycloak-oidc/keycloak-wildfly-adapter-dist-'+current_version+'.tar.gz'
60 toxvenv_sh(ctx, remote, ['cd', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), 'wget', link2])
61
62 file2 = 'keycloak-wildfly-adapter-dist-'+current_version+'.tar.gz'
63 toxvenv_sh(ctx, remote, ['tar', '-C', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config)), '-xvzf', '{tdr}/{file}'.format(tdr=get_keycloak_dir(ctx,config),file=file2)])
64
65 try:
66 yield
67 finally:
68 log.info('Removing packaged dependencies of Keycloak...')
69 for client in config:
20effc67
TL
70 current_version = get_keycloak_version(config)
71 ctx.cluster.only(client).run(
72 args=['cd', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), 'rm', '-rf', 'keycloak-wildfly-adapter-dist-' + current_version + '.tar.gz'],
73 )
74
f67539c2
TL
75 ctx.cluster.only(client).run(
76 args=['rm', '-rf', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config))],
77 )
78
20effc67
TL
79@contextlib.contextmanager
80def download_conf(ctx, config):
81 """
82 Downloads confi.py used in run_admin_cmds
83 """
84 assert isinstance(config, dict)
85 log.info('Downloading conf...')
86 testdir = teuthology.get_testdir(ctx)
87 conf_branch = 'main'
88 conf_repo = 'https://github.com/TRYTOBE8TME/scripts.git'
89 for (client, _) in config.items():
90 ctx.cluster.only(client).run(
91 args=[
92 'git', 'clone',
93 '-b', conf_branch,
94 conf_repo,
95 '{tdir}/scripts'.format(tdir=testdir),
96 ],
97 )
98 try:
99 yield
100 finally:
101 log.info('Removing conf...')
102 testdir = teuthology.get_testdir(ctx)
103 for client in config:
104 ctx.cluster.only(client).run(
105 args=[
106 'rm',
107 '-rf',
108 '{tdir}/scripts'.format(tdir=testdir),
109 ],
110 )
111
f67539c2
TL
112@contextlib.contextmanager
113def build(ctx,config):
114 """
115 Build process which needs to be done before starting a server.
116 """
117 assert isinstance(config, dict)
118 log.info('Building Keycloak...')
119 for (client,_) in config.items():
120 run_in_keycloak_dir(ctx, client, config,['cd', 'bin', run.Raw('&&'), './jboss-cli.sh', '--file=adapter-elytron-install-offline.cli'])
121 try:
122 yield
123 finally:
124 pass
125
126@contextlib.contextmanager
127def run_keycloak(ctx,config):
128 """
129 This includes two parts:
130 1. Adding a user to keycloak which is actually used to log in when we start the server and check in browser.
131 2. Starting the server.
132 """
133 assert isinstance(config, dict)
134 log.info('Bringing up Keycloak...')
135 for (client,_) in config.items():
136 (remote,) = ctx.cluster.only(client).remotes.keys()
137
138 ctx.cluster.only(client).run(
139 args=[
140 '{tdir}/bin/add-user-keycloak.sh'.format(tdir=get_keycloak_dir(ctx,config)),
141 '-r', 'master',
142 '-u', 'admin',
143 '-p', 'admin',
144 ],
145 )
146
147 toxvenv_sh(ctx, remote, ['cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), './standalone.sh', run.Raw('&'), 'exit'])
148 try:
149 yield
150 finally:
151 log.info('Stopping Keycloak Server...')
152
153 for (client, _) in config.items():
154 (remote,) = ctx.cluster.only(client).remotes.keys()
155 toxvenv_sh(ctx, remote, ['cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), './jboss-cli.sh', '--connect', 'command=:shutdown'])
156
157@contextlib.contextmanager
158def run_admin_cmds(ctx,config):
159 """
160 Running Keycloak Admin commands(kcadm commands) in order to get the token, aud value, thumbprint and realm name.
161 """
162 assert isinstance(config, dict)
163 log.info('Running admin commands...')
164 for (client,_) in config.items():
165 (remote,) = ctx.cluster.only(client).remotes.keys()
166
167 remote.run(
168 args=[
169 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
170 'config', 'credentials',
171 '--server', 'http://localhost:8080/auth',
172 '--realm', 'master',
173 '--user', 'admin',
174 '--password', 'admin',
175 '--client', 'admin-cli',
176 ],
177 )
178
179 realm_name='demorealm'
180 realm='realm={}'.format(realm_name)
181
182 remote.run(
183 args=[
184 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
185 'create', 'realms',
186 '-s', realm,
187 '-s', 'enabled=true',
188 '-s', 'accessTokenLifespan=1800',
189 '-o',
190 ],
191 )
192
193 client_name='my_client'
194 client='clientId={}'.format(client_name)
195
196 remote.run(
197 args=[
198 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
199 'create', 'clients',
200 '-r', realm_name,
201 '-s', client,
20effc67 202 '-s', 'directAccessGrantsEnabled=true',
f67539c2
TL
203 '-s', 'redirectUris=["http://localhost:8080/myapp/*"]',
204 ],
205 )
206
207 ans1= toxvenv_sh(ctx, remote,
208 [
209 'cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'),
210 './kcadm.sh', 'get', 'clients',
211 '-r', realm_name,
212 '-F', 'id,clientId', run.Raw('|'),
213 'jq', '-r', '.[] | select (.clientId == "my_client") | .id'
214 ])
215
216 pre0=ans1.rstrip()
217 pre1="clients/{}".format(pre0)
218
219 remote.run(
220 args=[
221 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
222 'update', pre1,
223 '-r', realm_name,
224 '-s', 'enabled=true',
225 '-s', 'serviceAccountsEnabled=true',
226 '-s', 'redirectUris=["http://localhost:8080/myapp/*"]',
227 ],
228 )
229
230 ans2= pre1+'/client-secret'
231
232 out2= toxvenv_sh(ctx, remote,
233 [
234 'cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'),
235 './kcadm.sh', 'get', ans2,
236 '-r', realm_name,
237 '-F', 'value'
238 ])
239
240 ans0= '{client}:{secret}'.format(client=client_name,secret=out2[15:51])
241 ans3= 'client_secret={}'.format(out2[15:51])
242 clientid='client_id={}'.format(client_name)
243
20effc67
TL
244 proto_map = pre1+"/protocol-mappers/models"
245 uname = "username=testuser"
246 upass = "password=testuser"
247
248 remote.run(
249 args=[
250 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
251 'create', 'users',
252 '-s', uname,
253 '-s', 'enabled=true',
254 '-s', 'attributes.\"https://aws.amazon.com/tags\"=\"{"principal_tags":{"Department":["Engineering", "Marketing"]}}\"',
255 '-r', realm_name,
256 ],
257 )
258
259 sample = 'testuser'
260
261 remote.run(
262 args=[
263 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
264 'set-password',
265 '-r', realm_name,
266 '--username', sample,
267 '--new-password', sample,
268 ],
269 )
270
271 file_path = '{tdir}/scripts/confi.py'.format(tdir=teuthology.get_testdir(ctx))
272
273 remote.run(
274 args=[
275 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
276 'create', proto_map,
277 '-r', realm_name,
278 '-f', file_path,
279 ],
280 )
281
282 remote.run(
283 args=[
284 '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
285 'config', 'credentials',
286 '--server', 'http://localhost:8080/auth',
287 '--realm', realm_name,
288 '--user', sample,
289 '--password', sample,
290 '--client', 'admin-cli',
291 ],
292 )
293
294 out9= toxvenv_sh(ctx, remote,
295 [
296 'curl', '-k', '-v',
297 '-X', 'POST',
298 '-H', 'Content-Type:application/x-www-form-urlencoded',
299 '-d', 'scope=openid',
300 '-d', 'grant_type=password',
301 '-d', clientid,
302 '-d', ans3,
303 '-d', uname,
304 '-d', upass,
305 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token', run.Raw('|'),
306 'jq', '-r', '.access_token'
307 ])
308
309 user_token_pre = out9.rstrip()
310 user_token = '{}'.format(user_token_pre)
311
f67539c2
TL
312 out3= toxvenv_sh(ctx, remote,
313 [
314 'curl', '-k', '-v',
315 '-X', 'POST',
316 '-H', 'Content-Type:application/x-www-form-urlencoded',
317 '-d', 'scope=openid',
318 '-d', 'grant_type=client_credentials',
319 '-d', clientid,
320 '-d', ans3,
321 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token', run.Raw('|'),
322 'jq', '-r', '.access_token'
323 ])
324
325 pre2=out3.rstrip()
326 acc_token= 'token={}'.format(pre2)
327 ans4= '{}'.format(pre2)
328
329 out4= toxvenv_sh(ctx, remote,
330 [
331 'curl', '-k', '-v',
332 '-X', 'GET',
333 '-H', 'Content-Type:application/x-www-form-urlencoded',
334 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/certs', run.Raw('|'),
335 'jq', '-r', '.keys[].x5c[]'
336 ])
337
338 pre3=out4.rstrip()
339 cert_value='{}'.format(pre3)
340 start_value= "-----BEGIN CERTIFICATE-----\n"
341 end_value= "\n-----END CERTIFICATE-----"
342 user_data=""
343 user_data+=start_value
344 user_data+=cert_value
345 user_data+=end_value
346
347 remote.write_file(
348 path='{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
349 data=user_data
350 )
351
352 out5= toxvenv_sh(ctx, remote,
353 [
354 'openssl', 'x509',
355 '-in', '{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
356 '--fingerprint', '--noout', '-sha1'
357 ])
358
359 pre_ans= '{}'.format(out5[17:76])
360 ans5=""
361
362 for character in pre_ans:
363 if(character!=':'):
364 ans5+=character
365
20effc67
TL
366 str1 = 'curl'
367 str2 = '-k'
368 str3 = '-v'
369 str4 = '-X'
370 str5 = 'POST'
371 str6 = '-u'
372 str7 = '-d'
373 str8 = 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token/introspect'
374
375 out6= toxvenv_sh(ctx, remote,
f67539c2 376 [
20effc67
TL
377 str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.aud'
378 ])
379
380 out7= toxvenv_sh(ctx, remote,
381 [
382 str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.sub'
383 ])
384
385 out8= toxvenv_sh(ctx, remote,
386 [
387 str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.azp'
f67539c2
TL
388 ])
389
390 ans6=out6.rstrip()
20effc67
TL
391 ans7=out7.rstrip()
392 ans8=out8.rstrip()
f67539c2
TL
393
394 os.environ['TOKEN']=ans4
395 os.environ['THUMBPRINT']=ans5
396 os.environ['AUD']=ans6
20effc67
TL
397 os.environ['SUB']=ans7
398 os.environ['AZP']=ans8
399 os.environ['USER_TOKEN']=user_token
f67539c2
TL
400 os.environ['KC_REALM']=realm_name
401
402 try:
403 yield
404 finally:
405 log.info('Removing certificate.crt file...')
406 for (client,_) in config.items():
407 (remote,) = ctx.cluster.only(client).remotes.keys()
408 remote.run(
409 args=['rm', '-f',
410 '{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
411 ],
412 )
413
20effc67
TL
414 remote.run(
415 args=['rm', '-f',
416 '{tdir}/confi.py'.format(tdir=teuthology.get_testdir(ctx)),
417 ],
418 )
419
f67539c2
TL
420@contextlib.contextmanager
421def task(ctx,config):
422 """
423 To run keycloak the prerequisite is to run the tox task. Following is the way how to run
424 tox and then keycloak::
425
426 tasks:
427 - tox: [ client.0 ]
428 - keycloak:
429 client.0:
430 keycloak_version: 11.0.0
431
432 To pass extra arguments to nose (e.g. to run a certain test)::
433
434 tasks:
435 - tox: [ client.0 ]
436 - keycloak:
437 client.0:
438 keycloak_version: 11.0.0
439 - s3tests:
440 client.0:
441 extra_attrs: ['webidentity_test']
442
443 """
444 assert config is None or isinstance(config, list) \
445 or isinstance(config, dict), \
446 "task keycloak only supports a list or dictionary for configuration"
447
448 if not hasattr(ctx, 'tox'):
449 raise ConfigError('keycloak must run after the tox task')
450
451 all_clients = ['client.{id}'.format(id=id_)
452 for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
453 if config is None:
454 config = all_clients
455 if isinstance(config, list):
456 config = dict.fromkeys(config)
457
458 log.debug('Keycloak config is %s', config)
459
460 with contextutil.nested(
461 lambda: install_packages(ctx=ctx, config=config),
462 lambda: build(ctx=ctx, config=config),
463 lambda: run_keycloak(ctx=ctx, config=config),
20effc67 464 lambda: download_conf(ctx=ctx, config=config),
f67539c2
TL
465 lambda: run_admin_cmds(ctx=ctx, config=config),
466 ):
467 yield
468