From b07ecce9a970498da050d11d6b419c316056ab21 Mon Sep 17 00:00:00 2001 From: pjrm <4622652+pjrm@users.noreply.github.com> Date: Fri, 18 Dec 2020 15:27:13 +0000 Subject: [PATCH 1/3] Maintain consistency across all methods and force all payload content to be in JSON format --- keycloak/keycloak_admin.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 8fd19f6..9c1155c 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -536,7 +536,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "id": user_id} params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri} data_raw = self.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path), - data=payload, **params_query) + data=json.dumps(payload), **params_query) return raise_error_from_response(data_raw, KeycloakGetError) def send_verify_email(self, user_id, client_id=None, redirect_uri=None): @@ -1359,7 +1359,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), - data=payload) + data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) def copy_authentication_flow(self, payload, flow_alias): @@ -1373,7 +1373,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post(URL_ADMIN_FLOWS_COPY.format(**params_path), - data=payload) + data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def get_authentication_flow_executions(self, flow_alias): @@ -1401,7 +1401,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_put(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), - data=payload) + data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def create_authentication_flow_execution(self, payload, flow_alias): @@ -1418,7 +1418,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION.format(**params_path), - data=payload) + data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): @@ -1436,7 +1436,7 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), - data=payload) + data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) def get_authenticator_config(self, config_id): From 021c549e30a522f322cb140dec28d64e4c8952d2 Mon Sep 17 00:00:00 2001 From: hamedSh Date: Sun, 10 Jan 2021 13:48:04 +0330 Subject: [PATCH 2/3] Get sessions associated with the client. --- keycloak/keycloak_admin.py | 20 ++++++++++++++++++-- keycloak/urls_patterns.py | 1 + 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 8fd19f6..76219c3 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -32,7 +32,7 @@ from .connection import ConnectionManager from .exceptions import raise_error_from_response, KeycloakGetError from .keycloak_openid import KeycloakOpenID from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURCES, URL_ADMIN_CLIENT_ROLES, \ - URL_ADMIN_GET_SESSIONS, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_GROUPS_REALM_ROLES,\ + URL_ADMIN_GET_SESSIONS, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_GROUPS_REALM_ROLES, \ URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE, URL_ADMIN_CLIENT_INSTALLATION_PROVIDER, \ URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, URL_ADMIN_GET_GROUPS_REALM_ROLES, URL_ADMIN_GROUPS_CLIENT_ROLES, \ URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, URL_ADMIN_USER_GROUP, URL_ADMIN_REALM_ROLES, URL_ADMIN_GROUP_CHILD, \ @@ -46,7 +46,8 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, URL_ADMIN_CLIENT_ROLE_MEMBERS, \ URL_ADMIN_REALM_ROLES_MEMBERS, URL_ADMIN_CLIENT_PROTOCOL_MAPPER, URL_ADMIN_CLIENT_SCOPES_MAPPERS, \ URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY, \ - URL_ADMIN_FLOWS_ALIAS, URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, URL_ADMIN_AUTHENTICATOR_CONFIG + URL_ADMIN_FLOWS_ALIAS, URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, URL_ADMIN_AUTHENTICATOR_CONFIG, \ + URL_ADMIN_CLIENT_ALL_SESSIONS class KeycloakAdmin: @@ -1810,3 +1811,18 @@ class KeycloakAdmin: else: raise self.connection.add_param_headers('Authorization', 'Bearer ' + self.token.get('access_token')) + + def get_client_all_sessions(self, client_id): + """ + Get sessions associated with the client + + :param client_id: id of client + + UserSessionRepresentation + http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation + + :return: UserSessionRepresentation + """ + params_path = {"realm-name": self.realm_name, "client-id": client_id} + data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index d45ef8d..95acaf9 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -63,6 +63,7 @@ URL_ADMIN_GROUP_MEMBERS = "admin/realms/{realm-name}/groups/{id}/members" URL_ADMIN_CLIENTS = "admin/realms/{realm-name}/clients" URL_ADMIN_CLIENT = URL_ADMIN_CLIENTS + "/{id}" +URL_ADMIN_CLIENT_ALL_SESSIONS = URL_ADMIN_CLIENT + "/user-sessions" URL_ADMIN_CLIENT_SECRETS= URL_ADMIN_CLIENT + "/client-secret" URL_ADMIN_CLIENT_ROLES = URL_ADMIN_CLIENT + "/roles" URL_ADMIN_CLIENT_ROLE = URL_ADMIN_CLIENT + "/roles/{role-name}" From 6ed65ea4e51098e9eb805dbfd31f4a0ac62212a5 Mon Sep 17 00:00:00 2001 From: Lukas Martini Date: Thu, 14 Jan 2021 11:57:45 +0100 Subject: [PATCH 3/3] Add exist_ok attribute to KeycloakAdmin.create_user This attribute allows configuration of the behaviour of create_user when a user with the passed username already exists. If set to False, an exception will be raised (passed through) from the API. If set to True (default), the existing user ID will silently be returned. --- README.md | 10 ++++++++++ keycloak/keycloak_admin.py | 10 ++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c2af7fc..095782b 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ The documentation for python-keycloak is available on [readthedocs](http://pytho * [Josha Inglis](https://bitbucket.org/joshainglis/) * [Alex](https://bitbucket.org/alex_zel/) * [Ewan Jone](https://bitbucket.org/kisamoto/) +* [Lukas Martini](https://github.com/lutoma) ## Usage @@ -125,6 +126,15 @@ new_user = keycloak_admin.create_user({"email": "example@example.com", "enabled": True, "firstName": "Example", "lastName": "Example"}) + +# Add user and raise exception if username already exists +# exist_ok currently defaults to True for backwards compatibility reasons +new_user = keycloak_admin.create_user({"email": "example@example.com", + "username": "example@example.com", + "enabled": True, + "firstName": "Example", + "lastName": "Example"}, + exist_ok=False) # Add user and set password new_user = keycloak_admin.create_user({"email": "example@example.com", diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 8fd19f6..b42f6a3 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -361,7 +361,7 @@ class KeycloakAdmin: data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def create_user(self, payload): + def create_user(self, payload, exist_ok=True): """ Create a new user. Username must be unique @@ -369,15 +369,17 @@ class KeycloakAdmin: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation :param payload: UserRepresentation + :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID. :return: UserRepresentation """ params_path = {"realm-name": self.realm_name} - exists = self.get_user_id(username=payload['username']) + if exist_ok: + exists = self.get_user_id(username=payload['username']) - if exists is not None: - return str(exists) + if exists is not None: + return str(exists) data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload))