Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/source/modules/admin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Configure admin client
username='example-admin',
password='secret',
realm_name="master",
user_realm_name="only_if_other_realm_than_master")
user_realm_name="only_if_other_realm_than_master",
pool_maxsize=20)


Configure admin client with connection
Expand All @@ -34,6 +35,7 @@ Configure admin client with connection
user_realm_name="only_if_other_realm_than_master",
client_id="my_client",
client_secret_key="client-secret",
pool_maxsize=25,
verify=True)

keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
Expand Down
3 changes: 2 additions & 1 deletion docs/source/modules/openid_client.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ Configure client OpenID
keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/",
client_id="example_client",
realm_name="example_realm",
client_secret_key="secret")
client_secret_key="secret",
pool_maxsize=15) # Example: Set connection pool size


Get .well_know
Expand Down
25 changes: 24 additions & 1 deletion src/keycloak/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ class ConnectionManager:
:type cert: Union[str,Tuple[str,str]]
:param max_retries: The total number of times to retry HTTP requests.
:type max_retries: int
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""

def __init__(
Expand All @@ -70,6 +72,7 @@ def __init__(
proxies: dict | None = None,
cert: str | tuple | None = None,
max_retries: int = 1,
pool_maxsize: int | None = None,
) -> None:
"""
Init method.
Expand All @@ -91,19 +94,25 @@ def __init__(
:type cert: Union[str,Tuple[str,str]]
:param max_retries: The total number of times to retry HTTP requests.
:type max_retries: int
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""
self.base_url = base_url
self.headers = headers
self.timeout = timeout
self.verify = verify
self.cert = cert
self.pool_maxsize = pool_maxsize
self._s = requests.Session()
self._s.auth = lambda x: x # don't let requests add auth headers

# retry once to reset connection with Keycloak after tomcat's ConnectionTimeout
# see https://github.com/marcospereirampj/python-keycloak/issues/36
for protocol in ("https://", "http://"):
adapter = HTTPAdapter(max_retries=max_retries)
adapter_kwargs = {"max_retries": max_retries}
if pool_maxsize is not None:
adapter_kwargs["pool_maxsize"] = pool_maxsize
adapter = HTTPAdapter(**adapter_kwargs)
# adds POST to retry whitelist
allowed_methods = set(adapter.max_retries.allowed_methods)
allowed_methods.add("POST")
Expand Down Expand Up @@ -184,6 +193,20 @@ def cert(self) -> str | tuple:
def cert(self, value: str | tuple) -> None:
self._cert = value

@property
def pool_maxsize(self) -> int | None:
"""
Return the maximum number of connections to save in the pool.

:returns: Pool maxsize
:rtype: int or None
"""
return self._pool_maxsize

@pool_maxsize.setter
def pool_maxsize(self, value: int | None) -> None:
self._pool_maxsize = value

@property
def headers(self) -> dict:
"""
Expand Down
6 changes: 6 additions & 0 deletions src/keycloak/keycloak_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class KeycloakAdmin:
:type max_retries: int
:param connection: A KeycloakOpenIDConnection as an alternative to individual params.
:type connection: KeycloakOpenIDConnection
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""

PAGE_SIZE = 100
Expand All @@ -109,6 +111,7 @@ def __init__(
cert: str | tuple | None = None,
max_retries: int = 1,
connection: KeycloakOpenIDConnection | None = None,
pool_maxsize: int | None = None,
) -> None:
"""
Init method.
Expand Down Expand Up @@ -148,6 +151,8 @@ def __init__(
:type max_retries: int
:param connection: An OpenID Connection as an alternative to individual params.
:type connection: KeycloakOpenIDConnection
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""
self.connection = connection or KeycloakOpenIDConnection(
server_url=server_url,
Expand All @@ -165,6 +170,7 @@ def __init__(
timeout=timeout,
cert=cert,
max_retries=max_retries,
pool_maxsize=pool_maxsize,
)

@property
Expand Down
7 changes: 6 additions & 1 deletion src/keycloak/keycloak_openid.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ class KeycloakOpenID:
Either a path to an SSL certificate file, or two-tuple of
(certificate file, key file).
:param max_retries: The total number of times to retry HTTP requests.
:type max_retries: int
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""

def __init__(
Expand All @@ -102,6 +103,7 @@ def __init__(
timeout: int = 60,
cert: str | tuple | None = None,
max_retries: int = 1,
pool_maxsize: int | None = None,
) -> None:
"""
Init method.
Expand Down Expand Up @@ -129,6 +131,8 @@ def __init__(
:type cert: Union[str,Tuple[str,str]]
:param max_retries: The total number of times to retry HTTP requests.
:type max_retries: int
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""
self.client_id = client_id
self.client_secret_key = client_secret_key
Expand All @@ -142,6 +146,7 @@ def __init__(
proxies=proxies,
cert=cert,
max_retries=max_retries,
pool_maxsize=pool_maxsize,
)

self.authorization = Authorization()
Expand Down
4 changes: 4 additions & 0 deletions src/keycloak/openid_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def __init__(
timeout: int | None = 60,
cert: str | tuple | None = None,
max_retries: int = 1,
pool_maxsize: int | None = None,
) -> None:
"""
Init method.
Expand Down Expand Up @@ -120,6 +121,8 @@ def __init__(
:type cert: Union[str,Tuple[str,str]]
:param max_retries: The total number of times to retry HTTP requests.
:type max_retries: int
:param pool_maxsize: The maximum number of connections to save in the pool.
:type pool_maxsize: int
"""
# token is renewed when it hits 90% of its lifetime. This is to account for any possible
# clock skew.
Expand Down Expand Up @@ -154,6 +157,7 @@ def __init__(
verify=self.verify,
cert=cert,
max_retries=max_retries,
pool_maxsize=pool_maxsize,
)

@property
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we keep the fixtures minimal and just delete the pool_maxsize in here? (i.e. let the defaults take place). This way we can be sure that minimal configuration will keep working for most operations.

Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ def admin(env: KeycloakTestEnv) -> KeycloakAdmin:
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
username=env.keycloak_admin,
password=env.keycloak_admin_password,
pool_maxsize=5,
)


Expand All @@ -179,6 +180,7 @@ def admin_frozen(env: KeycloakTestEnv) -> KeycloakAdmin:
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
username=env.keycloak_admin,
password=env.keycloak_admin_password,
pool_maxsize=5,
)


