summaryrefslogtreecommitdiffstats
path: root/qa/tasks/keycloak.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--qa/tasks/keycloak.py468
1 files changed, 468 insertions, 0 deletions
diff --git a/qa/tasks/keycloak.py b/qa/tasks/keycloak.py
new file mode 100644
index 000000000..1d89a27a5
--- /dev/null
+++ b/qa/tasks/keycloak.py
@@ -0,0 +1,468 @@
+"""
+Deploy and configure Keycloak for Teuthology
+"""
+import contextlib
+import logging
+import os
+
+from teuthology import misc as teuthology
+from teuthology import contextutil
+from teuthology.orchestra import run
+from teuthology.exceptions import ConfigError
+
+log = logging.getLogger(__name__)
+
+def get_keycloak_version(config):
+ for client, client_config in config.items():
+ if 'keycloak_version' in client_config:
+ keycloak_version = client_config.get('keycloak_version')
+ return keycloak_version
+
+def get_keycloak_dir(ctx, config):
+ keycloak_version = get_keycloak_version(config)
+ current_version = 'keycloak-'+keycloak_version
+ return '{tdir}/{ver}'.format(tdir=teuthology.get_testdir(ctx),ver=current_version)
+
+def run_in_keycloak_dir(ctx, client, config, args, **kwargs):
+ return ctx.cluster.only(client).run(
+ args=[ 'cd', get_keycloak_dir(ctx,config), run.Raw('&&'), ] + args,
+ **kwargs
+ )
+
+def get_toxvenv_dir(ctx):
+ return ctx.tox.venv_path
+
+def toxvenv_sh(ctx, remote, args, **kwargs):
+ activate = get_toxvenv_dir(ctx) + '/bin/activate'
+ return remote.sh(['source', activate, run.Raw('&&')] + args, **kwargs)
+
+@contextlib.contextmanager
+def install_packages(ctx, config):
+ """
+ Downloading the two required tar files
+ 1. Keycloak
+ 2. Wildfly (Application Server)
+ """
+ assert isinstance(config, dict)
+ log.info('Installing packages for Keycloak...')
+
+ for (client, _) in config.items():
+ (remote,) = ctx.cluster.only(client).remotes.keys()
+ test_dir=teuthology.get_testdir(ctx)
+ current_version = get_keycloak_version(config)
+ link1 = 'https://downloads.jboss.org/keycloak/'+current_version+'/keycloak-'+current_version+'.tar.gz'
+ toxvenv_sh(ctx, remote, ['wget', link1])
+
+ file1 = 'keycloak-'+current_version+'.tar.gz'
+ toxvenv_sh(ctx, remote, ['tar', '-C', test_dir, '-xvzf', file1])
+
+ link2 ='https://downloads.jboss.org/keycloak/'+current_version+'/adapters/keycloak-oidc/keycloak-wildfly-adapter-dist-'+current_version+'.tar.gz'
+ toxvenv_sh(ctx, remote, ['cd', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), 'wget', link2])
+
+ file2 = 'keycloak-wildfly-adapter-dist-'+current_version+'.tar.gz'
+ 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)])
+
+ try:
+ yield
+ finally:
+ log.info('Removing packaged dependencies of Keycloak...')
+ for client in config:
+ current_version = get_keycloak_version(config)
+ ctx.cluster.only(client).run(
+ args=['cd', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), 'rm', '-rf', 'keycloak-wildfly-adapter-dist-' + current_version + '.tar.gz'],
+ )
+
+ ctx.cluster.only(client).run(
+ args=['rm', '-rf', '{tdir}'.format(tdir=get_keycloak_dir(ctx,config))],
+ )
+
+@contextlib.contextmanager
+def download_conf(ctx, config):
+ """
+ Downloads confi.py used in run_admin_cmds
+ """
+ assert isinstance(config, dict)
+ log.info('Downloading conf...')
+ testdir = teuthology.get_testdir(ctx)
+ conf_branch = 'main'
+ conf_repo = 'https://github.com/TRYTOBE8TME/scripts.git'
+ for (client, _) in config.items():
+ ctx.cluster.only(client).run(
+ args=[
+ 'git', 'clone',
+ '-b', conf_branch,
+ conf_repo,
+ '{tdir}/scripts'.format(tdir=testdir),
+ ],
+ )
+ try:
+ yield
+ finally:
+ log.info('Removing conf...')
+ testdir = teuthology.get_testdir(ctx)
+ for client in config:
+ ctx.cluster.only(client).run(
+ args=[
+ 'rm',
+ '-rf',
+ '{tdir}/scripts'.format(tdir=testdir),
+ ],
+ )
+
+@contextlib.contextmanager
+def build(ctx,config):
+ """
+ Build process which needs to be done before starting a server.
+ """
+ assert isinstance(config, dict)
+ log.info('Building Keycloak...')
+ for (client,_) in config.items():
+ run_in_keycloak_dir(ctx, client, config,['cd', 'bin', run.Raw('&&'), './jboss-cli.sh', '--file=adapter-elytron-install-offline.cli'])
+ try:
+ yield
+ finally:
+ pass
+
+@contextlib.contextmanager
+def run_keycloak(ctx,config):
+ """
+ This includes two parts:
+ 1. Adding a user to keycloak which is actually used to log in when we start the server and check in browser.
+ 2. Starting the server.
+ """
+ assert isinstance(config, dict)
+ log.info('Bringing up Keycloak...')
+ for (client,_) in config.items():
+ (remote,) = ctx.cluster.only(client).remotes.keys()
+
+ ctx.cluster.only(client).run(
+ args=[
+ '{tdir}/bin/add-user-keycloak.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ '-r', 'master',
+ '-u', 'admin',
+ '-p', 'admin',
+ ],
+ )
+
+ toxvenv_sh(ctx, remote, ['cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), './standalone.sh', run.Raw('&'), 'exit'])
+ try:
+ yield
+ finally:
+ log.info('Stopping Keycloak Server...')
+
+ for (client, _) in config.items():
+ (remote,) = ctx.cluster.only(client).remotes.keys()
+ toxvenv_sh(ctx, remote, ['cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'), './jboss-cli.sh', '--connect', 'command=:shutdown'])
+
+@contextlib.contextmanager
+def run_admin_cmds(ctx,config):
+ """
+ Running Keycloak Admin commands(kcadm commands) in order to get the token, aud value, thumbprint and realm name.
+ """
+ assert isinstance(config, dict)
+ log.info('Running admin commands...')
+ for (client,_) in config.items():
+ (remote,) = ctx.cluster.only(client).remotes.keys()
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'config', 'credentials',
+ '--server', 'http://localhost:8080/auth',
+ '--realm', 'master',
+ '--user', 'admin',
+ '--password', 'admin',
+ '--client', 'admin-cli',
+ ],
+ )
+
+ realm_name='demorealm'
+ realm='realm={}'.format(realm_name)
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'create', 'realms',
+ '-s', realm,
+ '-s', 'enabled=true',
+ '-s', 'accessTokenLifespan=1800',
+ '-o',
+ ],
+ )
+
+ client_name='my_client'
+ client='clientId={}'.format(client_name)
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'create', 'clients',
+ '-r', realm_name,
+ '-s', client,
+ '-s', 'directAccessGrantsEnabled=true',
+ '-s', 'redirectUris=["http://localhost:8080/myapp/*"]',
+ ],
+ )
+
+ ans1= toxvenv_sh(ctx, remote,
+ [
+ 'cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'),
+ './kcadm.sh', 'get', 'clients',
+ '-r', realm_name,
+ '-F', 'id,clientId', run.Raw('|'),
+ 'jq', '-r', '.[] | select (.clientId == "my_client") | .id'
+ ])
+
+ pre0=ans1.rstrip()
+ pre1="clients/{}".format(pre0)
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'update', pre1,
+ '-r', realm_name,
+ '-s', 'enabled=true',
+ '-s', 'serviceAccountsEnabled=true',
+ '-s', 'redirectUris=["http://localhost:8080/myapp/*"]',
+ ],
+ )
+
+ ans2= pre1+'/client-secret'
+
+ out2= toxvenv_sh(ctx, remote,
+ [
+ 'cd', '{tdir}/bin'.format(tdir=get_keycloak_dir(ctx,config)), run.Raw('&&'),
+ './kcadm.sh', 'get', ans2,
+ '-r', realm_name,
+ '-F', 'value'
+ ])
+
+ ans0= '{client}:{secret}'.format(client=client_name,secret=out2[15:51])
+ ans3= 'client_secret={}'.format(out2[15:51])
+ clientid='client_id={}'.format(client_name)
+
+ proto_map = pre1+"/protocol-mappers/models"
+ uname = "username=testuser"
+ upass = "password=testuser"
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'create', 'users',
+ '-s', uname,
+ '-s', 'enabled=true',
+ '-s', 'attributes.\"https://aws.amazon.com/tags\"=\"{"principal_tags":{"Department":["Engineering", "Marketing"]}}\"',
+ '-r', realm_name,
+ ],
+ )
+
+ sample = 'testuser'
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'set-password',
+ '-r', realm_name,
+ '--username', sample,
+ '--new-password', sample,
+ ],
+ )
+
+ file_path = '{tdir}/scripts/confi.py'.format(tdir=teuthology.get_testdir(ctx))
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'create', proto_map,
+ '-r', realm_name,
+ '-f', file_path,
+ ],
+ )
+
+ remote.run(
+ args=[
+ '{tdir}/bin/kcadm.sh'.format(tdir=get_keycloak_dir(ctx,config)),
+ 'config', 'credentials',
+ '--server', 'http://localhost:8080/auth',
+ '--realm', realm_name,
+ '--user', sample,
+ '--password', sample,
+ '--client', 'admin-cli',
+ ],
+ )
+
+ out9= toxvenv_sh(ctx, remote,
+ [
+ 'curl', '-k', '-v',
+ '-X', 'POST',
+ '-H', 'Content-Type:application/x-www-form-urlencoded',
+ '-d', 'scope=openid',
+ '-d', 'grant_type=password',
+ '-d', clientid,
+ '-d', ans3,
+ '-d', uname,
+ '-d', upass,
+ 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token', run.Raw('|'),
+ 'jq', '-r', '.access_token'
+ ])
+
+ user_token_pre = out9.rstrip()
+ user_token = '{}'.format(user_token_pre)
+
+ out3= toxvenv_sh(ctx, remote,
+ [
+ 'curl', '-k', '-v',
+ '-X', 'POST',
+ '-H', 'Content-Type:application/x-www-form-urlencoded',
+ '-d', 'scope=openid',
+ '-d', 'grant_type=client_credentials',
+ '-d', clientid,
+ '-d', ans3,
+ 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token', run.Raw('|'),
+ 'jq', '-r', '.access_token'
+ ])
+
+ pre2=out3.rstrip()
+ acc_token= 'token={}'.format(pre2)
+ ans4= '{}'.format(pre2)
+
+ out4= toxvenv_sh(ctx, remote,
+ [
+ 'curl', '-k', '-v',
+ '-X', 'GET',
+ '-H', 'Content-Type:application/x-www-form-urlencoded',
+ 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/certs', run.Raw('|'),
+ 'jq', '-r', '.keys[].x5c[]'
+ ])
+
+ pre3=out4.rstrip()
+ cert_value='{}'.format(pre3)
+ start_value= "-----BEGIN CERTIFICATE-----\n"
+ end_value= "\n-----END CERTIFICATE-----"
+ user_data=""
+ user_data+=start_value
+ user_data+=cert_value
+ user_data+=end_value
+
+ remote.write_file(
+ path='{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
+ data=user_data
+ )
+
+ out5= toxvenv_sh(ctx, remote,
+ [
+ 'openssl', 'x509',
+ '-in', '{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
+ '--fingerprint', '--noout', '-sha1'
+ ])
+
+ pre_ans= '{}'.format(out5[17:76])
+ ans5=""
+
+ for character in pre_ans:
+ if(character!=':'):
+ ans5+=character
+
+ str1 = 'curl'
+ str2 = '-k'
+ str3 = '-v'
+ str4 = '-X'
+ str5 = 'POST'
+ str6 = '-u'
+ str7 = '-d'
+ str8 = 'http://localhost:8080/auth/realms/'+realm_name+'/protocol/openid-connect/token/introspect'
+
+ out6= toxvenv_sh(ctx, remote,
+ [
+ str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.aud'
+ ])
+
+ out7= toxvenv_sh(ctx, remote,
+ [
+ str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.sub'
+ ])
+
+ out8= toxvenv_sh(ctx, remote,
+ [
+ str1, str2, str3, str4, str5, str6, ans0, str7, acc_token, str8, run.Raw('|'), 'jq', '-r', '.azp'
+ ])
+
+ ans6=out6.rstrip()
+ ans7=out7.rstrip()
+ ans8=out8.rstrip()
+
+ os.environ['TOKEN']=ans4
+ os.environ['THUMBPRINT']=ans5
+ os.environ['AUD']=ans6
+ os.environ['SUB']=ans7
+ os.environ['AZP']=ans8
+ os.environ['USER_TOKEN']=user_token
+ os.environ['KC_REALM']=realm_name
+
+ try:
+ yield
+ finally:
+ log.info('Removing certificate.crt file...')
+ for (client,_) in config.items():
+ (remote,) = ctx.cluster.only(client).remotes.keys()
+ remote.run(
+ args=['rm', '-f',
+ '{tdir}/bin/certificate.crt'.format(tdir=get_keycloak_dir(ctx,config)),
+ ],
+ )
+
+ remote.run(
+ args=['rm', '-f',
+ '{tdir}/confi.py'.format(tdir=teuthology.get_testdir(ctx)),
+ ],
+ )
+
+@contextlib.contextmanager
+def task(ctx,config):
+ """
+ To run keycloak the prerequisite is to run the tox task. Following is the way how to run
+ tox and then keycloak::
+
+ tasks:
+ - tox: [ client.0 ]
+ - keycloak:
+ client.0:
+ keycloak_version: 11.0.0
+
+ To pass extra arguments to nose (e.g. to run a certain test)::
+
+ tasks:
+ - tox: [ client.0 ]
+ - keycloak:
+ client.0:
+ keycloak_version: 11.0.0
+ - s3tests:
+ client.0:
+ extra_attrs: ['webidentity_test']
+
+ """
+ assert config is None or isinstance(config, list) \
+ or isinstance(config, dict), \
+ "task keycloak only supports a list or dictionary for configuration"
+
+ if not hasattr(ctx, 'tox'):
+ raise ConfigError('keycloak must run after the tox task')
+
+ all_clients = ['client.{id}'.format(id=id_)
+ for id_ in teuthology.all_roles_of_type(ctx.cluster, 'client')]
+ if config is None:
+ config = all_clients
+ if isinstance(config, list):
+ config = dict.fromkeys(config)
+
+ log.debug('Keycloak config is %s', config)
+
+ with contextutil.nested(
+ lambda: install_packages(ctx=ctx, config=config),
+ lambda: build(ctx=ctx, config=config),
+ lambda: run_keycloak(ctx=ctx, config=config),
+ lambda: download_conf(ctx=ctx, config=config),
+ lambda: run_admin_cmds(ctx=ctx, config=config),
+ ):
+ yield
+