|
16 | 16 | # limitations under the License. |
17 | 17 | # |
18 | 18 | import asyncio |
| 19 | +import certifi |
19 | 20 | import json |
20 | 21 | import logging |
| 22 | +import os |
| 23 | +import ssl |
21 | 24 | import time |
22 | 25 | import urllib |
23 | 26 | from urllib.parse import unquote, urlparse |
24 | 27 |
|
25 | 28 | import httpx |
26 | | -from typing import List, Dict, Optional, Union, Any, Tuple, Callable |
| 29 | +from typing import List, Dict, Optional, Union, Any, Callable |
27 | 30 |
|
28 | 31 | from cachetools import TTLCache, LRUCache |
29 | 32 | from httpx import Response |
@@ -148,24 +151,43 @@ def __init__(self, conf: dict): |
148 | 151 | raise ValueError("Missing required configuration property url") |
149 | 152 | self.base_urls = base_urls |
150 | 153 |
|
151 | | - self.verify = True |
152 | | - ca = conf_copy.pop('ssl.ca.location', None) |
153 | | - if ca is not None: |
154 | | - self.verify = ca |
155 | | - |
| 154 | + ca: Union[str, bool, None] = conf_copy.pop('ssl.ca.location', None) |
156 | 155 | key: Optional[str] = conf_copy.pop('ssl.key.location', None) |
| 156 | + key_password: Optional[str] = conf_copy.pop('ssl.key.password', None) |
157 | 157 | client_cert: Optional[str] = conf_copy.pop('ssl.certificate.location', None) |
158 | | - self.cert: Union[str, Tuple[str, str], None] = None |
159 | | - |
160 | | - if client_cert is not None and key is not None: |
161 | | - self.cert = (client_cert, key) |
162 | 158 |
|
163 | | - if client_cert is not None and key is None: |
164 | | - self.cert = client_cert |
| 159 | + # this mimicks legacy, deprecated behaviour of httpx |
| 160 | + # self.verify is always set to an ssl.SSLContext in case we need to load_cert_chain |
| 161 | + if ca is False: |
| 162 | + self.verify = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 163 | + self.verify.check_hostname = False |
| 164 | + self.verify.verify_mode = ssl.CERT_NONE |
| 165 | + elif isinstance(ca, str): |
| 166 | + if os.path.isdir(ca): |
| 167 | + self.verify = ssl.create_default_context(capath=ca) |
| 168 | + else: |
| 169 | + self.verify = ssl.create_default_context(cafile=ca) |
| 170 | + else: |
| 171 | + if os.environ.get("SSL_CERT_FILE"): |
| 172 | + self.verify = ssl.create_default_context(cafile=os.environ["SSL_CERT_FILE"]) |
| 173 | + elif os.environ.get("SSL_CERT_DIR"): |
| 174 | + self.verify = ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"]) |
| 175 | + else: |
| 176 | + self.verify = ssl.create_default_context(cafile=certifi.where()) |
| 177 | + |
| 178 | + if client_cert is not None: |
| 179 | + if key is not None and key_password is not None: |
| 180 | + self.verify.load_cert_chain(certfile=client_cert, keyfile=key, password=key_password) |
| 181 | + elif key is not None: |
| 182 | + self.verify.load_cert_chain(certfile=client_cert, keyfile=key) |
| 183 | + elif key_password is not None: |
| 184 | + self.verify.load_cert_chain(certfile=client_cert, password=key_password) |
| 185 | + else: |
| 186 | + self.verify.load_cert_chain(certfile=client_cert) |
165 | 187 |
|
166 | | - if key is not None and client_cert is None: |
| 188 | + if (key is not None or key_password is not None) and client_cert is None: |
167 | 189 | raise ValueError("ssl.certificate.location required when" |
168 | | - " configuring ssl.key.location") |
| 190 | + " configuring ssl.key.location or ssl.key.password") |
169 | 191 |
|
170 | 192 | parsed = urlparse(self.base_urls[0]) |
171 | 193 | try: |
@@ -351,7 +373,6 @@ def __init__(self, conf: dict): |
351 | 373 |
|
352 | 374 | self.session = httpx.AsyncClient( |
353 | 375 | verify=self.verify, |
354 | | - cert=self.cert, |
355 | 376 | auth=self.auth, |
356 | 377 | proxy=self.proxy, |
357 | 378 | timeout=self.timeout |
@@ -516,10 +537,18 @@ class AsyncSchemaRegistryClient(object): |
516 | 537 | | ``ssl.key.location`` | str | | |
517 | 538 | | | | ``ssl.certificate.location`` must also be set. | |
518 | 539 | +------------------------------+------+-------------------------------------------------+ |
519 | | - | | | Path to client's public key (PEM) used for | |
| 540 | + | | | Password to use to decrypt the client's private | |
| 541 | + | | | key. | |
| 542 | + | | | | |
| 543 | + | ``ssl.key.password`` | str | The private key may be provided using | |
| 544 | + | | | ``ssl.key.location``, or bundled with the | |
| 545 | + | | | certificate in ``ssl.certificate.location``. | |
| 546 | + | | | Password is optional (key may be unencrypted). | |
| 547 | + +------------------------------+------+-------------------------------------------------+ |
| 548 | + | | | Path to client's certificate (PEM) used for | |
520 | 549 | | | | authentication. | |
521 | 550 | | ``ssl.certificate.location`` | str | | |
522 | | - | | | May be set without ssl.key.location if the | |
| 551 | + | | | May be set without ``ssl.key.location`` if the | |
523 | 552 | | | | private key is stored within the PEM as well. | |
524 | 553 | +------------------------------+------+-------------------------------------------------+ |
525 | 554 | | | | Client HTTP credentials in the form of | |
|
0 commit comments