Expand Down Expand Up @@ -218,6 +220,7 @@ def oid(
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
realm_name=realm,
client_id=client,
pool_maxsize=5,
)
# Cleanup
admin.delete_client(client_id=client_id)
Expand Down Expand Up @@ -279,6 +282,7 @@ def oid_with_credentials(
realm_name=realm,
client_id=client,
client_secret_key=secret,
pool_maxsize=5,
),
username,
password,
Expand Down Expand Up @@ -354,6 +358,7 @@ def oid_with_credentials_authz(
realm_name=realm,
client_id=client,
client_secret_key=secret,
pool_maxsize=5,
),
username,
password,
Expand Down Expand Up @@ -421,6 +426,7 @@ def oid_with_credentials_device(
realm_name=realm,
client_id=client,
client_secret_key=secret,
pool_maxsize=5,
),
username,
password,
Expand Down
16 changes: 16 additions & 0 deletions tests/test_keycloak_admin.py
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar here. There's no need to set the pool max size for all these cases. 1 time is enough.

Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
username=env.keycloak_admin,
password=env.keycloak_admin_password,
pool_maxsize=5,
)
assert admin.connection.server_url == f"http://{env.keycloak_host}:{env.keycloak_port}", (
admin.connection.server_url
Expand All @@ -72,13 +73,22 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
assert admin.connection.token is None, admin.connection.token
assert admin.connection.user_realm_name is None, admin.connection.user_realm_name
assert admin.connection.custom_headers is None, admin.connection.custom_headers
assert admin.connection.pool_maxsize == 5, admin.connection.pool_maxsize

admin_default = KeycloakAdmin(
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
username=env.keycloak_admin,
password=env.keycloak_admin_password,
)
assert admin_default.connection.pool_maxsize is None

admin = KeycloakAdmin(
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
username=env.keycloak_admin,
password=env.keycloak_admin_password,
realm_name=None,
user_realm_name="master",
pool_maxsize=5,
)
assert admin.connection.token is None
admin = KeycloakAdmin(
Expand All @@ -87,6 +97,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
password=env.keycloak_admin_password,
realm_name=None,
user_realm_name=None,
pool_maxsize=5,
)
assert admin.connection.token is None

Expand All @@ -97,6 +108,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
token=token,
realm_name=None,
user_realm_name=None,
pool_maxsize=5,
)
assert admin.connection.token == token

Expand All @@ -121,6 +133,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
user_realm_name="authz",
client_id="authz-client",
client_secret_key=secret["value"],
pool_maxsize=5,
)
admin_auth.connection.refresh_token()
assert admin_auth.connection.token is not None
Expand All @@ -133,6 +146,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
password=None,
client_secret_key=None,
custom_headers={"custom": "header"},
pool_maxsize=5,
).connection.token
is None
)
Expand All @@ -144,6 +158,7 @@ def test_keycloak_admin_init(env: KeycloakTestEnv) -> None:
realm_name="master",
client_id="admin-cli",
verify=True,
pool_maxsize=5,
)
keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
keycloak_admin.connection.get_token()
Expand Down Expand Up @@ -3615,6 +3630,7 @@ async def test_a_realms(admin: KeycloakAdmin) -> None:
realms = await admin.a_get_realms()
assert len(realms) == 1, realms
assert realms[0]["realm"] == "master"
assert admin.connection.pool_maxsize == 5, admin.connection.pool_maxsize

# Create a test realm
res = await admin.a_create_realm(payload={"realm": "test"})
Expand Down
10 changes: 10 additions & 0 deletions tests/test_keycloak_openid.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,22 @@ def test_keycloak_openid_init(env: KeycloakTestEnv) -> None:
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
realm_name="master",
client_id="admin-cli",
pool_maxsize=5,
)

assert oid.client_id == "admin-cli"
assert oid.client_secret_key is None
assert oid.realm_name == "master"
assert isinstance(oid.connection, ConnectionManager)
assert isinstance(oid.authorization, Authorization)
assert oid.connection.pool_maxsize == 5

oid_default = KeycloakOpenID(
server_url=f"http://{env.keycloak_host}:{env.keycloak_port}",
realm_name="master",
client_id="admin-cli",
)
assert oid_default.connection.pool_maxsize is None


def test_well_known(oid: KeycloakOpenID) -> None:
Expand Down Expand Up @@ -576,6 +585,7 @@ async def test_a_well_known(oid: KeycloakOpenID) -> None:
res = await oid.a_well_known()
assert res is not None
assert res != {}
assert oid.connection.pool_maxsize == 5
for key in [
"acr_values_supported",
"authorization_encryption_alg_values_supported",
Expand Down
Loading