|
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