Browse Source

fix(moved-query-params-out-of-url-patterns): query params in url patterns break with httpx later versions, the query parameters are therefore explicitly set on each request

BREAKING CHANGE: removed a couple of unused url patterns
pull/626/head
Richard Nemeth 4 months ago
parent
commit
dfe7517807
No known key found for this signature in database GPG Key ID: 21C39470DF3DEC39
  1. 10
      poetry.lock
  2. 8
      pyproject.toml
  3. 86
      src/keycloak/keycloak_admin.py
  4. 23
      src/keycloak/urls_patterns.py
  5. 2
      tests/test_keycloak_admin.py

10
poetry.lock

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "alabaster"
@ -49,13 +49,13 @@ test = ["coverage", "mypy", "pexpect", "ruff", "wheel"]
[[package]]
name = "astroid"
version = "3.3.5"
version = "3.3.6"
description = "An abstract syntax tree for Python with inference support."
optional = false
python-versions = ">=3.9.0"
files = [
{file = "astroid-3.3.5-py3-none-any.whl", hash = "sha256:a9d1c946ada25098d790e079ba2a1b112157278f3fb7e718ae6a9252f5835dc8"},
{file = "astroid-3.3.5.tar.gz", hash = "sha256:5cfc40ae9f68311075d27ef68a4841bdc5cc7f6cf86671b49f00607d30188e2d"},
{file = "astroid-3.3.6-py3-none-any.whl", hash = "sha256:db676dc4f3ae6bfe31cda227dc60e03438378d7a896aec57422c95634e8d722f"},
{file = "astroid-3.3.6.tar.gz", hash = "sha256:6aaea045f938c735ead292204afdb977a36e989522b7833ef6fea94de743f442"},
]
[package.dependencies]
@ -2153,4 +2153,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<4.0"
content-hash = "91b8284a9bc0213fd77fc49cf049ee2f02062cd13d1fa0d52ea711ae400dea5f"
content-hash = "b4e07d4e56d1bc47fe5d3ac0312deaebbef6a5a358688c32a7b3f8466afd5d18"

8
pyproject.toml

