def create_service_account(namespace: str) -> object: enforce_authorization(namespace) f = open("templates/keycloak/client.json", "r") j = json.loads(f.read()) cid = "sa-%s-%s" % (namespace, get_random_string(10)) j['clientId'] = cid j['protocolMappers'][0]['config']['claim.value'] = namespace keycloak_admin = admin_api() try: response = keycloak_admin.create_client(j) cuuid = keycloak_admin.get_client_id(cid) r = keycloak_admin.generate_client_secrets(cuuid) return ({'client_id': cid, 'client_secret': r['value']}, 201) except KeycloakGetError as err: if err.response_code == 409: abort( make_response( jsonify( error= "Service Account for this namespace is already created." ), 400)) else: log.error(err) abort( make_response(jsonify(error="Failed to add service account"), 400))
def update_service_account_credentials(namespace: str, client_id: str) -> object: enforce_authorization(namespace) cid = "sa-%s-" % namespace if not client_id.startswith(cid): abort(make_response(jsonify(error="Invalid client ID"), 400)) keycloak_admin = admin_api() try: cuuid = keycloak_admin.get_client_id(client_id) r = keycloak_admin.generate_client_secrets(cuuid) return ({'client_id': client_id, 'client_secret': r['value']}, 201) except KeycloakGetError as err: if err.response_code == 409: abort( make_response( jsonify( error= "Service Account for this namespace is already created." ), 400)) else: log.error(err) abort( make_response(jsonify(error="Failed to add service account"), 400))
def membership_sync(namespace, role_name, desired_membership_list): log = app.logger desired_membership = [] for user in desired_membership_list: if role_name in user['roles']: desired_membership.append(user['username']) keycloak_admin = admin_api() base_group_path = get_base_group_path(role_name) group = keycloak_admin.get_group_by_path("%s/%s" % (base_group_path, namespace), search_in_subgroups=True) if group is None: log.warn("[%s] Group %s/%s Missing!" % (namespace, base_group_path, namespace)) create_group(namespace, base_group_path, role_name) group = keycloak_admin.get_group_by_path("%s/%s" % (base_group_path, namespace), search_in_subgroups=True) group = keycloak_admin.get_group(group['id']) membership = keycloak_admin.get_group_members(group['id']) counts_removed = 0 counts_added = 0 counts_missing = 0 # Remove users that are not part of the provided membership for member in membership: if member['username'] not in desired_membership: log.debug("[%s] REMOVE user %s from %s", namespace, member['username'], base_group_path) keycloak_admin.group_user_remove(member['id'], group['id']) counts_removed = counts_removed + 1 else: desired_membership.remove(member['username']) # Add missing users to the membership unregistered_users = [] for username in desired_membership: user_id = keycloak_admin.get_user_id(username) if user_id is None: log.debug("[%s] UNREGISTERED user %s FROM %s", namespace, username, base_group_path) counts_missing = counts_missing + 1 unregistered_users.append(username) else: log.debug("[%s] ADDING user %s TO %s", namespace, username, base_group_path) keycloak_admin.group_user_add(user_id, group['id']) counts_added = counts_added + 1 # Update the pending attribute with users that are not registered yet update_pending_registrations(group, unregistered_users) return counts_added, counts_removed, counts_missing
def create_namespace() -> object: log = app.logger enforce_role_authorization('aps.ns:manage') keycloak_admin = admin_api() namespace = request.get_json(force=True)['name'] if not namespace_valid(namespace): log.error("Namespace validation failed %s" % namespace) abort( make_response( jsonify( error= "Namespace name validation failed. Reference regular expression '%s'." % namespace_validation_rule), 400)) payload = {"name": namespace} try: for role_name in ['viewer', 'admin']: group_base_path = get_base_group_path(role_name) parent_group = keycloak_admin.get_group_by_path(group_base_path) if parent_group is None: keycloak_admin.create_group( {"name": get_base_group_name(role_name)}) parent_group = keycloak_admin.get_group_by_path( group_base_path) response = keycloak_admin.create_group(payload, parent=parent_group['id']) log.debug("[%s] Group %s/%s created!" % (namespace, group_base_path, namespace)) new_users_group_id = response['id'] if 'preferred_username' in g.principal: username = g.principal['preferred_username'] user_id = keycloak_admin.get_user_id(username) log.debug("[%s] ADDING user %s TO %s" % (namespace, username, group_base_path)) keycloak_admin.group_user_add(user_id, new_users_group_id) except KeycloakGetError as err: if err.response_code == 409: log.error("Namespace %s already created." % namespace) log.error(err) abort( make_response(jsonify(error="Namespace is already created."), 400)) else: log.error("Failed to create namespace %s" % namespace) log.error(err) abort(make_response(jsonify(error="Failed to add namespace"), 400)) return ('', 201)
def update_pending_registrations(group, unregistered_users): if 'attributes' in group: attrs = group['attributes'] else: attrs = group['attributes'] = {} attrs['pending'] = unregistered_users keycloak_admin = admin_api() keycloak_admin.update_group(group['id'], group)
def create_group(namespace, group_base_path, role_name): log = app.logger keycloak_admin = admin_api() parent_group = keycloak_admin.get_group_by_path(group_base_path) if parent_group is None: keycloak_admin.create_group({"name": get_base_group_name(role_name)}) parent_group = keycloak_admin.get_group_by_path(group_base_path) keycloak_admin.create_group({"name": namespace}, parent=parent_group['id']) log.debug("[%s] Group %s/%s created!", namespace, group_base_path, namespace)
def delete_namespace(namespace: str) -> object: log = app.logger enforce_authorization(namespace) enforce_role_authorization('aps.ns:manage') keycloak_admin = admin_api() try: for role_name in ['viewer', 'admin']: group = keycloak_admin.get_group_by_path( "%s/%s" % (get_base_group_path(role_name), namespace), search_in_subgroups=True) if group is not None: keycloak_admin.delete_group(group['id']) except KeycloakGetError as err: log.error(err) abort(make_response(jsonify(error="Failed to delete namespace"), 400)) return ('', 204)
def list_service_accounts(namespace: str) -> object: enforce_authorization(namespace) keycloak_admin = admin_api() try: params_path = {"realm-name": keycloak_admin.realm_name} data_raw = keycloak_admin.raw_get( URL_ADMIN_CLIENTS.format(**params_path), clientId='sa-%s-' % namespace, search=True) response = raise_error_from_response(data_raw, KeycloakGetError) result = [] for r in response: result.append(r['clientId']) return (json.dumps(result), 200) except KeycloakGetError as err: log.error(err) abort( make_response(jsonify(error="Failed to read service accounts"), 400))
def export_details() -> object: log = app.logger try: keycloak_admin = admin_api() response = [] all_plugins = get_plugins() acl_namespaces = get_acl_namespaces(keycloak_admin) namespaces = get_namespaces(keycloak_admin) for namespace in namespaces: ns_name = namespace['name'] ns_id = namespace['id'] ns = { "namespace": ns_name, "attributes": {}, "view_membership": [], "admin_membership": [], "service_accounts": [], "acl_protected": [] } for acln in acl_namespaces: if acln['name'] == ns_name: ns['admin_membership'] = get_group_membership( keycloak_admin, acln['id']) break ns['view_membership'] = get_group_membership(keycloak_admin, ns_id) ns['attributes'] = get_group_attributes(keycloak_admin, ns_id) ns['service_accounts'] = get_service_accounts( keycloak_admin, ns_name) ns['acl_protected'] = get_acl_protected_services_by_ns( ns_name, all_plugins) response.append(ns) return make_response(jsonify(response)) except: traceback.print_exc() log.error("Error generating report. %s" % sys.exc_info()[0]) abort(make_response(jsonify(error="Failed to generate report"), 400))
def delete_service_account(namespace: str, client_id: str) -> object: log = app.logger enforce_authorization(namespace) if client_id_valid(namespace, client_id) == False: abort(make_response(jsonify(error="Invalid client ID"), 400)) keycloak_admin = admin_api() try: cuuid = keycloak_admin.get_client_id(client_id) if cuuid is None: abort( make_response(jsonify(error="Service Account does not exist"), 400)) else: keycloak_admin.delete_client(cuuid) return ({}, 204) except KeycloakGetError as err: log.error(err) abort( make_response(jsonify(error="Failed to delete service account"), 400))
def __init__ (self): self.keycloak_admin = admin_api()