Skip to content

Commit

Permalink
fix: correctly pass query params in a_send_update_account and a_send_…
Browse files Browse the repository at this point in the history
…verify_email (#581)
  • Loading branch information
pboehm authored Jul 16, 2024
1 parent 139d528 commit 5f8c030
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 7 deletions.
22 changes: 18 additions & 4 deletions src/keycloak/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ async def a_raw_get(self, path, **kwargs):
try:
return await self.async_s.get(
urljoin(self.base_url, path),
params=kwargs,
params=self._filter_query_params(kwargs),
headers=self.headers,
timeout=self.timeout,
)
Expand All @@ -331,7 +331,7 @@ async def a_raw_post(self, path, data, **kwargs):
return await self.async_s.request(
method="POST",
url=urljoin(self.base_url, path),
params=kwargs,
params=self._filter_query_params(kwargs),
data=data,
headers=self.headers,
timeout=self.timeout,
Expand All @@ -355,7 +355,7 @@ async def a_raw_put(self, path, data, **kwargs):
try:
return await self.async_s.put(
urljoin(self.base_url, path),
params=kwargs,
params=self._filter_query_params(kwargs),
data=data,
headers=self.headers,
timeout=self.timeout,
Expand All @@ -381,9 +381,23 @@ async def a_raw_delete(self, path, data=None, **kwargs):
method="DELETE",
url=urljoin(self.base_url, path),
data=data or dict(),
params=kwargs,
params=self._filter_query_params(kwargs),
headers=self.headers,
timeout=self.timeout,
)
except Exception as e:
raise KeycloakConnectionError("Can't connect to server (%s)" % e)

@staticmethod
def _filter_query_params(query_params):
"""Explicitly filter query params with None values for compatibility.
Httpx and requests differ in the way they handle query params with the value None,
requests does not include params with the value None while httpx includes them as-is.
:param query_params: the query params
:type query_params: dict
:returns: the filtered query params
:rtype: dict
"""
return {k: v for k, v in query_params.items() if v is not None}
4 changes: 2 additions & 2 deletions src/keycloak/keycloak_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5073,7 +5073,7 @@ async def a_send_update_account(
data_raw = await self.connection.a_raw_put(
urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
data=json.dumps(payload),
kwargs=params_query,
**params_query,
)
return raise_error_from_response(data_raw, KeycloakPutError)

Expand All @@ -5097,7 +5097,7 @@ async def a_send_verify_email(self, user_id, client_id=None, redirect_uri=None):
data_raw = await self.connection.a_raw_put(
urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
data={},
kwargs=params_query,
**params_query,
)
return raise_error_from_response(data_raw, KeycloakPutError)

Expand Down
57 changes: 56 additions & 1 deletion tests/test_keycloak_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,20 @@
import uuid
from inspect import iscoroutinefunction, signature
from typing import Tuple
from unittest.mock import ANY, patch

import freezegun
import pytest
from dateutil import parser as datetime_parser
from packaging.version import Version

import keycloak
from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakOpenIDConnection
from keycloak import (
KeycloakAdmin,
KeycloakConnectionError,
KeycloakOpenID,
KeycloakOpenIDConnection,
)
from keycloak.connection import ConnectionManager
from keycloak.exceptions import (
KeycloakAuthenticationError,
Expand Down Expand Up @@ -5170,6 +5176,55 @@ async def test_a_email(admin: KeycloakAdmin, user: str):
assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')


@pytest.mark.asyncio
async def test_a_email_query_param_handling(admin: KeycloakAdmin, user: str):
"""Test that the optional parameters are correctly transformed into query params.
:param admin: Keycloak Admin client
:type admin: KeycloakAdmin
:param user: Keycloak user
:type user: str
"""
with patch.object(
admin.connection.async_s, "put", side_effect=Exception("An expected error")
) as mock_put, pytest.raises(KeycloakConnectionError):
await admin.a_send_update_account(
user_id=user,
payload=["UPDATE_PASSWORD"],
client_id="update-account-client-id",
redirect_uri="https://example.com",
)

mock_put.assert_awaited_once_with(
ANY,
data='["UPDATE_PASSWORD"]',
params={
"client_id": "update-account-client-id",
"redirect_uri": "https://example.com",
},
headers=ANY,
timeout=60,
)

with patch.object(
admin.connection.async_s, "put", side_effect=Exception("An expected error")
) as mock_put, pytest.raises(KeycloakConnectionError):
await admin.a_send_verify_email(
user_id=user, client_id="verify-client-id", redirect_uri="https://example.com"
)

mock_put.assert_awaited_once_with(
ANY,
data=ANY,
params={
"client_id": "verify-client-id",
"redirect_uri": "https://example.com",
},
headers=ANY,
timeout=60,
)


@pytest.mark.asyncio
async def test_a_get_sessions(admin: KeycloakAdmin):
"""Test get sessions.
Expand Down

0 comments on commit 5f8c030

Please sign in to comment.