@ -4,12 +4,12 @@ version = "0.0.0"
description = "python-keycloak is a Python package providing access to the Keycloak API."
license = "MIT"
readme = "README.md"
keywords = [ "keycloak", "openid", "oidc" ]
keywords = ["keycloak", "openid", "oidc"]
authors = [
"Marcos Pereira <marcospereira.mpj@gmail.com>",
"Richard Nemeth <ryshoooo@gmail.com>"
"Richard Nemeth <ryshoooo@gmail.com>",
]
classifiers=[
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Development Status :: 3 - Alpha",
@ -35,7 +35,7 @@ requests = ">=2.20.0"
requests-toolbelt = ">=0.6.0"
deprecation = ">=2.1.0"
jwcrypto = ">=1.5.4"
httpx = ">=0.28.0"
httpx = ">=0.23.2"
async-property = ">=0.2.2"
[tool.poetry.group.docs.dependencies]

86
src/keycloak/keycloak_admin.py

@ -298,7 +298,10 @@ class KeycloakAdmin:
"export-groups-and-roles": export_groups_and_role,
}
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data=""
urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path),
data="",
exportClients=export_clients,
exportGroupsAndRoles=export_groups_and_role,
)
return raise_error_from_response(data_raw, KeycloakPostError)
@ -1334,9 +1337,9 @@ class KeycloakAdmin:
:return: client_id (uuid as string)
:rtype: str
"""
params_path = {"realm-name": self.connection.realm_name, "client-id": client_id}
params_path = {"realm-name": self.connection.realm_name}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENTS_CLIENT_ID.format(**params_path)
urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), clientId=client_id
)
data_response = raise_error_from_response(data_raw, KeycloakGetError)
@ -1381,6 +1384,7 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -1453,7 +1457,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -1513,6 +1517,7 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -1550,6 +1555,8 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
data=json.dumps(payload),
max=-1,
permission=False,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -1589,6 +1596,7 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -1605,7 +1613,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -1625,6 +1633,7 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
@ -1639,7 +1648,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -1654,7 +1663,9 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
max=-1,
permission=False,
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -1971,19 +1982,15 @@ class KeycloakAdmin:
:return: Keycloak server response (RoleRepresentation)
:rtype: list
"""
url = urls_patterns.URL_ADMIN_REALM_ROLES
params_path = {"realm-name": self.connection.realm_name}
params = {"briefRepresentation": brief_representation}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
)
# set the search_text path param, if it is a valid string
if search_text is not None and search_text.strip() != "":
params_path["search-text"] = search_text
url = urls_patterns.URL_ADMIN_REALM_ROLES_SEARCH
params["search"] = search_text
data_raw = self.connection.raw_get(url.format(**params_path), **params)
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError)
def get_realm_role_groups(self, role_name, query=None, brief_representation=True):
@ -3669,10 +3676,7 @@ class KeycloakAdmin:
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
}
params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
data_raw = self.connection.raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS.format(**params_path)
)
@ -4122,6 +4126,7 @@ class KeycloakAdmin:
data_raw = self.connection.raw_post(
urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
@ -4584,7 +4589,10 @@ class KeycloakAdmin:
"export-groups-and-roles": export_groups_and_role,
}
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data=""
urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path),
data="",
exportClients=export_clients,
exportGroupsAndRoles=export_groups_and_role,
)
return raise_error_from_response(data_raw, KeycloakPostError)
@ -5646,9 +5654,9 @@ class KeycloakAdmin:
:return: client_id (uuid as string)
:rtype: str
"""
params_path = {"realm-name": self.connection.realm_name, "client-id": client_id}
params_path = {"realm-name": self.connection.realm_name}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENTS_CLIENT_ID.format(**params_path)
urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), clientId=client_id
)
data_response = raise_error_from_response(data_raw, KeycloakGetError)
@ -5693,6 +5701,7 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -5765,7 +5774,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -5825,6 +5834,7 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -5862,6 +5872,8 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
data=json.dumps(payload),
max=-1,
permission=False,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -5903,6 +5915,7 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(
data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
@ -5919,7 +5932,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -5939,6 +5952,7 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
@ -5953,7 +5967,7 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path), max=-1
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -5968,7 +5982,9 @@ class KeycloakAdmin:
"""
params_path = {"realm-name": self.connection.realm_name, "id": client_id}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)
urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
max=-1,
permission=False,
)
return raise_error_from_response(data_raw, KeycloakGetError)
@ -6287,19 +6303,15 @@ class KeycloakAdmin:
:return: Keycloak server response (RoleRepresentation)
:rtype: list
"""
url = urls_patterns.URL_ADMIN_REALM_ROLES
params_path = {"realm-name": self.connection.realm_name}
params = {"briefRepresentation": brief_representation}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
)
# set the search_text path param, if it is a valid string
if search_text is not None and search_text.strip() != "":
params_path["search-text"] = search_text
url = urls_patterns.URL_ADMIN_REALM_ROLES_SEARCH
params["search"] = search_text
data_raw = await self.connection.a_raw_get(url.format(**params_path), **params)
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
)
return raise_error_from_response(data_raw, KeycloakGetError)
async def a_get_realm_role_groups(self, role_name, query=None, brief_representation=True):
@ -7998,10 +8010,7 @@ class KeycloakAdmin:
:return: Keycloak server response (array RoleRepresentation)
:rtype: dict
"""
params_path = {
"realm-name": self.connection.realm_name,
"scope-id": client_scope_id,
}
params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
data_raw = await self.connection.a_raw_get(
urls_patterns.URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS.format(**params_path)
)
@ -8453,6 +8462,7 @@ class KeycloakAdmin:
data_raw = await self.connection.a_raw_post(
urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
data=json.dumps(payload),
max=-1,
)
return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])

23
src/keycloak/urls_patterns.py

