From 163a56aa3af4cdeb39949ab8f17ab8a6bacaab6a Mon Sep 17 00:00:00 2001 From: Juan Nehuen Gonzalez Montoro Date: Wed, 24 Jul 2024 11:49:48 -0300 Subject: [PATCH] feat: allow the use of client certificates in all connections --- src/keycloak/connection.py | 24 ++++++++++++++++++++++-- src/keycloak/keycloak_admin.py | 4 ++++ src/keycloak/keycloak_openid.py | 5 ++++- src/keycloak/openid_connection.py | 6 ++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/keycloak/connection.py b/src/keycloak/connection.py index c4881f0..62c4562 100644 --- a/src/keycloak/connection.py +++ b/src/keycloak/connection.py @@ -51,7 +51,7 @@ class ConnectionManager(object): :type proxies: dict """ - def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None): + def __init__(self, base_url, headers={}, timeout=60, verify=True, proxies=None, cert=None): """Init method. :param base_url: The server URL. @@ -65,11 +65,14 @@ class ConnectionManager(object): :type verify: Union[bool,str] :param proxies: The proxies servers requests is sent by. :type proxies: dict + :param cert: An SSL certificate used by the requested host to authenticate the client. Either a path to an SSL certificate file, or two-tuple of (certificate file, key file). + :type cert: Union[str,Tuple[str,str]] """ self.base_url = base_url self.headers = headers self.timeout = timeout self.verify = verify + self.cert = cert self._s = requests.Session() self._s.auth = lambda x: x # don't let requests add auth headers @@ -87,7 +90,7 @@ class ConnectionManager(object): if proxies: self._s.proxies.update(proxies) - self.async_s = httpx.AsyncClient(verify=verify, proxies=proxies) + self.async_s = httpx.AsyncClient(verify=verify, proxies=proxies, cert=cert) self.async_s.auth = None # don't let requests add auth headers self.async_s.transport = httpx.AsyncHTTPTransport(retries=1) @@ -140,6 +143,19 @@ class ConnectionManager(object): def verify(self, value): self._verify = value + @property + def cert(self): + """Return client certificates in use for request to the server. + + :returns: Client certificate + :rtype: Union[str,Tuple[str,str]] + """ + return self._cert + + @cert.setter + def cert(self, value): + self._cert = value + @property def headers(self): """Return header request to the server. @@ -213,6 +229,7 @@ class ConnectionManager(object): headers=self.headers, timeout=self.timeout, verify=self.verify, + cert=self.cert, ) except Exception as e: raise KeycloakConnectionError("Can't connect to server (%s)" % e) @@ -238,6 +255,7 @@ class ConnectionManager(object): headers=self.headers, timeout=self.timeout, verify=self.verify, + cert=self.cert, ) except Exception as e: raise KeycloakConnectionError("Can't connect to server (%s)" % e) @@ -263,6 +281,7 @@ class ConnectionManager(object): headers=self.headers, timeout=self.timeout, verify=self.verify, + cert=self.cert, ) except Exception as e: raise KeycloakConnectionError("Can't connect to server (%s)" % e) @@ -288,6 +307,7 @@ class ConnectionManager(object): headers=self.headers, timeout=self.timeout, verify=self.verify, + cert=self.cert, ) return r except Exception as e: diff --git a/src/keycloak/keycloak_admin.py b/src/keycloak/keycloak_admin.py index 41f2e46..e73c6cb 100644 --- a/src/keycloak/keycloak_admin.py +++ b/src/keycloak/keycloak_admin.py @@ -93,6 +93,7 @@ class KeycloakAdmin: custom_headers=None, user_realm_name=None, timeout=60, + cert=None, connection: Optional[KeycloakOpenIDConnection] = None, ): """Init method. @@ -123,6 +124,8 @@ class KeycloakAdmin: :type user_realm_name: str :param timeout: connection timeout in seconds :type timeout: int + :param cert: An SSL certificate used by the requested host to authenticate the client. Either a path to an SSL certificate file, or two-tuple of (certificate file, key file). + :type cert: Union[str,Tuple[str,str]] :param connection: An OpenID Connection as an alternative to individual params. :type connection: KeycloakOpenIDConnection """ @@ -139,6 +142,7 @@ class KeycloakAdmin: user_realm_name=user_realm_name, custom_headers=custom_headers, timeout=timeout, + cert=cert, ) @property diff --git a/src/keycloak/keycloak_openid.py b/src/keycloak/keycloak_openid.py index 205e160..404fde7 100644 --- a/src/keycloak/keycloak_openid.py +++ b/src/keycloak/keycloak_openid.py @@ -86,6 +86,7 @@ class KeycloakOpenID: custom_headers=None, proxies=None, timeout=60, + cert=None, ): """Init method. @@ -106,13 +107,15 @@ class KeycloakOpenID: :type proxies: dict :param timeout: connection timeout in seconds :type timeout: int + :param cert: An SSL certificate used by the requested host to authenticate the client. Either a path to an SSL certificate file, or two-tuple of (certificate file, key file). + :type cert: Union[str,Tuple[str,str]] """ self.client_id = client_id self.client_secret_key = client_secret_key self.realm_name = realm_name headers = custom_headers if custom_headers is not None else dict() self.connection = ConnectionManager( - base_url=server_url, headers=headers, timeout=timeout, verify=verify, proxies=proxies + base_url=server_url, headers=headers, timeout=timeout, verify=verify, proxies=proxies, cert=cert ) self.authorization = Authorization() diff --git a/src/keycloak/openid_connection.py b/src/keycloak/openid_connection.py index c604356..da7e3e5 100644 --- a/src/keycloak/openid_connection.py +++ b/src/keycloak/openid_connection.py @@ -70,6 +70,7 @@ class KeycloakOpenIDConnection(ConnectionManager): custom_headers=None, user_realm_name=None, timeout=60, + cert=None, ): """Init method. @@ -99,6 +100,8 @@ class KeycloakOpenIDConnection(ConnectionManager): :type user_realm_name: str :param timeout: connection timeout in seconds :type timeout: int + :param cert: An SSL certificate used by the requested host to authenticate the client. Either a path to an SSL certificate file, or two-tuple of (certificate file, key file). + :type cert: Union[str,Tuple[str,str]] """ # token is renewed when it hits 90% of its lifetime. This is to account for any possible # clock skew. @@ -117,12 +120,14 @@ class KeycloakOpenIDConnection(ConnectionManager): self.timeout = timeout self.custom_headers = custom_headers self.headers = {**self.headers, "Content-Type": "application/json"} + self.cert = cert super().__init__( base_url=self.server_url, headers=self.headers, timeout=self.timeout, verify=self.verify, + cert=cert, ) @property @@ -297,6 +302,7 @@ class KeycloakOpenIDConnection(ConnectionManager): client_secret_key=self.client_secret_key, timeout=self.timeout, custom_headers=self.custom_headers, + cert=self.cert, ) return self._keycloak_openid