From e03a1ba9bec4ad9fb7c13db9c5c2030a03ec2b9b Mon Sep 17 00:00:00 2001 From: Romain Philibert Date: Thu, 30 Apr 2020 16:51:45 +0200 Subject: [PATCH 1/4] feat: add components --- docs/source/index.rst | 20 +++++++++++ keycloak/keycloak_admin.py | 74 +++++++++++++++++++++++++++++++++++++- keycloak/urls_patterns.py | 3 ++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index 597cfb2..aeb8744 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -276,3 +276,23 @@ Main methods:: # Function to trigger user sync from provider sync_users(storage_id="storage_di", action="action") + + # Rotate RSA realm keys + # List existing rsa keys + components = keycloak_admin.get_components(query={"parent":"example_realm", "type":"org.keycloak.keys.KeyProvider"}) + components_rsa_generated = list(filter(lambda component: component["provider-id"] == "rsa-generated")) + + # Create a new one + keycloak_admin.create_component({"name":"rsa-generated","providerId":"rsa-generated","providerType":"org.keycloak.keys.KeyProvider","parentId":"example_realm","config":{"priority":["100"],"enabled":["true"],"active":["true"],"algorithm":["RS256"],"keySize":["2048"]}}) + + for component in components_rsa_generated: + component_details = keycloak_admin.get_component(component['id']) + + # Delete inactive keys + if component_details['config']['active'] == ["false"]: + keycloak_admin.delete_component(component['id']) + + # Make previous keys inactive + else: + component_details['config']['active'] = ["false"] + keycloak_admin.update_component(component['id'], component_details) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 0bcf28f..c0d8ab8 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -42,7 +42,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC 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_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ - URL_ADMIN_USER_REALM_ROLES + URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT class KeycloakAdmin: @@ -1174,6 +1174,78 @@ class KeycloakAdmin: data_raw = self.raw_get(URL_ADMIN_CLIENT_SECRETS.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) + def get_components(self, query=None): + """ + Return a list of components, filtered according to query parameters + + ComponentRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + + :param query: Query parameters (optional) + :return: components list + """ + params_path = {"realm-name": self.realm_name} + return self.__fetch_all(URL_ADMIN_COMPONENTS.format(**params_path), query) + + def create_component(self, payload): + """ + Create a new component. + + ComponentRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + + :param payload: ComponentRepresentation + + :return: UserRepresentation + """ + params_path = {"realm-name": self.realm_name} + + data_raw = self.raw_post(URL_ADMIN_COMPONENTS.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists) + + def get_component(self, component_id): + """ + Get representation of the component + + :param component_id: Component id + + ComponentRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + + :return: ComponentRepresentation + """ + params_path = {"realm-name": self.realm_name, "id": component_id} + data_raw = self.raw_get(URL_ADMIN_COMPONENT.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError) + + def update_component(self, component_id, payload): + """ + Update the component + + :param component_id: Component id + :param payload: ComponentRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation + + :return: Http response + """ + params_path = {"realm-name": self.realm_name, "id": component_id} + data_raw = self.raw_put(URL_ADMIN_COMPONENT.format(**params_path), + data=json.dumps(payload)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + + def delete_component(self, component_id): + """ + Delete the component + + :param component_id: Component id + + :return: Http response + """ + params_path = {"realm-name": self.realm_name, "id": component_id} + data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path)) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + def raw_get(self, *args, **kwargs): """ diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 19ca28f..809e1f1 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -79,3 +79,6 @@ URL_ADMIN_IDPS = "admin/realms/{realm-name}/identity-provider/instances" URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" + +URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" +URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/component/{component-id}" From 77d5325c07ed2f7b103c758afb17ae2a789c000a Mon Sep 17 00:00:00 2001 From: Romain Philibert Date: Thu, 30 Apr 2020 19:01:15 +0200 Subject: [PATCH 2/4] fixes + add get_keys --- docs/source/index.rst | 26 +++++++++++--------------- keycloak/keycloak_admin.py | 30 +++++++++++++++++++++++------- keycloak/urls_patterns.py | 3 ++- 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index aeb8744..2b41d35 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -277,22 +277,18 @@ Main methods:: # Function to trigger user sync from provider sync_users(storage_id="storage_di", action="action") - # Rotate RSA realm keys - # List existing rsa keys - components = keycloak_admin.get_components(query={"parent":"example_realm", "type":"org.keycloak.keys.KeyProvider"}) - components_rsa_generated = list(filter(lambda component: component["provider-id"] == "rsa-generated")) + # List public RSA keys + components = keycloak_admin.keys - # Create a new one - keycloak_admin.create_component({"name":"rsa-generated","providerId":"rsa-generated","providerType":"org.keycloak.keys.KeyProvider","parentId":"example_realm","config":{"priority":["100"],"enabled":["true"],"active":["true"],"algorithm":["RS256"],"keySize":["2048"]}}) + # List all keys + components = keycloak_admin.get_components(query={"parent":"example_realm", "type":"org.keycloak.keys.KeyProvider"}) - for component in components_rsa_generated: - component_details = keycloak_admin.get_component(component['id']) + # Create a new RSA key + component = keycloak_admin.create_component({"name":"rsa-generated","providerId":"rsa-generated","providerType":"org.keycloak.keys.KeyProvider","parentId":"example_realm","config":{"priority":["100"],"enabled":["true"],"active":["true"],"algorithm":["RS256"],"keySize":["2048"]}}) - # Delete inactive keys - if component_details['config']['active'] == ["false"]: - keycloak_admin.delete_component(component['id']) + # Update the key + component_details['config']['active'] = ["false"] + keycloak_admin.update_component(component['id']) - # Make previous keys inactive - else: - component_details['config']['active'] = ["false"] - keycloak_admin.update_component(component['id'], component_details) + # Delete the key + keycloak_admin.delete_component(component['id']) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index c0d8ab8..36dca80 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -41,8 +41,8 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC 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_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ - URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT + URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, + URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS class KeycloakAdmin: @@ -1185,7 +1185,9 @@ class KeycloakAdmin: :return: components list """ params_path = {"realm-name": self.realm_name} - return self.__fetch_all(URL_ADMIN_COMPONENTS.format(**params_path), query) + data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path), + data=None, **query) + return raise_error_from_response(data_raw, KeycloakGetError) def create_component(self, payload): """ @@ -1202,7 +1204,7 @@ class KeycloakAdmin: data_raw = self.raw_post(URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload)) - return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists) + return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201) def get_component(self, component_id): """ @@ -1215,7 +1217,7 @@ class KeycloakAdmin: :return: ComponentRepresentation """ - params_path = {"realm-name": self.realm_name, "id": component_id} + params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_get(URL_ADMIN_COMPONENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError) @@ -1229,7 +1231,7 @@ class KeycloakAdmin: :return: Http response """ - params_path = {"realm-name": self.realm_name, "id": component_id} + params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_put(URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload)) return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) @@ -1242,10 +1244,24 @@ class KeycloakAdmin: :return: Http response """ - params_path = {"realm-name": self.realm_name, "id": component_id} + params_path = {"realm-name": self.realm_name, "component-id": component_id} data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path)) return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204) + def get_keys(self): + """ + Return a list of keys, filtered according to query parameters + + KeysMetadataRepresentation + https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_key_resource + + :return: keys list + """ + params_path = {"realm-name": self.realm_name} + data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path), + data=None) + return raise_error_from_response(data_raw, KeycloakGetError) + def raw_get(self, *args, **kwargs): """ diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 809e1f1..8cf764b 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -81,4 +81,5 @@ URL_ADMIN_FLOWS = "admin/realms/{realm-name}/authentication/flows" URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{flow-alias}/executions" URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" -URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/component/{component-id}" +URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/components/{component-id}" +URL_ADMIN_KEYS = "admin/realms/{realm-name}/components/keys" From b1ef1d3dfda04301f64466fbd959ed4892607766 Mon Sep 17 00:00:00 2001 From: Romain Philibert Date: Sun, 3 May 2020 22:25:47 +0200 Subject: [PATCH 3/4] fix indent --- keycloak/keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak/keycloak_admin.py b/keycloak/keycloak_admin.py index 36dca80..ae66d53 100644 --- a/keycloak/keycloak_admin.py +++ b/keycloak/keycloak_admin.py @@ -41,7 +41,7 @@ from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURC 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_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, + URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \ URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS From 24fc50fae89924e3e1b0c69399bbd90f2b15e977 Mon Sep 17 00:00:00 2001 From: Romain Philibert Date: Sun, 3 May 2020 22:31:06 +0200 Subject: [PATCH 4/4] fix keys url --- keycloak/urls_patterns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak/urls_patterns.py b/keycloak/urls_patterns.py index 8cf764b..95fc345 100644 --- a/keycloak/urls_patterns.py +++ b/keycloak/urls_patterns.py @@ -82,4 +82,4 @@ URL_ADMIN_FLOWS_EXECUTIONS = "admin/realms/{realm-name}/authentication/flows/{fl URL_ADMIN_COMPONENTS = "admin/realms/{realm-name}/components" URL_ADMIN_COMPONENT = "admin/realms/{realm-name}/components/{component-id}" -URL_ADMIN_KEYS = "admin/realms/{realm-name}/components/keys" +URL_ADMIN_KEYS = "admin/realms/{realm-name}/keys"