From 2aedfec220c47f62cbabc771a43d38ad5e34df3f Mon Sep 17 00:00:00 2001 From: Abhijeet Sharma Date: Fri, 4 Sep 2020 16:49:31 +0530 Subject: [PATCH 01/17] Add Missing Methods --- keycloak/keycloak_admin.py | 31 ++++++++++++++++++++++++++++++- keycloak/urls_patterns.py | 1 + 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..71964db 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -40,7 +40,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_GROUPS, URL_ADMIN_CLIENTS, URL_ADMIN_FLOWS_EXECUTIONS, URL_ADMIN_GROUPS, URL_ADMIN_USER_CLIENT_ROLES, \ URL_ADMIN_REALMS, URL_ADMIN_USERS_COUNT, URL_ADMIN_FLOWS, URL_ADMIN_GROUP, URL_ADMIN_CLIENT_AUTHZ_SETTINGS, \ URL_ADMIN_GROUP_MEMBERS, URL_ADMIN_USER_STORAGE, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_IDPS, \ - URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ + URL_ADMIN_IDP_MAPPERS, URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES @@ -306,6 +306,35 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} return self.__fetch_all(URL_ADMIN_USERS.format(**params_path), query) + def create_idp(self, payload): + """ + Create an ID Provider, + + IdentityProviderRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation + + :param: payload: IdentityProviderRepresentation + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + + def add_mapper_to_idp(self, idp_alias, payload): + """ + Create an ID Provider, + + IdentityProviderRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityprovidermapperrepresentation + + :param: idp_alias: alias for Idp to add mapper in + :param: payload: IdentityProviderMapperRepresentation + """ + params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias} + data_raw = self.raw_post(URL_ADMIN_IDP_MAPPERS.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + def get_idps(self): """ Returns a list of ID Providers, diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 3e28de8..2c6fcfc 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -78,6 +78,7 @@ URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles" URL_ADMIN_REALMS = "admin/realms" URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" +URL_ADMIN_IDP_MAPPERS = "admin/realms/{realm-name}/identity-provider/instances/{idp-alias}/mappers" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" From f59dc97466fb58fb1aede31359bc51e5f9199b47 Mon Sep 17 00:00:00 2001 From: Abhijeet Sharma Date: Tue, 8 Sep 2020 14:33:25 +0530 Subject: [PATCH 02/17] Added delete_idp method --- keycloak/keycloak_admin.py | 12 +++++++++++- keycloak/urls_patterns.py | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 71964db..a03ef92 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -39,7 +39,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_CONSENTS, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_CLIENT, URL_ADMIN_USER, URL_ADMIN_CLIENT_ROLE, \ URL_ADMIN_USER_GROUPS, URL_ADMIN_CLIENTS, URL_ADMIN_FLOWS_EXECUTIONS, URL_ADMIN_GROUPS, URL_ADMIN_USER_CLIENT_ROLES, \ URL_ADMIN_REALMS, URL_ADMIN_USERS_COUNT, URL_ADMIN_FLOWS, URL_ADMIN_GROUP, URL_ADMIN_CLIENT_AUTHZ_SETTINGS, \ - URL_ADMIN_GROUP_MEMBERS, URL_ADMIN_USER_STORAGE, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_IDPS, \ + URL_ADMIN_GROUP_MEMBERS, URL_ADMIN_USER_STORAGE, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_IDPS, URL_ADMIN_IDP, \ URL_ADMIN_IDP_MAPPERS, URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ @@ -348,6 +348,16 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_IDPS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + def delete_idp(self, idp_alias): + """ + Deletes ID Provider, + + :param: idp_alias: idp alias name + """ + params_path = {"realm-name": self.realm_name, "alias": idp_alias} + 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): """ Create a new user. Username must be unique diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 2c6fcfc..11187c1 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -79,6 +79,7 @@ URL_ADMIN_REALMS = "admin/realms" URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_IDP_MAPPERS = "admin/realms/{realm-name}/identity-provider/instances/{idp-alias}/mappers" +URL_ADMIN_IDP = "admin/realms//{realm-name}/identity-provider/instances/{alias}" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" From 0c0bec4e6668fcc44e6c25c654c4d7402027c803 Mon Sep 17 00:00:00 2001 From: andres Date: Wed, 23 Sep 2020 12:57:16 +0200 Subject: [PATCH 03/17] Added get_realm_roles_of_user --- keycloak/keycloak_admin.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..36c3cb8 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1015,6 +1015,11 @@ class KeycloakAdmin: data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + def get_realm_roles_of_user(self, user_id): + params_path = {"realm-name": self.realm_name, "id": user_id} + data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + def assign_group_realm_roles(self, group_id, roles): """ Assign realm roles to a group From c17fdaa3d8522a39a5c812d8e40e60d09ca367f5 Mon Sep 17 00:00:00 2001 From: andres Date: Wed, 23 Sep 2020 12:57:54 +0200 Subject: [PATCH 04/17] Added get_realm_roles_of_user --- keycloak/keycloak_admin.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 36c3cb8..40fa977 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1016,6 +1016,13 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) def get_realm_roles_of_user(self, user_id): + """ + Get all realm roles for a user. + + :param user_id: id of user + :return: Keycloak server response (array RoleRepresentation) + """ + params_path = {"realm-name": self.realm_name, "id": user_id} data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) From 28a2f4eb11f4a943d382b847ddde4d7864755315 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Weing=C3=A4rtner?= Date: Wed, 23 Sep 2020 22:14:26 -0300 Subject: [PATCH 05/17] Create add mapper to SP Client and delete mapper from scope methods --- keycloak/keycloak_admin.py | 38 +++++++++++++++++++++++++++++++++++++- keycloak/urls_patterns.py | 2 ++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..bfa6e55 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -43,7 +43,8 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ - URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES + URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, \ + URL_ADMIN_CLIENT_PROTOCOL_MAPPER, URL_ADMIN_CLIENT_SCOPES_MAPPERS class KeycloakAdmin: @@ -1269,6 +1270,41 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id): + """ + Delete a mapper from a client scope + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_delete_mapper + + :param client_scope_id: The id of the client scope + :param payload: ProtocolMapperRepresentation + :return: Keycloak server Response + """ + + params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id, + "protocol-mapper-id": protocol_mppaer_id} + + data_raw = self.raw_delete( + URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)) + + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + + def add_mapper_to_client(self, client_id, payload): + """ + Add a mapper to a client + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper + + :param client_id: The id of the client + :param payload: ProtocolMapperRepresentation + :return: Keycloak server Response + """ + + params_path = {"realm-name": self.realm_name, "id": client_id} + + data_raw = self.raw_post( + URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload)) + + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + def generate_client_secrets(self, client_id): """ diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 3e28de8..bc7fd72 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -69,10 +69,12 @@ URL_ADMIN_CLIENT_ROLE = URL_ADMIN_CLIENT + "/roles/{role-name}" URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT + "/authz/resource-server/settings" URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT + "/authz/resource-server/resource" URL_ADMIN_CLIENT_CERTS = URL_ADMIN_CLIENT + "/certificates/{attr}" +URL_ADMIN_CLIENT_PROTOCOL_MAPPER = URL_ADMIN_CLIENT + "/protocol-mappers/models" URL_ADMIN_CLIENT_SCOPES = "admin/realms/{realm-name}/client-scopes" URL_ADMIN_CLIENT_SCOPE = URL_ADMIN_CLIENT_SCOPES + "/{scope-id}" URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER = URL_ADMIN_CLIENT_SCOPE + "/protocol-mappers/models" +URL_ADMIN_CLIENT_SCOPES_MAPPERS = URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER + "/{protocol-mapper-id}" URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles" URL_ADMIN_REALMS = "admin/realms" From 6757373d29b8d0776e3ab9207c82421e0d4e8acf Mon Sep 17 00:00:00 2001 From: Bob van Luijt Date: Wed, 30 Sep 2020 15:59:22 +0200 Subject: [PATCH 06/17] Added user_realm_name to the docs --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 98af23d..4204c56 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,8 @@ from keycloak import KeycloakAdmin keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/", username='example-admin', password='secret', - realm_name="example_realm", + realm_name="master", + user_realm_name="only_if_other_realm_than_master", client_secret_key="client-secret", verify=True) From 80729ae3ccf0cf98ceef423f690ee0c60deaf624 Mon Sep 17 00:00:00 2001 From: Bergiu Date: Thu, 1 Oct 2020 13:53:14 +0200 Subject: [PATCH 07/17] fixed #104 --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98af23d..aaae5b4 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ count_users = keycloak_admin.users_count() users = keycloak_admin.get_users({}) # Get user ID from name -user-id-keycloak = keycloak_admin.get_user_id("example@example.com") +user_id_keycloak = keycloak_admin.get_user_id("example@example.com") # Get User user = keycloak_admin.get_user("user-id-keycloak") @@ -175,7 +175,7 @@ server_info = keycloak_admin.get_server_info() clients = keycloak_admin.get_clients() # Get client - id (not client-id) from client by name -client_id=keycloak_admin.get_client_id("my-client") +client_id = keycloak_admin.get_client_id("my-client") # Get representation of the client - id of client (not client-id) client = keycloak_admin.get_client(client_id="client_id") From 171d12a675590914d708c98ff66dafb067fe79bb Mon Sep 17 00:00:00 2001 From: "A. Shpak" Date: Tue, 20 Oct 2020 20:07:57 +0300 Subject: [PATCH 08/17] fix wrong param in example from readme --- README.md | 2 +- docs/source/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 98af23d..ea354b4 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ token_info = keycloak_openid.introspect(token['access_token'])) # Decode Token KEYCLOAK_PUBLIC_KEY = keycloak_openid.public_key() -options = {"verify_signature": True, "verify_aud": True, "exp": True} +options = {"verify_signature": True, "verify_aud": True, "verify_exp": True} token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options) # Get permissions by token diff --git a/docs/source/index.rst b/docs/source/index.rst index 2b41d35..0cd6e2f 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -132,7 +132,7 @@ Main methods:: # Decode Token KEYCLOAK_PUBLIC_KEY = "secret" - options = {"verify_signature": True, "verify_aud": True, "exp": True} + options = {"verify_signature": True, "verify_aud": True, "verify_exp": True} token_info = keycloak_openid.decode_token(token['access_token'], key=KEYCLOAK_PUBLIC_KEY, options=options) # Get permissions by token From 89225eff12b5e92b29b12f782accce6f8255c7d0 Mon Sep 17 00:00:00 2001 From: rpisani5c <55719539+rpisani5c@users.noreply.github.com> Date: Tue, 27 Oct 2020 12:33:44 -0500 Subject: [PATCH 09/17] Update keycloak_admin.py Swapped the names for get_group_client_roles & delete_group_client_roles respectively. Updated usage, removed the roles object build for the get_ method. --- keycloak/keycloak_admin.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..53ddab4 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1072,9 +1072,9 @@ class KeycloakAdmin: data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) - def delete_group_client_roles(self, group_id, client_id, roles): + def get_group_client_roles(self, group_id, client_id): """ - Delete client roles of a group + Get client roles of a group :param group_id: id of group :param client_id: id of client (not client-id) @@ -1082,14 +1082,13 @@ class KeycloakAdmin: :return Keycloak server response """ - payload = roles if isinstance(roles, list) else [roles] params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id} data_raw = self.raw_get(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) - def get_group_client_roles(self, group_id, client_id, roles): + def delete_group_client_roles(self, group_id, client_id, roles): """ - Get client roles of a group + Delete client roles of a group :param group_id: id of group :param client_id: id of client (not client-id) From 70b9efeaa30017208bd1b641637ff051142f6b21 Mon Sep 17 00:00:00 2001 From: rpisani5c <55719539+rpisani5c@users.noreply.github.com> Date: Tue, 27 Oct 2020 12:36:42 -0500 Subject: [PATCH 10/17] Update keycloak_admin.py Removed the roles param definition on the get_group_client_roles method. --- keycloak/keycloak_admin.py | 1 - 1 file changed, 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 53ddab4..6356cfe 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -1078,7 +1078,6 @@ class KeycloakAdmin: :param group_id: id of group :param client_id: id of client (not client-id) - :param roles: roles list or role (use GroupRoleRepresentation) :return Keycloak server response """ From 42ee704d58287008b4e31c384510f13e02abb0ff Mon Sep 17 00:00:00 2001 From: PGijsbers Date: Wed, 28 Oct 2020 12:29:21 +0200 Subject: [PATCH 11/17] Remove excess parenthesis --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98af23d..c808845 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ token_rpt_info = keycloak_openid.introspect(keycloak_openid.introspect(token['ac token_type_hint="requesting_party_token")) # Introspect Token -token_info = keycloak_openid.introspect(token['access_token'])) +token_info = keycloak_openid.introspect(token['access_token']) # Decode Token KEYCLOAK_PUBLIC_KEY = keycloak_openid.public_key() From 876640616b91d3983d203f8e4771dfd4f43a72bd Mon Sep 17 00:00:00 2001 From: faffeldt Date: Fri, 6 Nov 2020 10:13:52 +0100 Subject: [PATCH 12/17] Adds create_authentication_flow_subflow admin method --- keycloak/keycloak_admin.py | 20 +++++++++++++++++++- keycloak/urls_patterns.py | 2 +- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..f546a1e 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -43,7 +43,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ - URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES + URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, URL_ADMIN_FLOWS_SUBFLOWS class KeycloakAdmin: @@ -1211,6 +1211,24 @@ class KeycloakAdmin: data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): + """ + Create a new sub authentication flow for a given authentication flow + + AuthenticationFlowRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + + :param payload: AuthenticationFlowRepresentation + :param flow_alias: The flow alias + :param skip_exists: If true then do not raise an error if authentication flow already exists + :return: Keycloak server response (RoleRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} + data_raw = self.raw_post(URL_ADMIN_FLOWS_SUBFLOWS.format(**params_path), + data=payload) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + def sync_users(self, storage_id, action): """ Function to trigger user sync from provider diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 3e28de8..447d28e 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -80,9 +80,9 @@ URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" - URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" +URL_ADMIN_FLOWS_SUBFLOWS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/components/{component-id}" From 39aee83585b977c4c16512c9c8feaf16f7d85722 Mon Sep 17 00:00:00 2001 From: faffeldt Date: Fri, 6 Nov 2020 10:42:37 +0100 Subject: [PATCH 13/17] Adds create_authentication_flow_execution admin method --- keycloak/keycloak_admin.py | 22 ++++++++++++++++++++-- keycloak/urls_patterns.py | 3 ++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index f546a1e..b0c9917 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -43,7 +43,8 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \ URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ - URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, URL_ADMIN_FLOWS_SUBFLOWS + URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, \ + URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW class KeycloakAdmin: @@ -1210,6 +1211,23 @@ class KeycloakAdmin: data_raw = self.raw_put(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + + def create_authentication_flow_execution(self, payload, flow_alias): + """ + Create an authentication flow execution + + AuthenticationExecutionInfoRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation + + :param payload: AuthenticationExecutionInfoRepresentation + :param flow_alias: The flow alias + :return: Keycloak server response + """ + + 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) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False): """ @@ -1225,7 +1243,7 @@ class KeycloakAdmin: """ params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias} - data_raw = self.raw_post(URL_ADMIN_FLOWS_SUBFLOWS.format(**params_path), + data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 447d28e..ea10e45 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -82,7 +82,8 @@ URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" -URL_ADMIN_FLOWS_SUBFLOWS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" +URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/execution" +URL_ADMIN_FLOWS_EXECUTIONS_FLOW = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/components/{component-id}" From 8dfdddc5c64c5b8c4a42d4002ca3468727d38a8f Mon Sep 17 00:00:00 2001 From: faffeldt Date: Fri, 6 Nov 2020 11:47:03 +0100 Subject: [PATCH 14/17] Adds copy_authentication_flow admin method --- keycloak/keycloak_admin.py | 17 ++++++++++++++++- keycloak/urls_patterns.py | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index b0c9917..726bfa8 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -44,7 +44,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, \ - URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW + URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY class KeycloakAdmin: @@ -1184,6 +1184,21 @@ class KeycloakAdmin: data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + def copy_authentication_flow(self, payload, flow_alias): + """ + Copy existing authentication flow under a new name. The new name is given as 'newName' attribute of the passed payload. + + :param payload: JSON containing 'newName' attribute + :param flow_alias: the flow alias + :return: Keycloak server response (RoleRepresentation) + """ + + 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) + return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) + + def get_authentication_flow_executions(self, flow_alias): """ Get authentication flow executions. Returns all execution steps diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index ea10e45..a7bc39c 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -81,6 +81,7 @@ URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" +URL_ADMIN_FLOWS_COPY = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/copy" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/execution" URL_ADMIN_FLOWS_EXECUTIONS_FLOW = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/flow" From 6c62286adefdf9b6293b3ee23fbfcf4740db09df Mon Sep 17 00:00:00 2001 From: faffeldt Date: Fri, 6 Nov 2020 11:55:31 +0100 Subject: [PATCH 15/17] Adds get_authentication_flow_for_id admin method --- keycloak/keycloak_admin.py | 18 ++++++++++++++++-- keycloak/urls_patterns.py | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 726bfa8..a8c17f2 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -44,7 +44,8 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \ URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, \ - URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY + URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY, \ + URL_ADMIN_FLOWS_ALIAS class KeycloakAdmin: @@ -1166,6 +1167,20 @@ class KeycloakAdmin: params_path = {"realm-name": self.realm_name} data_raw = self.raw_get(URL_ADMIN_FLOWS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + + def get_authentication_flow_for_id(self, flow_id): + """ + Get one authentication flow by it's id/alias. Returns all flow details + + AuthenticationFlowRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation + + :param flow_id: the id of a flow NOT it's alias + :return: Keycloak server response (AuthenticationFlowRepresentation) + """ + params_path = {"realm-name": self.realm_name, "flow-id": flow_id} + data_raw = self.raw_get(URL_ADMIN_FLOWS_ALIAS.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) def create_authentication_flow(self, payload, skip_exists=False): """ @@ -1198,7 +1213,6 @@ class KeycloakAdmin: data=payload) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201]) - def get_authentication_flow_executions(self, flow_alias): """ Get authentication flow executions. Returns all execution steps diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index a7bc39c..2ea23fd 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -81,6 +81,7 @@ URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" +URL_ADMIN_FLOWS_ALIAS = "admin/realms/{realm-name}/authentication/flows/{flow-id}" URL_ADMIN_FLOWS_COPY = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/copy" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions/execution" From 4624c7b2dce0869dc4d6b2396086f82eca420a5b Mon Sep 17 00:00:00 2001 From: Tamara Nocentini Date: Wed, 11 Nov 2020 14:32:18 +0100 Subject: [PATCH 16/17] Manage composite realm roles of the realm role Signed-off-by: Tamara Nocentini Signed-off-by: Paolo Romolini --- keycloak/keycloak_admin.py | 52 +++++++++++++++++++++++++++++++++++++- keycloak/urls_patterns.py | 1 + 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index ae443b6..81201cd 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -29,7 +29,10 @@ from builtins import isinstance from typing import List, Iterable from keycloak.urls_patterns import URL_ADMIN_GROUPS_REALM_ROLES, \ - URL_ADMIN_GET_GROUPS_REALM_ROLES, URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, URL_ADMIN_GROUPS_CLIENT_ROLES + URL_ADMIN_GET_GROUPS_REALM_ROLES, URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, URL_ADMIN_GROUPS_CLIENT_ROLES, \ + URL_ADMIN_GET_GROUPS_REALM_ROLES, URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, \ + URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE + from .connection import ConnectionManager from .exceptions import raise_error_from_response, KeycloakGetError from .keycloak_openid import KeycloakOpenID @@ -999,6 +1002,53 @@ class KeycloakAdmin: URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204]) + def add_composite_realm_roles_to_role(self, role_name, roles): + """ + Add composite roles to the role + + :param role_name: The name of the role + :param roles: roles list or role (use RoleRepresentation) to be updated + :return Keycloak server response + """ + + payload = roles if isinstance(roles, list) else [roles] + params_path = {"realm-name": self.realm_name, "role-name": role_name} + data_raw = self.raw_post( + URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, + expected_codes=[204]) + + def remove_composite_realm_roles_to_role(self, role_name, roles): + """ + Remove composite roles from the role + + :param role_name: The name of the role + :param roles: roles list or role (use RoleRepresentation) to be removed + :return Keycloak server response + """ + + payload = roles if isinstance(roles, list) else [roles] + params_path = {"realm-name": self.realm_name, "role-name": role_name} + data_raw = self.raw_delete( + URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, + expected_codes=[204]) + + def get_composite_realm_roles_of_role(self, role_name): + """ + Get composite roles of the role + + :param role_name: The name of the role + :return Keycloak server response (array RoleRepresentation) + """ + + params_path = {"realm-name": self.realm_name, "role-name": role_name} + data_raw = self.raw_get( + URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + def assign_realm_roles(self, user_id, client_id, roles): """ Assign realm roles to a user diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 3e28de8..78cf34c 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -79,6 +79,7 @@ URL_ADMIN_REALMS = "admin/realms" URL_ADMIN_REALM = "admin/realms/{realm-name}" URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_REALM_ROLES_ROLE_BY_NAME = "admin/realms/{realm-name}/roles/{role-name}" +URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE = "admin/realms/{realm-name}/roles/{role-name}/composites" URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" From 05b3a276543341b647f16b82fcc83d9c0bbae217 Mon Sep 17 00:00:00 2001 From: Paolo Romolini Date: Fri, 13 Nov 2020 16:11:28 +0100 Subject: [PATCH 17/17] Add "Get a role by name" API Signed-off-by: Paolo Romolini --- keycloak/keycloak_admin.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 81201cd..f98ca84 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -977,6 +977,19 @@ class KeycloakAdmin: data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists) + def get_realm_role(self, role_name): + """ + Get realm role by role name + :param role_name: role's name, not id! + + RoleRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation + :return: role_id + """ + params_path = {"realm-name": self.realm_name, "role-name": role_name} + data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + def update_realm_role(self, role_name, payload): """ Update a role for the realm by name