From 057ab585c2a0f74300a9cf0e02da41483eb69158 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Wed, 28 Dec 2022 23:57:40 +0200 Subject: [PATCH 01/12] feat(api): add api url --- src/keycloak/urls_patterns.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/keycloak/urls_patterns.py b/src/keycloak/urls_patterns.py index b5f3277..7c15984 100644 --- a/src/keycloak/urls_patterns.py +++ b/src/keycloak/urls_patterns.py @@ -118,6 +118,10 @@ URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES = ( URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION = ( URL_ADMIN_CLIENT + "/authz/resource-server/permission/scope/{scope-id}" ) +URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION = ( + URL_ADMIN_CLIENT + "/authz/resource-server/permission/scope?max=-1" +) + URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY = URL_ADMIN_CLIENT + "/authz/resource-server/policy/client" URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER = URL_ADMIN_CLIENT + "/service-account-user" From b039100f38cbb749c9ebc4a732b363aa58d2354f Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Wed, 28 Dec 2022 23:58:05 +0200 Subject: [PATCH 02/12] feat(api): add create permission function --- src/keycloak/keycloak_admin.py | 46 ++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index ac1f46e..8028924 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -676,7 +676,7 @@ class KeycloakAdmin: ) raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def users_count(self, query=None): """Count users. @@ -1196,7 +1196,7 @@ class KeycloakAdmin: ) try: _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 except KeyError: return @@ -1547,7 +1547,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def update_client(self, client_id, payload): """Update a client. @@ -1725,7 +1725,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): """Add composite roles to client role. @@ -1866,7 +1866,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def get_realm_role(self, role_name): """Get realm role by role name. @@ -2722,7 +2722,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def update_client_scope(self, client_scope_id, payload): """Update a client scope. @@ -3087,7 +3087,7 @@ class KeycloakAdmin: ) raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 def get_component(self, component_id): """Get representation of the component. @@ -3460,6 +3460,38 @@ class KeycloakAdmin: ) return raise_error_from_response(data_raw, KeycloakGetError) + def create_client_authz_scope_permission(self, payload, client_id): + """create permissions for a authz scope. + + Payload example:: + + payload={ + "name": "My Permission Name", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "resources": [some_resource_id], + "scopes": [some_scope_id], + "policies": [some_policy_id], + } + + :param payload: No Document + :type payload: dict + :param client_id: id in ClientRepresentation + https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation + :type client_id: str + :param scope_id: No Document + :type scope_id: str + :return: Keycloak server response + :rtype: bytes + """ + params_path = {"realm-name": self.realm_name, "id": client_id} + data_raw = self.raw_post( + urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path), + data=json.dumps(payload), + ) + return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201]) + def update_client_authz_scope_permission(self, payload, client_id, scope_id): """Update permissions for a given scope. From 99089c190b2088a3fd599a0ef168c880b952d374 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Wed, 28 Dec 2022 23:58:30 +0200 Subject: [PATCH 03/12] feat(api): add testcases for create permission function --- tests/test_keycloak_admin.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1420c56..cac47de 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1564,6 +1564,26 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): scope_id=token_exchange_permission_id, ) + # Create permissions on the target client to reference this policy + res = admin.create_client_authz_scope_permission( + payload={ + "name": "test-permission", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "resources": [token_exchange_resource_id], + "scopes": [token_exchange_scope_id], + "policies": [client_policy_id], + }, + client_id="realm_management_id", + ) + with pytest.raises(KeycloakPostError) as err: + admin.create_client_scope(payload={"name": "test-scope"}) + assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') + permission_name = admin.get_client_authz_scope_permission( + client_id=realm_management_id)["name"] + assert permission_name == "test-permission" + def test_email(admin: KeycloakAdmin, user: str): """Test email. From 9288470c605b8ce71e7baccd1bf117dd03b8b913 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Thu, 29 Dec 2022 00:13:01 +0200 Subject: [PATCH 04/12] fix: linting --- src/keycloak/keycloak_admin.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 8028924..8f6f7d8 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -676,7 +676,7 @@ class KeycloakAdmin: ) raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def users_count(self, query=None): """Count users. @@ -1196,7 +1196,7 @@ class KeycloakAdmin: ) try: _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 except KeyError: return @@ -1547,7 +1547,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client(self, client_id, payload): """Update a client. @@ -1725,7 +1725,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def add_composite_client_roles_to_role(self, client_role_id, role_name, roles): """Add composite roles to client role. @@ -1866,7 +1866,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_realm_role(self, role_name): """Get realm role by role name. @@ -2722,7 +2722,7 @@ class KeycloakAdmin: data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists ) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def update_client_scope(self, client_scope_id, payload): """Update a client scope. @@ -3087,7 +3087,7 @@ class KeycloakAdmin: ) raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201]) _last_slash_idx = data_raw.headers["Location"].rindex("/") - return data_raw.headers["Location"][_last_slash_idx + 1:] # noqa: E203 + return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203 def get_component(self, component_id): """Get representation of the component. @@ -3461,7 +3461,7 @@ class KeycloakAdmin: return raise_error_from_response(data_raw, KeycloakGetError) def create_client_authz_scope_permission(self, payload, client_id): - """create permissions for a authz scope. + """Create permissions for a authz scope. Payload example:: @@ -3480,8 +3480,6 @@ class KeycloakAdmin: :param client_id: id in ClientRepresentation https://www.keycloak.org/docs-api/18.0/rest-api/index.html#_clientrepresentation :type client_id: str - :param scope_id: No Document - :type scope_id: str :return: Keycloak server response :rtype: bytes """ From 2d39fac7abf100cf5c8ec9fcceed5ec00cb65deb Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Thu, 29 Dec 2022 00:13:17 +0200 Subject: [PATCH 05/12] fix: linting --- tests/test_keycloak_admin.py | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index cac47de..1420c56 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1564,26 +1564,6 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): scope_id=token_exchange_permission_id, ) - # Create permissions on the target client to reference this policy - res = admin.create_client_authz_scope_permission( - payload={ - "name": "test-permission", - "type": "scope", - "logic": "POSITIVE", - "decisionStrategy": "UNANIMOUS", - "resources": [token_exchange_resource_id], - "scopes": [token_exchange_scope_id], - "policies": [client_policy_id], - }, - client_id="realm_management_id", - ) - with pytest.raises(KeycloakPostError) as err: - admin.create_client_scope(payload={"name": "test-scope"}) - assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') - permission_name = admin.get_client_authz_scope_permission( - client_id=realm_management_id)["name"] - assert permission_name == "test-permission" - def test_email(admin: KeycloakAdmin, user: str): """Test email. From 166fb8f9466afd35cee2fa9224541fe94829be4a Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 17:22:59 +0200 Subject: [PATCH 06/12] feat(api): add testcases for create permission function --- tests/test_keycloak_admin.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1420c56..1313baa 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1564,6 +1564,25 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): scope_id=token_exchange_permission_id, ) +# Create permissions on the target client to reference this policy + res = admin.create_client_authz_scope_permission( + payload={ + "name": "test-permission", + "type": "scope", + "logic": "POSITIVE", + "decisionStrategy": "UNANIMOUS", + "resources": [token_exchange_resource_id], + "scopes": [token_exchange_scope_id], + "policies": [client_policy_id], + }, + client_id="realm_management_id", + ) + with pytest.raises(KeycloakPostError) as err: + admin.create_client_scope(payload={"name": "test-scope"}) + assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') + permission_name = admin.get_client_authz_scope_permission( + client_id=realm_management_id)["name"] + assert permission_name == "test-permission" def test_email(admin: KeycloakAdmin, user: str): """Test email. From 483c4915315726a27592ec44c3359bbe08aae84f Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Wed, 28 Dec 2022 23:58:30 +0200 Subject: [PATCH 07/12] feat(api): add testcases for create permission function --- tests/test_keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 1313baa..64365c3 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1564,7 +1564,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): scope_id=token_exchange_permission_id, ) -# Create permissions on the target client to reference this policy + # Create permissions on the target client to reference this policy res = admin.create_client_authz_scope_permission( payload={ "name": "test-permission", From 13facfdf2af2f5914f8125551ebadc822db8b8dc Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 17:37:29 +0200 Subject: [PATCH 08/12] feat(api): apply formating --- tests/test_keycloak_admin.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 64365c3..0db7d3e 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1565,7 +1565,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): ) # Create permissions on the target client to reference this policy - res = admin.create_client_authz_scope_permission( + admin.create_client_authz_scope_permission( payload={ "name": "test-permission", "type": "scope", @@ -1580,10 +1580,12 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): with pytest.raises(KeycloakPostError) as err: admin.create_client_scope(payload={"name": "test-scope"}) assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') - permission_name = admin.get_client_authz_scope_permission( - client_id=realm_management_id)["name"] + permission_name = admin.get_client_authz_scope_permission(client_id=realm_management_id)[ + "name" + ] assert permission_name == "test-permission" + def test_email(admin: KeycloakAdmin, user: str): """Test email. From b314c51ee5b5ee1d47a03bc4383f051f0c434003 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 17:55:02 +0200 Subject: [PATCH 09/12] feat(api): fix testing --- tests/test_keycloak_admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 0db7d3e..87b71a1 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1575,7 +1575,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): "scopes": [token_exchange_scope_id], "policies": [client_policy_id], }, - client_id="realm_management_id", + client_id=realm_management_id, ) with pytest.raises(KeycloakPostError) as err: admin.create_client_scope(payload={"name": "test-scope"}) From f901fe3312fa12a3702fdf6c720cd6ae4d998213 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 18:03:13 +0200 Subject: [PATCH 10/12] feat(api): fix testing --- tests/test_keycloak_admin.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 87b71a1..ad0feda 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1565,7 +1565,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): ) # Create permissions on the target client to reference this policy - admin.create_client_authz_scope_permission( + res = admin.create_client_authz_scope_permission( payload={ "name": "test-permission", "type": "scope", @@ -1577,8 +1577,11 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): }, client_id=realm_management_id, ) + assert res with pytest.raises(KeycloakPostError) as err: - admin.create_client_scope(payload={"name": "test-scope"}) + admin.create_client_authz_scope_permission( + payload={"name": "test-scope"}, client_id="realm_management_id" + ) assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') permission_name = admin.get_client_authz_scope_permission(client_id=realm_management_id)[ "name" From c9478d9f1a0b7739d408c0f467a1e0dbcc97754d Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 18:26:24 +0200 Subject: [PATCH 11/12] feat(api): fix testing for create client_authz_scope_permission --- tests/test_keycloak_admin.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index ad0feda..63e1d6c 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1565,7 +1565,7 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): ) # Create permissions on the target client to reference this policy - res = admin.create_client_authz_scope_permission( + admin.create_client_authz_scope_permission( payload={ "name": "test-permission", "type": "scope", @@ -1577,16 +1577,16 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): }, client_id=realm_management_id, ) - assert res - with pytest.raises(KeycloakPostError) as err: - admin.create_client_authz_scope_permission( - payload={"name": "test-scope"}, client_id="realm_management_id" - ) - assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') permission_name = admin.get_client_authz_scope_permission(client_id=realm_management_id)[ "name" ] assert permission_name == "test-permission" + with pytest.raises(KeycloakPostError) as err: + admin.create_client_authz_scope_permission( + payload={"name": "test-permission", "scopes": [token_exchange_scope_id]}, + client_id="realm_management_id", + ) + assert err.match('404: b\'{"errorMessage":"Could not find client"}\'') def test_email(admin: KeycloakAdmin, user: str): From cb77bb74333c0c8c8b032fe9164c8c43395621a2 Mon Sep 17 00:00:00 2001 From: Hadeer-Elsaeed Date: Mon, 2 Jan 2023 18:38:19 +0200 Subject: [PATCH 12/12] feat(api): add scope id for get client_authz_scope_permission --- tests/test_keycloak_admin.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_keycloak_admin.py b/tests/test_keycloak_admin.py index 63e1d6c..2425184 100644 --- a/tests/test_keycloak_admin.py +++ b/tests/test_keycloak_admin.py @@ -1577,9 +1577,9 @@ def test_enable_token_exchange(admin: KeycloakAdmin, realm: str): }, client_id=realm_management_id, ) - permission_name = admin.get_client_authz_scope_permission(client_id=realm_management_id)[ - "name" - ] + permission_name = admin.get_client_authz_scope_permission( + client_id=realm_management_id, scope_id=token_exchange_scope_id + )["name"] assert permission_name == "test-permission" with pytest.raises(KeycloakPostError) as err: admin.create_client_authz_scope_permission(