@ -92,7 +92,6 @@ URL_ADMIN_GROUP_MEMBERS = "admin/realms/{realm-name}/groups/{id}/members"
URL_ADMIN_CLIENT_INITIAL_ACCESS = "admin/realms/{realm-name}/clients-initial-access"
URL_ADMIN_CLIENTS = "admin/realms/{realm-name}/clients"
URL_ADMIN_CLIENT = URL_ADMIN_CLIENTS + "/{id}"
URL_ADMIN_CLIENTS_CLIENT_ID = URL_ADMIN_CLIENTS + "?clientId={client-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"
@ -117,14 +116,12 @@ URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE = (
URL_ADMIN_CLIENT_AUTHZ = URL_ADMIN_CLIENT + "/authz/resource-server"
URL_ADMIN_CLIENT_AUTHZ_SETTINGS = URL_ADMIN_CLIENT_AUTHZ + "/settings"
URL_ADMIN_CLIENT_AUTHZ_RESOURCE = URL_ADMIN_CLIENT_AUTHZ + "/resource/{resource-id}"
URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT_AUTHZ + "/resource?max=-1"
URL_ADMIN_CLIENT_AUTHZ_SCOPES = URL_ADMIN_CLIENT_AUTHZ + "/scope?max=-1"
URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS = URL_ADMIN_CLIENT_AUTHZ + "/permission?max=-1"
URL_ADMIN_CLIENT_AUTHZ_POLICIES = URL_ADMIN_CLIENT_AUTHZ + "/policy?max=-1&permission=false"
URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/role?max=-1"
URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = (
URL_ADMIN_CLIENT_AUTHZ + "/permission/resource?max=-1"
)
URL_ADMIN_CLIENT_AUTHZ_RESOURCES = URL_ADMIN_CLIENT_AUTHZ + "/resource"
URL_ADMIN_CLIENT_AUTHZ_SCOPES = URL_ADMIN_CLIENT_AUTHZ + "/scope"
URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS = URL_ADMIN_CLIENT_AUTHZ + "/permission"
URL_ADMIN_CLIENT_AUTHZ_POLICIES = URL_ADMIN_CLIENT_AUTHZ + "/policy"
URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/role"
URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION = URL_ADMIN_CLIENT_AUTHZ + "/permission/resource"
URL_ADMIN_CLIENT_AUTHZ_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/{policy-id}"
URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/scopes"
URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES = URL_ADMIN_CLIENT_AUTHZ_POLICY + "/resources"
@ -132,7 +129,7 @@ URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION = URL_ADMIN_CLIENT_AUTHZ + "/permission/
URL_ADMIN_CLIENT_AUTHZ_RESOURCE_PERMISSION = (
URL_ADMIN_CLIENT_AUTHZ + "/permission/resource/{resource-id}"
)
URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION = URL_ADMIN_CLIENT_AUTHZ + "/permission/scope?max=-1"
URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION = URL_ADMIN_CLIENT_AUTHZ + "/permission/scope"
URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY = URL_ADMIN_CLIENT_AUTHZ + "/policy/client"
URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY_ASSOCIATED_POLICIES = (
URL_ADMIN_CLIENT_AUTHZ + "/policy/{policy-id}/associatedPolicies"
@ -155,7 +152,6 @@ URL_ADMIN_CLIENT_SCOPE_ROLE_MAPPINGS_CLIENT = (
)
URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles"
URL_ADMIN_REALM_ROLES_SEARCH = URL_ADMIN_REALM_ROLES + "?search={search-text}"
URL_ADMIN_REALM_ROLES_MEMBERS = URL_ADMIN_REALM_ROLES + "/{role-name}/users"
URL_ADMIN_REALM_ROLES_GROUPS = URL_ADMIN_REALM_ROLES + "/{role-name}/groups"
URL_ADMIN_REALMS = "admin/realms"
@ -169,10 +165,7 @@ 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_REALM_EXPORT = (
"admin/realms/{realm-name}/partial-export?exportClients={export-clients}&"
+ "exportGroupsAndRoles={export-groups-and-roles}"
)
URL_ADMIN_REALM_EXPORT = "admin/realms/{realm-name}/partial-export"
URL_ADMIN_REALM_PARTIAL_IMPORT = "admin/realms/{realm-name}/partialImport"

2
tests/test_keycloak_admin.py

@ -3353,7 +3353,7 @@ async def test_a_partial_import_realm(admin: KeycloakAdmin, realm: str):
client_id = await admin.a_create_client(payload={"name": test_client, "clientId": test_client})
realm_export = await admin.a_export_realm(export_clients=True, export_groups_and_role=False)
assert realm_export["realm"] == realm, realm
client_config = [
client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
][0]

Loading…
Cancel
Save