Skip to content

Commit 201c3c9

Browse files
majiayu000claudepetyaslavova
authored
Add ssl_ca_path support to async Redis client (#3879)
* Add ssl_ca_path support to async Redis client (#3414) Add ssl_ca_path parameter to the async Redis client and SSLConnection to enable specifying a directory of CA certificates, matching the sync client's functionality. Changes: - Add ssl_ca_path parameter to Redis.__init__ - Add ssl_ca_path parameter to SSLConnection.__init__ - Add ca_path parameter to RedisSSLContext - Pass capath to ssl.SSLContext.load_verify_locations() 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Add tests for ssl_ca_path and add to REDIS_ALLOWED_KEYS - Add test_ssl_ca_path_parameter test to verify ssl_ca_path is properly passed through to SSLConnection - Add ssl_ca_path to REDIS_ALLOWED_KEYS tuple in cluster.py for sync cluster client support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: petyaslavova <petya.slavova@redis.com>
1 parent 5c5bdc4 commit 201c3c9

File tree

4 files changed

+38
-2
lines changed

4 files changed

+38
-2
lines changed

redis/asyncio/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ def __init__(
243243
ssl_exclude_verify_flags: Optional[List[VerifyFlags]] = None,
244244
ssl_ca_certs: Optional[str] = None,
245245
ssl_ca_data: Optional[str] = None,
246+
ssl_ca_path: Optional[str] = None,
246247
ssl_check_hostname: bool = True,
247248
ssl_min_version: Optional[TLSVersion] = None,
248249
ssl_ciphers: Optional[str] = None,
@@ -354,6 +355,7 @@ def __init__(
354355
"ssl_exclude_verify_flags": ssl_exclude_verify_flags,
355356
"ssl_ca_certs": ssl_ca_certs,
356357
"ssl_ca_data": ssl_ca_data,
358+
"ssl_ca_path": ssl_ca_path,
357359
"ssl_check_hostname": ssl_check_hostname,
358360
"ssl_min_version": ssl_min_version,
359361
"ssl_ciphers": ssl_ciphers,

redis/asyncio/connection.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@ def __init__(
813813
ssl_exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
814814
ssl_ca_certs: Optional[str] = None,
815815
ssl_ca_data: Optional[str] = None,
816+
ssl_ca_path: Optional[str] = None,
816817
ssl_check_hostname: bool = True,
817818
ssl_min_version: Optional[TLSVersion] = None,
818819
ssl_ciphers: Optional[str] = None,
@@ -829,6 +830,7 @@ def __init__(
829830
exclude_verify_flags=ssl_exclude_verify_flags,
830831
ca_certs=ssl_ca_certs,
831832
ca_data=ssl_ca_data,
833+
ca_path=ssl_ca_path,
832834
check_hostname=ssl_check_hostname,
833835
min_version=ssl_min_version,
834836
ciphers=ssl_ciphers,
@@ -886,6 +888,7 @@ class RedisSSLContext:
886888
"exclude_verify_flags",
887889
"ca_certs",
888890
"ca_data",
891+
"ca_path",
889892
"context",
890893
"check_hostname",
891894
"min_version",
@@ -901,6 +904,7 @@ def __init__(
901904
exclude_verify_flags: Optional[List["ssl.VerifyFlags"]] = None,
902905
ca_certs: Optional[str] = None,
903906
ca_data: Optional[str] = None,
907+
ca_path: Optional[str] = None,
904908
check_hostname: bool = False,
905909
min_version: Optional[TLSVersion] = None,
906910
ciphers: Optional[str] = None,
@@ -928,6 +932,7 @@ def __init__(
928932
self.exclude_verify_flags = exclude_verify_flags
929933
self.ca_certs = ca_certs
930934
self.ca_data = ca_data
935+
self.ca_path = ca_path
931936
self.check_hostname = (
932937
check_hostname if self.cert_reqs != ssl.CERT_NONE else False
933938
)
@@ -948,8 +953,10 @@ def get(self) -> SSLContext:
948953
context.verify_flags &= ~flag
949954
if self.certfile and self.keyfile:
950955
context.load_cert_chain(certfile=self.certfile, keyfile=self.keyfile)
951-
if self.ca_certs or self.ca_data:
952-
context.load_verify_locations(cafile=self.ca_certs, cadata=self.ca_data)
956+
if self.ca_certs or self.ca_data or self.ca_path:
957+
context.load_verify_locations(
958+
cafile=self.ca_certs, capath=self.ca_path, cadata=self.ca_data
959+
)
953960
if self.min_version is not None:
954961
context.minimum_version = self.min_version
955962
if self.ciphers is not None:

redis/cluster.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ def parse_cluster_myshardid(resp, **options):
185185
"ssl",
186186
"ssl_ca_certs",
187187
"ssl_ca_data",
188+
"ssl_ca_path",
188189
"ssl_certfile",
189190
"ssl_cert_reqs",
190191
"ssl_include_verify_flags",

tests/test_asyncio/test_ssl.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,3 +141,29 @@ def capture_context_create_default():
141141

142142
finally:
143143
await r.aclose()
144+
145+
async def test_ssl_ca_path_parameter(self, request):
146+
"""Test that ssl_ca_path parameter is properly passed to SSLConnection"""
147+
ssl_url = request.config.option.redis_ssl_url
148+
parsed_url = urlparse(ssl_url)
149+
150+
# Test with a mock ca_path directory
151+
test_ca_path = "/tmp/test_ca_certs"
152+
153+
r = redis.Redis(
154+
host=parsed_url.hostname,
155+
port=parsed_url.port,
156+
ssl=True,
157+
ssl_cert_reqs="none",
158+
ssl_ca_path=test_ca_path,
159+
)
160+
161+
try:
162+
# Get the connection to verify ssl_ca_path is passed through
163+
conn = r.connection_pool.make_connection()
164+
assert isinstance(conn, redis.SSLConnection)
165+
166+
# Verify the ca_path is stored in the SSL context
167+
assert conn.ssl_context.ca_path == test_ca_path
168+
finally:
169+
await r.aclose()

0 commit comments

Comments
 (0)