From 26aa7f9aff64dca9f7ccceaff27c9d4d4b7323bc Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Thu, 14 Sep 2023 17:35:18 +0900 Subject: [PATCH 01/10] add WF to install tks-admin-tools --- .../tks-install-admin-tools.yaml | 76 +++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 tks-admin-cluster/tks-install-admin-tools.yaml diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml new file mode 100644 index 00000000..514cb345 --- /dev/null +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -0,0 +1,76 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: tks-install-admin-tools + namespace: argo +spec: + entrypoint: main + arguments: + parameters: + - name: manifest_repo_url + value: "" + - name: revision + value: main + # This param is also used as default target cluster on Argo CD + - name: site_name + value: "" + - name: app_prefix + value: "tks-admin" + - name: install-nginx + value: false + volumes: + - name: config + secret: + secretName: tks-admin-kubeconfig-secret + templates: + - name: main + steps: + - - name: install-nginx-controller + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + ## If value-override for admin-cluster is different from that of user-cluster, + ## then the ingress-nginx should be copied to tks-admin-tools group. + { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" }, + ] + when: "{{workflow.parameters.install-nginx}} == true" + + - - name: install-keycloak + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + { "app_group": "tks-admin-tools", "path": "keycloak", "namespace": "keycloak", "target_cluster": "" } + ] + + - - name: configure-keycloak + + + + + + - - name: install-tks-api-and-harbor + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + { "app_group": "tks-admin-tools", "path": "tks-api", "namespace": "tks", "target_cluster": "" }, + { "app_group": "tks-admin-tools", "path": "harbor", "namespace": "harbor", "target_cluster": "" } + ] + + + + From 47bef8fb2945ed4565f5bbc6ff663cceb6aea718 Mon Sep 17 00:00:00 2001 From: donggyu Date: Wed, 20 Sep 2023 12:36:42 +0900 Subject: [PATCH 02/10] feature: add keycloak client workflows --- keycloak-client/lib/keycloak-clients.yaml | 723 ++++++++++++++++++ keycloak-client/lib/keycloak-realms.yaml | 137 ++++ keycloak-client/lib/keycloak-users.yaml | 353 +++++++++ keycloak-client/set-admin-cluster.yaml | 106 +++ .../tks-install-admin-tools.yaml | 23 +- 5 files changed, 1338 insertions(+), 4 deletions(-) create mode 100644 keycloak-client/lib/keycloak-clients.yaml create mode 100644 keycloak-client/lib/keycloak-realms.yaml create mode 100644 keycloak-client/lib/keycloak-users.yaml create mode 100644 keycloak-client/set-admin-cluster.yaml diff --git a/keycloak-client/lib/keycloak-clients.yaml b/keycloak-client/lib/keycloak-clients.yaml new file mode 100644 index 00000000..4f300438 --- /dev/null +++ b/keycloak-client/lib/keycloak-clients.yaml @@ -0,0 +1,723 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: keycloak-client + namespace: argo +spec: + templates: + - name: delete-client + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: test-client + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenID, KeycloakAdmin, KeycloakOpenIDConnection + import requests + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def delete_client(url, realm_name, client_id, token): + if (url[(- 1)] == '/'): + url = url[:(- 1)] + path = f'/admin/realms/{realm_name}/clients/{client_id}' + headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer ' + token['access_token'])} + response = requests.delete((url + path), headers=headers) + if (response.status_code == 204): + print(f'delete client {client_id} success') + elif (response.status_code == 404): + raise Exception(response.text) + else: + raise Exception(response.text) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception('get client failed') + try: + delete_client(url=input_params['server_url'], realm_name=input_params['target_realm_name'], client_id=hashed_client_id, token=keycloak_admin.connection.token) + except Exception as inner_e: + print(inner_e) + raise Exception('delete client on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""delete client "{input_params['target_client_id']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: create-client-role + inputs: + parameters: + - name: server_url + value: https://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: test-client2 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/dev_kubeconfig/kubeconfig') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + role_name = input_params['client_role_name'] + keycloak_admin.create_client_role(client_role_id=hashed_client_id, payload={'name': role_name, 'clientRole': True}) + print(f"""create client role "{role_name}" in client "{input_params['target_client_id']}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception('create client role on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""create client role {role_name} in client "{input_params['target_client_id']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: append-client-redirect-uri + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc7 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: redirect_uri + value: aaaa + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenID, KeycloakAdmin, KeycloakOpenIDConnection + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'redirect_uri': '{{inputs.parameters.redirect_uri}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + client = keycloak_admin.get_client(client_id=hashed_client_id) + existing_redirect_uris = client['redirectUris'] + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + redirect_uri = input_params['redirect_uri'] + if (redirect_uri in existing_redirect_uris): + print(f""""{redirect_uri}" already exists in client "{input_params['target_client_id']}".""") + else: + existing_redirect_uris.append(redirect_uri) + client['redirectUris'] = existing_redirect_uris + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + print(f"""append "{redirect_uri}" in client "{input_params['target_client_id']}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception('update client on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""append redirect uri "{input_params['redirect_uri']}" to client "{input_params['target_client_id']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: remove-client-redirect-uri + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc7 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: redirect_uri + value: aaaa + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenID, KeycloakAdmin, KeycloakOpenIDConnection + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'redirect_uri': '{{inputs.parameters.redirect_uri}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + client = keycloak_admin.get_client(client_id=hashed_client_id) + existing_redirect_uris = client['redirectUris'] + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + if (input_params['redirect_uri'] not in existing_redirect_uris): + print(f"""redirect-uri "{input_params['redirect_uri']}" not exist in client "{hashed_client_id}".""") + else: + existing_redirect_uris.remove(input_params['redirect_uri']) + client['redirectUris'] = existing_redirect_uris + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + print(f"""remove redirect-uri "{input_params['redirect_uri']}" in client "{hashed_client_id}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception(f'remove redirect-uri in client {hashed_client_id} on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""remove redirect uri "{input_params['redirect_uri']}" to client "{input_params['target_client_id']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: create-client-scope-mapper-client-role + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc6 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: mapper_name + value: k8s-role-mapper + - name: claim_name + value: groups + - name: add_to_access_token + value: false + - name: add_to_id_token + value: true + - name: add_to_userinfo + value: false + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + import requests + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'mapper_name': '{{inputs.parameters.mapper_name}}', 'claim_name': '{{inputs.parameters.claim_name}}', 'add_to_access_token': '{{inputs.parameters.add_to_access_token}}', 'add_to_id_token': '{{inputs.parameters.add_to_id_token}}', 'add_to_userinfo': '{{inputs.parameters.add_to_userinfo}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def create_client_scope_mapper(url, realm_name, client_id, hashed_client_id, token, mapper_name): + if (url[(- 1)] == '/'): + url = url[:(- 1)] + path = f'/admin/realms/{realm_name}/clients/{hashed_client_id}/protocol-mappers/models' + headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer ' + token['access_token'])} + data = {'name': mapper_name, 'protocol': 'openid-connect', 'protocolMapper': 'oidc-usermodel-client-role-mapper', 'config': {'usermodel.clientRoleMapping.clientId': client_id, 'claim.name': input_params['claim_name'], 'access.token.claim': input_params['add_to_access_token'], 'id.token.claim': input_params['add_to_id_token'], 'userinfo.token.claim': input_params['add_to_userinfo'], 'multivalued': 'true', 'jsonType.label': 'String'}} + response = requests.post((url + path), headers=headers, json=data) + if (response.status_code == 201): + print(f'create client scope mapper {client_id} success') + elif (response.status_code == 409): + raise Exception(response.text) + else: + raise Exception(response.text) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + create_client_scope_mapper(url=input_params['server_url'], realm_name=input_params['target_realm_name'], client_id=input_params['target_client_id'], hashed_client_id=hashed_client_id, token=keycloak_admin.connection.token, mapper_name=input_params['mapper_name']) + print(f"""create client scope mapper "{input_params['mapper_name']}" in client "{input_params['target_client_id']} success""") + except Exception as inner_e: + print(inner_e) + raise Exception('create client role on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""create client scope mapper "{input_params['mapper_name']}" in client "{input_params['target_client_id']} failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: delete-client-role + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc6 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + role_name = input_params['client_role_name'] + keycloak_admin.delete_client_role(client_role_id=hashed_client_id, role_name=role_name) + print(f"""create client role {role_name} in client "{input_params['target_client_id']}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception('create client role on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""create client role {role_name} in client "{input_params['target_client_id']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: create-client + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: test-client2 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenID + import requests + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def create_client(url, realm_name, client_id, token): + if (url[(- 1)] == '/'): + url = url[:(- 1)] + path = f'/admin/realms/{realm_name}/clients' + headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer ' + token['access_token'])} + data = {'clientId': client_id, 'enabled': True, 'publicClient': True, 'protocol': 'openid-connect', 'standardFlowEnabled': True, 'implicitFlowEnabled': False, 'directAccessGrantsEnabled': True, 'serviceAccountsEnabled': False, 'authorizationServicesEnabled': False, 'fullScopeAllowed': True} + response = requests.post((url + path), headers=headers, json=data) + if (response.status_code == 201): + print(f'create client {client_id} success') + elif (response.status_code == 409): + raise Exception(response.text) + else: + raise Exception(response.text) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + token = keycloak_openid.token(grant_type='password', username='admin', password=secret) + try: + create_client(input_params['server_url'], input_params['target_realm_name'], input_params['target_client_id'], token) + print(f"""create client "{input_params['target_client_id']}" success""") + keycloak_openid.logout(token['refresh_token']) + except Exception as e: + print(e) + print('create client failed') + keycloak_openid.logout(token['refresh_token']) + sys.exit(1) + - name: update-client-secret + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: test-client2 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + - name: client_secret_enabled + value: 'false' + - name: client_secret_value + value: test + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenID, KeycloakAdmin, KeycloakOpenIDConnection + import requests + from kubernetes import client, config + import sys + import base64 + import json + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'client_secret_enabled': '{{inputs.parameters.client_secret_enabled}}', 'client_secret_value': '{{inputs.parameters.client_secret_value}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def create_client(url, realm_name, client_id, token): + if (url[(- 1)] == '/'): + url = url[:(- 1)] + path = f'/admin/realms/{realm_name}/clients' + headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer ' + token['access_token'])} + data = {'clientId': client_id, 'enabled': True, 'publicClient': True, 'protocol': 'openid-connect', 'standardFlowEnabled': True, 'implicitFlowEnabled': False, 'directAccessGrantsEnabled': True, 'serviceAccountsEnabled': False, 'authorizationServicesEnabled': False, 'fullScopeAllowed': True} + response = requests.post((url + path), headers=headers, json=data) + if (response.status_code == 201): + print(f'create client {client_id} success') + elif (response.status_code == 409): + raise Exception(response.text) + else: + raise Exception(response.text) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}"""") + client = keycloak_admin.get_client(client_id=hashed_client_id) + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + if (input_params['client_secret_enabled'] == 'true'): + if (input_params['client_secret_value'] == 'null'): + print('null') + client['publicClient'] = False + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + print(json.dumps(keycloak_admin.get_client(client_id=hashed_client_id), indent=4, sort_keys=True)) + else: + client['publicClient'] = False + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + client_secret = input_params['client_secret_value'] + client['secret'] = client_secret + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + print(client_secret) + else: + client['publicClient'] = True + keycloak_admin.update_client(client_id=hashed_client_id, payload=client) + print(f"""delete client secret of client id "{input_params['target_client_id']}" on keycloak""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""update client id "{input_params['target_client_id']} failed""") + print(f"""update client "{input_params['target_client_id']}" success""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print('create client failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) diff --git a/keycloak-client/lib/keycloak-realms.yaml b/keycloak-client/lib/keycloak-realms.yaml new file mode 100644 index 00000000..2010111a --- /dev/null +++ b/keycloak-client/lib/keycloak-realms.yaml @@ -0,0 +1,137 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: keycloak-realm + namespace: argo +spec: + templates: + - name: delete-realm + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test4 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api() + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + keycloak_admin.delete_realm(realm_name=input_params['target_realm_name']) + print(f"delete realm {input_params['target_realm_name']} success") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"delete realm {input_params['target_realm_name']} failed") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: create-realm + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test4 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api() + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + keycloak_admin.create_realm({'realm': input_params['target_realm_name'], 'enabled': True, 'displayName': input_params['target_realm_name']}) + print(f"create realm {input_params['target_realm_name']} success") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print('create realm failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) diff --git a/keycloak-client/lib/keycloak-users.yaml b/keycloak-client/lib/keycloak-users.yaml new file mode 100644 index 00000000..20d888eb --- /dev/null +++ b/keycloak-client/lib/keycloak-users.yaml @@ -0,0 +1,353 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: keycloak-user + namespace: argo +spec: + templates: + - name: unassign-client-role-to-user + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc6 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + - name: user_name + value: user1 + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + import requests + from kubernetes import client, config + import sys + import base64 + import json + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_name': '{{inputs.parameters.user_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + idOfClientRole = keycloak_admin.get_client_role_id(client_id=hashed_client_id, role_name=input_params['client_role_name']) + print(f"""client role id in client id "{input_params['target_client_id']}" is "{idOfClientRole}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client role "{input_params['client_role_name']}" failed""") + try: + idOfUser = keycloak_admin.get_user_id(username=input_params['user_name']) + print(f"""id of user "{input_params['user_name']}" is "{idOfUser}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get user "{input_params['user_name']}" failed""") + try: + keycloak_admin.delete_client_roles_of_user(client_id=hashed_client_id, user_id=idOfUser, roles=[{'id': idOfClientRole, 'name': input_params['client_role_name']}]) + print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception('un-assign client role to user on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: create-user + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: user_name + value: user1 + - name: user_password + value: user1 + - name: user_email + value: test@gmail.com + - name: user_first_name + value: '' + - name: user_last_name + value: '' + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'user_name': '{{inputs.parameters.user_name}}', 'user_password': '{{inputs.parameters.user_password}}', 'user_email': '{{inputs.parameters.user_email}}', 'user_first_name': '{{inputs.parameters.user_first_name}}', 'user_last_name': '{{inputs.parameters.user_last_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + user_name = input_params['user_name'] + user_password = input_params['user_password'] + user_email = input_params['user_email'] + user_first_name = input_params['user_first_name'] + user_last_name = input_params['user_last_name'] + keycloak_admin.create_user({'username': user_name, 'email': user_email, 'enabled': True, 'firstName': user_first_name, 'lastName': user_last_name, 'credentials': [{'type': 'password', 'value': user_password, 'temporary': False}]}) + print(f'create user {user_name} success') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f'create user {user_name} failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: assign-client-role-to-user + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc6 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + - name: user_name + value: user1 + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_name': '{{inputs.parameters.user_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + idOfClientRole = keycloak_admin.get_client_role_id(client_id=hashed_client_id, role_name=input_params['client_role_name']) + print(f"""client role id in client id "{input_params['target_client_id']}" is "{idOfClientRole}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client role "{input_params['client_role_name']}" failed""") + try: + idOfUser = keycloak_admin.get_user_id(username=input_params['user_name']) + print(f"""id of user "{input_params['user_name']}" is "{idOfUser}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get user "{input_params['user_name']}" failed""") + try: + keycloak_admin.assign_client_role(client_id=hashed_client_id, user_id=idOfUser, roles=[{'id': idOfClientRole, 'name': input_params['client_role_name']}]) + print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" success""") + except Exception as inner_e: + print(inner_e) + raise Exception(f'assign client role to user on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: delete-user + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: user_name + value: user1 + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'user_name': '{{inputs.parameters.user_name}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + user_name = input_params['user_name'] + try: + user_id = keycloak_admin.get_user_id(user_name) + print(f'user id of {user_name}: {user_id}') + except Exception as e: + print(e) + raise Exception(f'failed to get user id of {user_name}') + try: + keycloak_admin.delete_user(user_id) + print(f'delete user {user_name} success') + except Exception as e: + print(e) + raise Exception(f'failed to remove user {user_name} on keycloak') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f'delete user {user_name} failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) diff --git a/keycloak-client/set-admin-cluster.yaml b/keycloak-client/set-admin-cluster.yaml new file mode 100644 index 00000000..b6b85ca7 --- /dev/null +++ b/keycloak-client/set-admin-cluster.yaml @@ -0,0 +1,106 @@ +apiVersion: argoproj.io/v1alpha1 +kind: WorkflowTemplate +metadata: + name: set-admin-cluster + namespace: argo +spec: + entrypoint: main + templates: + - name: main + inputs: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - name: user_name + value: "{{inputs.parameters.user_name}}" + steps: + - - name: create-client + templateRef: + name: keycloak-client + template: create-client + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - - name: create-client-role + templateRef: + name: keycloak-client + template: create-client-role + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - - name: create-client-scope-mapper-client-role + templateRef: + name: keycloak-client + template: create-client-scope-mapper-client-role + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: mapper_name + value: k8s-role-mapper + - name: claim_name + value: groups + - name: add_to_access_token + value: false + - name: add_to_id_token + value: true + - name: add_to_userinfo + value: false + - - name: assign-client-role-to-user + templateRef: + name: keycloak-user + template: assign-client-role-to-user + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - name: user_name + value: "{{inputs.parameters.user_name}}" diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 514cb345..271e17e8 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -53,10 +53,25 @@ spec: ] - - name: configure-keycloak - - - - + templateRef: + name: set-admin-cluster + template: main + arguments: + parameters: + - name: server_url + value: "{{workflow.parameters.keycloak_url}}" + - name: target_realm_name + value: "master" + - name: target_client_id + value: "admin-cluster-k8s-api" + - name: keycloak_credential_secret_name + value: "keycloak" + - name: kecyloak_credential_secret_namespace + value: "keycloak" + - name: client_role_name + value: "system:masters" + - name: user_name + value: "admin" - - name: install-tks-api-and-harbor templateRef: From 9377672d9726605c07b005b525dea15bddca79c0 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 25 Sep 2023 14:36:51 +0900 Subject: [PATCH 03/10] minor fix: fix url suffix issue --- keycloak-client/lib/keycloak-clients.yaml | 41 +++++++++++++++++++++++ keycloak-client/lib/keycloak-realms.yaml | 11 ++++++ keycloak-client/lib/keycloak-users.yaml | 21 ++++++++++++ 3 files changed, 73 insertions(+) diff --git a/keycloak-client/lib/keycloak-clients.yaml b/keycloak-client/lib/keycloak-clients.yaml index 4f300438..c6afdfe9 100644 --- a/keycloak-client/lib/keycloak-clients.yaml +++ b/keycloak-client/lib/keycloak-clients.yaml @@ -58,6 +58,11 @@ spec: raise Exception(response.text) else: raise Exception(response.text) + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -136,6 +141,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -216,6 +226,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -303,6 +318,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -412,6 +432,11 @@ spec: raise Exception(response.text) else: raise Exception(response.text) + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -491,6 +516,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -584,6 +614,11 @@ spec: raise Exception(response.text) else: raise Exception(response.text) + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -666,6 +701,11 @@ spec: raise Exception(response.text) else: raise Exception(response.text) + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -721,3 +761,4 @@ spec: print('create client failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) + diff --git a/keycloak-client/lib/keycloak-realms.yaml b/keycloak-client/lib/keycloak-realms.yaml index 2010111a..955741b2 100644 --- a/keycloak-client/lib/keycloak-realms.yaml +++ b/keycloak-client/lib/keycloak-realms.yaml @@ -42,6 +42,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api() try: secret_name = input_params['keycloak_credential_secret_name'] @@ -107,6 +112,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api() try: secret_name = input_params['keycloak_credential_secret_name'] @@ -135,3 +145,4 @@ spec: print('create realm failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) + diff --git a/keycloak-client/lib/keycloak-users.yaml b/keycloak-client/lib/keycloak-users.yaml index 20d888eb..7fbb112a 100644 --- a/keycloak-client/lib/keycloak-users.yaml +++ b/keycloak-client/lib/keycloak-users.yaml @@ -50,6 +50,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -147,6 +152,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -223,6 +233,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -312,6 +327,11 @@ spec: encoded_data = secret_obj.data.get('admin-password') decoded_data = base64.b64decode(encoded_data).decode('utf-8') return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) k8s_client = get_kubernetes_api(local=False) try: secret_name = input_params['keycloak_credential_secret_name'] @@ -351,3 +371,4 @@ spec: print(f'delete user {user_name} failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) + From e4501f4e16f2dd04111ee10a6044f9afb6134314 Mon Sep 17 00:00:00 2001 From: donggyu Date: Mon, 25 Sep 2023 17:30:51 +0900 Subject: [PATCH 04/10] improvement: add assign multiple user to role --- keycloak-client/lib/keycloak-clients.yaml | 3 +- keycloak-client/lib/keycloak-users.yaml | 235 ++++++++++++++++-- keycloak-client/set-admin-cluster.yaml | 196 +++++++-------- .../tks-install-admin-tools.yaml | 148 +++++------ 4 files changed, 388 insertions(+), 194 deletions(-) diff --git a/keycloak-client/lib/keycloak-clients.yaml b/keycloak-client/lib/keycloak-clients.yaml index c6afdfe9..baf5cf97 100644 --- a/keycloak-client/lib/keycloak-clients.yaml +++ b/keycloak-client/lib/keycloak-clients.yaml @@ -182,7 +182,7 @@ spec: keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) except Exception as e: print(e) - print(f"""create client role {role_name} in client "{input_params['target_client_id']}" failed""") + print(f"""create client role "{input_params['client_role_name']}" in client "{input_params['target_client_id']}" failed""") keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - name: append-client-redirect-uri @@ -761,4 +761,3 @@ spec: print('create client failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - diff --git a/keycloak-client/lib/keycloak-users.yaml b/keycloak-client/lib/keycloak-users.yaml index 7fbb112a..8aa1f487 100644 --- a/keycloak-client/lib/keycloak-users.yaml +++ b/keycloak-client/lib/keycloak-users.yaml @@ -20,8 +20,8 @@ spec: value: keycloak - name: client_role_name value: admin - - name: user_name - value: user1 + - name: user_names + value: '["user1"]' script: command: - python3 @@ -34,7 +34,7 @@ spec: import sys import base64 import json - input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_name': '{{inputs.parameters.user_name}}'} + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_names': '{{inputs.parameters.user_names}}'} def get_kubernetes_api(local=False): if local: @@ -87,22 +87,24 @@ spec: except Exception as inner_e: print(inner_e) raise Exception(f"""get client role "{input_params['client_role_name']}" failed""") - try: - idOfUser = keycloak_admin.get_user_id(username=input_params['user_name']) - print(f"""id of user "{input_params['user_name']}" is "{idOfUser}".""") - except Exception as inner_e: - print(inner_e) - raise Exception(f"""get user "{input_params['user_name']}" failed""") + input_params['user_names'] = json.loads(input_params['user_names']) + for user in input_params['user_names']: + try: + idOfUser = keycloak_admin.get_user_id(username=user) + print(f'id of user "{user}" is "{idOfUser}".') + except Exception as inner_e: + print(inner_e) + raise Exception(f'get user "{user}" failed') try: keycloak_admin.delete_client_roles_of_user(client_id=hashed_client_id, user_id=idOfUser, roles=[{'id': idOfClientRole, 'name': input_params['client_role_name']}]) - print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" success""") + print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_names']}" success""") except Exception as inner_e: print(inner_e) raise Exception('un-assign client role to user on keycloak failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) except Exception as e: print(e) - print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" failed""") + print(f"""un-assign client role "{input_params['client_role_name']}" to user "{input_params['user_names']}" failed""") keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - name: create-user @@ -190,7 +192,100 @@ spec: print(f'create user {user_name} failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - - name: assign-client-role-to-user + - name: list-users + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + import json + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}'} + output_file_path = '/tmp/output.json' + output_params = {'users': []} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + users = keycloak_admin.get_users() + print(f'get users success') + name_list = [] + for user in users: + name_list.append(user['username']) + try: + data = {'user_names': name_list} + with open(output_file_path, 'w') as f: + json.dump(data, f) + print(f'Users data saved to {output_file_path}') + except Exception as e: + print(e) + print(f'failed to save users data') + sys.exit(1) + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f'get users failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + volumeMounts: + - mountPath: /tmp + name: output-volume + outputs: + parameters: + - name: output + valueFrom: + path: /tmp/output.json + - name: assign-client-role-to-all-realm-user inputs: parameters: - name: server_url @@ -205,8 +300,6 @@ spec: value: keycloak - name: client_role_name value: admin - - name: user_name - value: user1 script: command: - python3 @@ -217,7 +310,8 @@ spec: from kubernetes import client, config import sys import base64 - input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_name': '{{inputs.parameters.user_name}}'} + import json + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}'} def get_kubernetes_api(local=False): if local: @@ -271,21 +365,121 @@ spec: print(inner_e) raise Exception(f"""get client role "{input_params['client_role_name']}" failed""") try: - idOfUser = keycloak_admin.get_user_id(username=input_params['user_name']) - print(f"""id of user "{input_params['user_name']}" is "{idOfUser}".""") + users = keycloak_admin.get_users() + for user in users: + username = user['username'] + idOfUser = keycloak_admin.get_user_id(username=username) + print(f'id of user "{username}" is "{idOfUser}".') + keycloak_admin.assign_client_role(client_id=hashed_client_id, user_id=idOfUser, roles=[{'id': idOfClientRole, 'name': input_params['client_role_name']}]) + print(f"""assign client role "{input_params['client_role_name']}" to user "{username}" success""") except Exception as inner_e: print(inner_e) - raise Exception(f"""get user "{input_params['user_name']}" failed""") + raise Exception(f'assign client role to user on keycloak failed') + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + except Exception as e: + print(e) + print(f"""assign client role "{input_params['client_role_name']}" to users failed""") + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + sys.exit(1) + - name: assign-client-role-to-user + inputs: + parameters: + - name: server_url + value: http://tks-console-dev.taco-cat.xyz/auth/ + - name: target_realm_name + value: test3 + - name: target_client_id + value: k8s-oidc6 + - name: keycloak_credential_secret_name + value: keycloak + - name: keycloak_credential_secret_namespace + value: keycloak + - name: client_role_name + value: admin + - name: user_names + value: '["user1"]' + script: + command: + - python3 + image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 + source: |2 + + from keycloak import KeycloakOpenIDConnection, KeycloakAdmin, KeycloakOpenID + from kubernetes import client, config + import sys + import base64 + import json + input_params = {'server_url': '{{inputs.parameters.server_url}}', 'target_realm_name': '{{inputs.parameters.target_realm_name}}', 'target_client_id': '{{inputs.parameters.target_client_id}}', 'keycloak_credential_secret_name': '{{inputs.parameters.keycloak_credential_secret_name}}', 'keycloak_credential_secret_namespace': '{{inputs.parameters.keycloak_credential_secret_namespace}}', 'client_role_name': '{{inputs.parameters.client_role_name}}', 'user_names': '{{inputs.parameters.user_names}}'} + + def get_kubernetes_api(local=False): + if local: + import os + kubeconfig_path = os.path.expandvars('$HOME/.kube/config') + config.load_kube_config(config_file=kubeconfig_path) + else: + config.load_incluster_config() + return client.CoreV1Api() + + def get_secret(k8s_client, secret_name, secret_namespace): + secret_obj = k8s_client.read_namespaced_secret(name=secret_name, namespace=secret_namespace) + encoded_data = secret_obj.data.get('admin-password') + decoded_data = base64.b64decode(encoded_data).decode('utf-8') + return decoded_data + + def input_validation(origin_input_params): + if (not (origin_input_params['server_url'][(- 1)] == '/')): + origin_input_params['server_url'] += '/' + input_validation(input_params) + k8s_client = get_kubernetes_api(local=False) + try: + secret_name = input_params['keycloak_credential_secret_name'] + secret_namespace = input_params['keycloak_credential_secret_namespace'] + secret = get_secret(k8s_client, secret_name, secret_namespace) + print(f'get secret "{secret_name}" in "{secret_namespace}" namespace') + except Exception as e: + print(e) + print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') + sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + try: + hashed_client_id = keycloak_admin.get_client_id(client_id=input_params['target_client_id']) + print(f"""hashed_client_id of client id "{input_params['target_client_id']}" is "{hashed_client_id}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client id "{input_params['target_client_id']} failed""") + try: + idOfClientRole = keycloak_admin.get_client_role_id(client_id=hashed_client_id, role_name=input_params['client_role_name']) + print(f"""client role id in client id "{input_params['target_client_id']}" is "{idOfClientRole}".""") + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client role "{input_params['client_role_name']}" failed""") + input_params['user_names'] = json.loads(input_params['user_names']) + for user in input_params['user_names']: + try: + idOfUser = keycloak_admin.get_user_id(username=user) + print(f'id of user "{user}" is "{idOfUser}".') + except Exception as inner_e: + print(inner_e) + raise Exception(f'get user "{user}" failed') try: keycloak_admin.assign_client_role(client_id=hashed_client_id, user_id=idOfUser, roles=[{'id': idOfClientRole, 'name': input_params['client_role_name']}]) - print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" success""") + print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_names']}" success""") except Exception as inner_e: print(inner_e) raise Exception(f'assign client role to user on keycloak failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) except Exception as e: print(e) - print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_name']}" failed""") + print(f"""assign client role "{input_params['client_role_name']}" to user "{input_params['user_names']}" failed""") keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - name: delete-user @@ -371,4 +565,3 @@ spec: print(f'delete user {user_name} failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - diff --git a/keycloak-client/set-admin-cluster.yaml b/keycloak-client/set-admin-cluster.yaml index b6b85ca7..740a9d79 100644 --- a/keycloak-client/set-admin-cluster.yaml +++ b/keycloak-client/set-admin-cluster.yaml @@ -6,101 +6,101 @@ metadata: spec: entrypoint: main templates: - - name: main - inputs: - parameters: - - name: server_url - value: "{{inputs.parameters.server_url}}" - - name: target_realm_name - value: "{{inputs.parameters.target_realm_name}}" - - name: target_client_id - value: "{{inputs.parameters.target_client_id}}" - - name: keycloak_credential_secret_name - value: "{{inputs.parameters.keycloak_credential_secret_name}}" - - name: keycloak_credential_secret_namespace - value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" - - name: client_role_name - value: "{{inputs.parameters.client_role_name}}" - - name: user_name - value: "{{inputs.parameters.user_name}}" - steps: - - - name: create-client - templateRef: - name: keycloak-client - template: create-client - arguments: - parameters: - - name: server_url - value: "{{inputs.parameters.server_url}}" - - name: target_realm_name - value: "{{inputs.parameters.target_realm_name}}" - - name: target_client_id - value: "{{inputs.parameters.target_client_id}}" - - name: keycloak_credential_secret_name - value: "{{inputs.parameters.keycloak_credential_secret_name}}" - - name: keycloak_credential_secret_namespace - value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" - - - name: create-client-role - templateRef: - name: keycloak-client - template: create-client-role - arguments: - parameters: - - name: server_url - value: "{{inputs.parameters.server_url}}" - - name: target_realm_name - value: "{{inputs.parameters.target_realm_name}}" - - name: target_client_id - value: "{{inputs.parameters.target_client_id}}" - - name: keycloak_credential_secret_name - value: "{{inputs.parameters.keycloak_credential_secret_name}}" - - name: keycloak_credential_secret_namespace - value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" - - name: client_role_name - value: "{{inputs.parameters.client_role_name}}" - - - name: create-client-scope-mapper-client-role - templateRef: - name: keycloak-client - template: create-client-scope-mapper-client-role - arguments: - parameters: - - name: server_url - value: "{{inputs.parameters.server_url}}" - - name: target_realm_name - value: "{{inputs.parameters.target_realm_name}}" - - name: target_client_id - value: "{{inputs.parameters.target_client_id}}" - - name: keycloak_credential_secret_name - value: "{{inputs.parameters.keycloak_credential_secret_name}}" - - name: keycloak_credential_secret_namespace - value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" - - name: mapper_name - value: k8s-role-mapper - - name: claim_name - value: groups - - name: add_to_access_token - value: false - - name: add_to_id_token - value: true - - name: add_to_userinfo - value: false - - - name: assign-client-role-to-user - templateRef: - name: keycloak-user - template: assign-client-role-to-user - arguments: - parameters: - - name: server_url - value: "{{inputs.parameters.server_url}}" - - name: target_realm_name - value: "{{inputs.parameters.target_realm_name}}" - - name: target_client_id - value: "{{inputs.parameters.target_client_id}}" - - name: keycloak_credential_secret_name - value: "{{inputs.parameters.keycloak_credential_secret_name}}" - - name: keycloak_credential_secret_namespace - value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" - - name: client_role_name - value: "{{inputs.parameters.client_role_name}}" - - name: user_name - value: "{{inputs.parameters.user_name}}" + - name: main + inputs: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - name: user_names + value: "{{inputs.parameters.user_names}}" + steps: + - - name: create-client + templateRef: + name: keycloak-client + template: create-client + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - - name: create-client-role + templateRef: + name: keycloak-client + template: create-client-role + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - - name: create-client-scope-mapper-client-role + templateRef: + name: keycloak-client + template: create-client-scope-mapper-client-role + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: mapper_name + value: k8s-role-mapper + - name: claim_name + value: groups + - name: add_to_access_token + value: false + - name: add_to_id_token + value: true + - name: add_to_userinfo + value: false + - - name: assign-client-role-to-user + templateRef: + name: keycloak-user + template: assign-client-role-to-user + arguments: + parameters: + - name: server_url + value: "{{inputs.parameters.server_url}}" + - name: target_realm_name + value: "{{inputs.parameters.target_realm_name}}" + - name: target_client_id + value: "{{inputs.parameters.target_client_id}}" + - name: keycloak_credential_secret_name + value: "{{inputs.parameters.keycloak_credential_secret_name}}" + - name: keycloak_credential_secret_namespace + value: "{{inputs.parameters.keycloak_credential_secret_namespace}}" + - name: client_role_name + value: "{{inputs.parameters.client_role_name}}" + - name: user_names + value: "{{inputs.parameters.user_names}}" diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 271e17e8..1031c725 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -7,84 +7,86 @@ spec: entrypoint: main arguments: parameters: - - name: manifest_repo_url - value: "" - - name: revision - value: main - # This param is also used as default target cluster on Argo CD - - name: site_name - value: "" - - name: app_prefix - value: "tks-admin" - - name: install-nginx - value: false + - name: manifest_repo_url + value: "" + - name: revision + value: main + # This param is also used as default target cluster on Argo CD + - name: site_name + value: "" + - name: app_prefix + value: "tks-admin" + - name: install-nginx + value: false + - name: keycloak_url + value: "" volumes: - - name: config - secret: - secretName: tks-admin-kubeconfig-secret + - name: config + secret: + secretName: tks-admin-kubeconfig-secret templates: - - name: main - steps: - - - name: install-nginx-controller - templateRef: - name: create-application - template: installApps - arguments: - parameters: - - name: list - value: | - [ - ## If value-override for admin-cluster is different from that of user-cluster, - ## then the ingress-nginx should be copied to tks-admin-tools group. - { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" }, - ] - when: "{{workflow.parameters.install-nginx}} == true" + - name: main + steps: + - - name: install-nginx-controller + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + ## If value-override for admin-cluster is different from that of user-cluster, + ## then the ingress-nginx should be copied to tks-admin-tools group. + { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" }, + ] + when: "{{workflow.parameters.install-nginx}} == true" - - - name: install-keycloak - templateRef: - name: create-application - template: installApps - arguments: - parameters: - - name: list - value: | - [ - { "app_group": "tks-admin-tools", "path": "keycloak", "namespace": "keycloak", "target_cluster": "" } - ] + - - name: install-keycloak + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + { "app_group": "tks-admin-tools", "path": "keycloak", "namespace": "keycloak", "target_cluster": "" } + ] - - - name: configure-keycloak - templateRef: - name: set-admin-cluster - template: main - arguments: - parameters: - - name: server_url - value: "{{workflow.parameters.keycloak_url}}" - - name: target_realm_name - value: "master" - - name: target_client_id - value: "admin-cluster-k8s-api" - - name: keycloak_credential_secret_name - value: "keycloak" - - name: kecyloak_credential_secret_namespace - value: "keycloak" - - name: client_role_name - value: "system:masters" - - name: user_name - value: "admin" + - - name: configure-keycloak + templateRef: + name: set-admin-cluster + template: main + arguments: + parameters: + - name: server_url + value: "{{workflow.parameters.keycloak_url}}" + - name: target_realm_name + value: "master" + - name: target_client_id + value: "admin-cluster-k8s-api" + - name: keycloak_credential_secret_name + value: "keycloak" + - name: keycloak_credential_secret_namespace + value: "keycloak" + - name: client_role_name + value: "system:masters" + - name: user_names + value: '["admin"]' - - - name: install-tks-api-and-harbor - templateRef: - name: create-application - template: installApps - arguments: - parameters: - - name: list - value: | - [ - { "app_group": "tks-admin-tools", "path": "tks-api", "namespace": "tks", "target_cluster": "" }, - { "app_group": "tks-admin-tools", "path": "harbor", "namespace": "harbor", "target_cluster": "" } - ] + - - name: install-tks-api-and-harbor + templateRef: + name: create-application + template: installApps + arguments: + parameters: + - name: list + value: | + [ + { "app_group": "tks-admin-tools", "path": "tks-api", "namespace": "tks", "target_cluster": "" }, + { "app_group": "tks-admin-tools", "path": "harbor", "namespace": "harbor", "target_cluster": "" } + ] From 354f561716d0548c87c376cbbd2e32a34105c28a Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Fri, 6 Oct 2023 10:54:17 +0900 Subject: [PATCH 05/10] init keyclock db before installation --- .../tks-install-admin-tools.yaml | 67 +++++++++++++++++-- 1 file changed, 61 insertions(+), 6 deletions(-) diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 1031c725..36420025 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -20,8 +20,13 @@ spec: value: false - name: keycloak_url value: "" + - name: db_host + value: "postgresql.tks-db" + - name: db_keycloak_password + value: "" + # TODO: check if this is necessary volumes: - - name: config + - name: admin-kubeconfig secret: secretName: tks-admin-kubeconfig-secret templates: @@ -33,15 +38,24 @@ spec: template: installApps arguments: parameters: + ## If value-override for admin-cluster is different from that of user-cluster, + ## then the ingress-nginx should be copied to tks-admin-tools group. - name: list value: | [ - ## If value-override for admin-cluster is different from that of user-cluster, - ## then the ingress-nginx should be copied to tks-admin-tools group. - { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" }, + { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" } ] when: "{{workflow.parameters.install-nginx}} == true" + - - name: init-keycloak-db + template: init-keycloak-db + arguments: + parameters: + - name: db_keycloak_password + value: "{{ workflow.parameters.db_keycloak_password }}" + - name: db_host + value: "{{ workflow.parameters.db_host }}" + - - name: install-keycloak templateRef: name: create-application @@ -88,6 +102,47 @@ spec: { "app_group": "tks-admin-tools", "path": "harbor", "namespace": "harbor", "target_cluster": "" } ] + ####################### + # Template Definition # + ####################### + - name: init-keycloak-db + inputs: + parameters: + - name: db_host + value: "postgres.tks-db" + - name: db_keycloak_user + value: "bn_keycloak" + - name: db_keycloak_password + value: "password" + - name: db_keycloak_db + value: "bitnami_keycloak" + container: + name: init-keycloak-db + image: jbergknoff/postgresql-client + env: + # postgres admin username + - name: POSTGRES_USER + value: "postgres" + - name: POSTGRES_HOST + value: "{{ inputs.parameters.db_host }}" + - name: POSTGRES_ADMIN_PW + valueFrom: + secretKeyRef: + name: argo-postgres-config + key: postgresql-password + command: + - /bin/sh + - "-exc" + - | + # config for new keycloak DB + NEW_USER="{{ inputs.parameters.db_keycloak_user }}" + NEW_USER_PASSWORD="{{ inputs.parameters.db_keycloak_password }}" + NEW_DB="{{ inputs.parameters.db_keycloak_db }}" - - + # psql 명령어를 사용하여 사용자와 데이터베이스를 생성하고 권한을 부여 + export PGPASSWORD=$POSTGRES_ADMIN_PW + psql -h $POSTGRES_HOST -U $POSTGRES_USER -d postgres < Date: Fri, 6 Oct 2023 17:31:46 +0900 Subject: [PATCH 06/10] init harbor db before installation --- .../tks-install-admin-tools.yaml | 61 +++++++++++++++++-- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 36420025..6ee39f9e 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -13,16 +13,17 @@ spec: value: main # This param is also used as default target cluster on Argo CD - name: site_name - value: "" + value: "tks-admin" - name: app_prefix value: "tks-admin" - name: install-nginx value: false - name: keycloak_url - value: "" + value: "https://keycloak.yourdomain.org/auth/" - name: db_host value: "postgresql.tks-db" - - name: db_keycloak_password + # DB password for keycloak and harbor user + - name: db_common_password value: "" # TODO: check if this is necessary volumes: @@ -52,7 +53,7 @@ spec: arguments: parameters: - name: db_keycloak_password - value: "{{ workflow.parameters.db_keycloak_password }}" + value: "{{ workflow.parameters.db_common_password }}" - name: db_host value: "{{ workflow.parameters.db_host }}" @@ -89,6 +90,15 @@ spec: - name: user_names value: '["admin"]' + - - name: init-harbor-db + template: init-harbor-db + arguments: + parameters: + - name: db_harbor_password + value: "{{ workflow.parameters.db_common_password }}" + - name: db_host + value: "{{ workflow.parameters.db_host }}" + - - name: install-tks-api-and-harbor templateRef: name: create-application @@ -139,10 +149,51 @@ spec: NEW_USER_PASSWORD="{{ inputs.parameters.db_keycloak_password }}" NEW_DB="{{ inputs.parameters.db_keycloak_db }}" - # psql 명령어를 사용하여 사용자와 데이터베이스를 생성하고 권한을 부여 + # Create user and db and grant privileges export PGPASSWORD=$POSTGRES_ADMIN_PW psql -h $POSTGRES_HOST -U $POSTGRES_USER -d postgres < Date: Tue, 10 Oct 2023 21:13:30 +0900 Subject: [PATCH 07/10] use ingress-nginx in tks-admin-tools group --- tks-admin-cluster/tks-install-admin-tools.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 6ee39f9e..0cd24da9 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -44,7 +44,7 @@ spec: - name: list value: | [ - { "app_group": "tks-cluster", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" } + { "app_group": "tks-admin-tools", "path": "ingress-nginx", "namespace": "ingress-nginx", "target_cluster": "" } ] when: "{{workflow.parameters.install-nginx}} == true" @@ -108,7 +108,7 @@ spec: - name: list value: | [ - { "app_group": "tks-admin-tools", "path": "tks-api", "namespace": "tks", "target_cluster": "" }, + { "app_group": "tks-admin-tools", "path": "tks-apis", "namespace": "tks", "target_cluster": "" }, { "app_group": "tks-admin-tools", "path": "harbor", "namespace": "harbor", "target_cluster": "" } ] From 81b2e7655102a456d642de77b5a66b5686872822 Mon Sep 17 00:00:00 2001 From: Robert Choi Date: Tue, 10 Oct 2023 21:42:28 +0900 Subject: [PATCH 08/10] fix wrong sql command --- tks-admin-cluster/tks-install-admin-tools.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tks-admin-cluster/tks-install-admin-tools.yaml b/tks-admin-cluster/tks-install-admin-tools.yaml index 0cd24da9..04869013 100644 --- a/tks-admin-cluster/tks-install-admin-tools.yaml +++ b/tks-admin-cluster/tks-install-admin-tools.yaml @@ -192,7 +192,7 @@ spec: export PGPASSWORD=$POSTGRES_ADMIN_PW psql -h $POSTGRES_HOST -U $POSTGRES_USER -d postgres < Date: Thu, 12 Oct 2023 13:42:20 +0900 Subject: [PATCH 09/10] minor fix: disable ssl verification --- keycloak-client/lib/keycloak-clients.yaml | 16 ++++++++-------- keycloak-client/lib/keycloak-realms.yaml | 5 ++--- keycloak-client/lib/keycloak-users.yaml | 12 ++++++------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/keycloak-client/lib/keycloak-clients.yaml b/keycloak-client/lib/keycloak-clients.yaml index baf5cf97..ad9a7d77 100644 --- a/keycloak-client/lib/keycloak-clients.yaml +++ b/keycloak-client/lib/keycloak-clients.yaml @@ -73,7 +73,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -156,7 +156,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -241,7 +241,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -333,7 +333,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -447,7 +447,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -531,7 +531,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -629,7 +629,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') + keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', verify=False) token = keycloak_openid.token(grant_type='password', username='admin', password=secret) try: create_client(input_params['server_url'], input_params['target_realm_name'], input_params['target_client_id'], token) @@ -716,7 +716,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) diff --git a/keycloak-client/lib/keycloak-realms.yaml b/keycloak-client/lib/keycloak-realms.yaml index 955741b2..02b58e4d 100644 --- a/keycloak-client/lib/keycloak-realms.yaml +++ b/keycloak-client/lib/keycloak-realms.yaml @@ -57,7 +57,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -127,7 +127,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -145,4 +145,3 @@ spec: print('create realm failed') keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) sys.exit(1) - diff --git a/keycloak-client/lib/keycloak-users.yaml b/keycloak-client/lib/keycloak-users.yaml index 8aa1f487..2168b726 100644 --- a/keycloak-client/lib/keycloak-users.yaml +++ b/keycloak-client/lib/keycloak-users.yaml @@ -65,7 +65,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -169,7 +169,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -247,7 +247,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -342,7 +342,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -440,7 +440,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) @@ -536,7 +536,7 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) - keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=True) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master') try: keycloak_admin = KeycloakAdmin(connection=keycloak_connection) From 92fbc752cdef039c478c9ea97f4a3e11cc7bf318 Mon Sep 17 00:00:00 2001 From: donggyu Date: Thu, 12 Oct 2023 15:00:46 +0900 Subject: [PATCH 10/10] minor fix: add idempotent to keycloak client --- keycloak-client/lib/keycloak-clients.yaml | 30 +++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/keycloak-client/lib/keycloak-clients.yaml b/keycloak-client/lib/keycloak-clients.yaml index ad9a7d77..e2bcc0de 100644 --- a/keycloak-client/lib/keycloak-clients.yaml +++ b/keycloak-client/lib/keycloak-clients.yaml @@ -174,7 +174,7 @@ spec: raise Exception(f"""get client id "{input_params['target_client_id']} failed""") try: role_name = input_params['client_role_name'] - keycloak_admin.create_client_role(client_role_id=hashed_client_id, payload={'name': role_name, 'clientRole': True}) + keycloak_admin.create_client_role(client_role_id=hashed_client_id, payload={'name': role_name, 'clientRole': True}, skip_exists=True) print(f"""create client role "{role_name}" in client "{input_params['target_client_id']}" success""") except Exception as inner_e: print(inner_e) @@ -426,10 +426,8 @@ spec: headers = {'Content-Type': 'application/json', 'Authorization': ('Bearer ' + token['access_token'])} data = {'name': mapper_name, 'protocol': 'openid-connect', 'protocolMapper': 'oidc-usermodel-client-role-mapper', 'config': {'usermodel.clientRoleMapping.clientId': client_id, 'claim.name': input_params['claim_name'], 'access.token.claim': input_params['add_to_access_token'], 'id.token.claim': input_params['add_to_id_token'], 'userinfo.token.claim': input_params['add_to_userinfo'], 'multivalued': 'true', 'jsonType.label': 'String'}} response = requests.post((url + path), headers=headers, json=data) - if (response.status_code == 201): + if ((response.status_code == 201) or (response.status_code == 409)): print(f'create client scope mapper {client_id} success') - elif (response.status_code == 409): - raise Exception(response.text) else: raise Exception(response.text) @@ -579,7 +577,7 @@ spec: image: harbor-cicd.taco-cat.xyz/dev/python-keycloak-cli:v0.1.0 source: |2 - from keycloak import KeycloakOpenID + from keycloak import KeycloakOpenID, KeycloakAdmin, KeycloakOpenIDConnection import requests from kubernetes import client, config import sys @@ -629,11 +627,29 @@ spec: print(e) print(f'failed to get secret "{secret_name}" in "{secret_namespace}" namespace') sys.exit(1) + keycloak_connection = KeycloakOpenIDConnection(server_url=input_params['server_url'], client_id='admin-cli', realm_name=input_params['target_realm_name'], user_realm_name='master', username='admin', password=secret, verify=False) keycloak_openid = KeycloakOpenID(server_url=input_params['server_url'], client_id='admin-cli', realm_name='master', verify=False) token = keycloak_openid.token(grant_type='password', username='admin', password=secret) try: - create_client(input_params['server_url'], input_params['target_realm_name'], input_params['target_client_id'], token) - print(f"""create client "{input_params['target_client_id']}" success""") + try: + keycloak_admin = KeycloakAdmin(connection=keycloak_connection) + print(f"login to {input_params['server_url']} success") + except Exception as e: + print(e) + print(f"login to {input_params['server_url']} failed") + sys.exit(1) + try: + hashed_client_id = keycloak_admin.get_client_id(input_params['target_client_id']) + except Exception as inner_e: + print(inner_e) + raise Exception(f"""get client with id "{input_params['target_client_id']}" failed""") + finally: + keycloak_openid.logout(keycloak_admin.connection.token['refresh_token']) + if hashed_client_id: + print('client already exists') + else: + create_client(input_params['server_url'], input_params['target_realm_name'], input_params['target_client_id'], token) + print(f"""create client "{input_params['target_client_id']}" success""") keycloak_openid.logout(token['refresh_token']) except Exception as e: print(e)