From 7a6a06440af5a5d6d383b235dbf82f4fbb7602ec Mon Sep 17 00:00:00 2001 From: Roel ter Maat Date: Wed, 1 Mar 2023 12:29:06 +0100 Subject: [PATCH 1/3] Add ssl option to connection --- README.md | 1 + examples/ssl_connection.py | 29 +++++++++++++++++++++++ txredisapi.py | 48 +++++++++++++++++++++----------------- 3 files changed, 57 insertions(+), 21 deletions(-) create mode 100644 examples/ssl_connection.py diff --git a/README.md b/README.md index 7c3a8ea..a749ef0 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ The arguments are: - hosts (for sharded): list of ``host:port`` pairs. [default: None] - paths (for sharded): list of ``pathnames``. [default: None] - password: password for the redis server. [default: None] +- use_ssl: boolean indicating wether to use SSL/TLS. [default: False] ### Connection Handlers ### diff --git a/examples/ssl_connection.py b/examples/ssl_connection.py new file mode 100644 index 0000000..6af250e --- /dev/null +++ b/examples/ssl_connection.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +import txredisapi as redis + +from twisted.internet import defer, ssl +from twisted.internet import reactor + + +class TestContextFactory(ssl.ClientContextFactory): + def getContext(self): + ctx = ssl.ClientContextFactory.getContext(self) + # ctx.load_verify_locations('./test/ca.crt') + # ctx.use_certificate_file('./test/redis.crt') + # ctx.use_privatekey_file('./test/redis.key') + return ctx + +@defer.inlineCallbacks +def main(): + rc = yield redis.Connection(ssl_context_factory=TestContextFactory()) + print(rc) + + yield rc.set("foo", "bar") + v = yield rc.get("foo") + print("foo:", repr(v)) + + yield rc.disconnect() + +if __name__ == "__main__": + main().addCallback(lambda ign: reactor.stop()) + reactor.run() \ No newline at end of file diff --git a/txredisapi.py b/txredisapi.py index a9b7cee..3aae200 100644 --- a/txredisapi.py +++ b/txredisapi.py @@ -35,7 +35,8 @@ import hashlib import random -from twisted.internet import defer +from typing import Optional, Union +from twisted.internet import defer, ssl from twisted.internet import protocol from twisted.internet import reactor from twisted.internet.tcp import Connector @@ -2380,14 +2381,19 @@ def __init__(self, isLazy=False, handler=ConnectionHandler): def makeConnection(host, port, dbid, poolsize, reconnect, isLazy, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers): uuid = "%s:%d" % (host, port) factory = RedisFactory(uuid, dbid, poolsize, isLazy, ConnectionHandler, charset, password, replyTimeout, convertNumbers) factory.continueTrying = reconnect for x in range(poolsize): - reactor.connectTCP(host, port, factory, connectTimeout) + if isinstance(ssl_context_factory, bool) and ssl_context_factory is True: + ssl_context_factory = ssl.ClientContextFactory() + if ssl_context_factory: + reactor.connectSSL(host, port, factory, ssl_context_factory, connectTimeout) + else: + reactor.connectTCP(host, port, factory, connectTimeout) if isLazy: return factory.handler @@ -2396,7 +2402,7 @@ def makeConnection(host, port, dbid, poolsize, reconnect, isLazy, def makeShardedConnection(hosts, dbid, poolsize, reconnect, isLazy, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers): err = "Please use a list or tuple of host:port for sharded connections" if not isinstance(hosts, (list, tuple)): @@ -2411,7 +2417,7 @@ def makeShardedConnection(hosts, dbid, poolsize, reconnect, isLazy, raise ValueError(err) c = makeConnection(host, port, dbid, poolsize, reconnect, isLazy, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) connections.append(c) @@ -2424,71 +2430,71 @@ def makeShardedConnection(hosts, dbid, poolsize, reconnect, isLazy, def Connection(host="localhost", port=6379, dbid=None, reconnect=True, - charset="utf-8", password=None, + charset="utf-8", password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeConnection(host, port, dbid, 1, reconnect, False, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def lazyConnection(host="localhost", port=6379, dbid=None, reconnect=True, - charset="utf-8", password=None, + charset="utf-8", password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeConnection(host, port, dbid, 1, reconnect, True, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def ConnectionPool(host="localhost", port=6379, dbid=None, - poolsize=10, reconnect=True, charset="utf-8", password=None, + poolsize=10, reconnect=True, charset="utf-8", password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeConnection(host, port, dbid, poolsize, reconnect, False, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def lazyConnectionPool(host="localhost", port=6379, dbid=None, poolsize=10, reconnect=True, charset="utf-8", - password=None, connectTimeout=None, replyTimeout=None, + password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeConnection(host, port, dbid, poolsize, reconnect, True, - charset, password, connectTimeout, replyTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def ShardedConnection(hosts, dbid=None, reconnect=True, charset="utf-8", - password=None, connectTimeout=None, replyTimeout=None, + password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeShardedConnection(hosts, dbid, 1, reconnect, False, - charset, password, connectTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def lazyShardedConnection(hosts, dbid=None, reconnect=True, charset="utf-8", - password=None, + password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeShardedConnection(hosts, dbid, 1, reconnect, True, - charset, password, connectTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def ShardedConnectionPool(hosts, dbid=None, poolsize=10, reconnect=True, - charset="utf-8", password=None, + charset="utf-8", password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeShardedConnection(hosts, dbid, poolsize, reconnect, False, - charset, password, connectTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) def lazyShardedConnectionPool(hosts, dbid=None, poolsize=10, reconnect=True, - charset="utf-8", password=None, + charset="utf-8", password=None, ssl_context_factory: Union[ssl.ClientContextFactory, bool]=False, connectTimeout=None, replyTimeout=None, convertNumbers=True): return makeShardedConnection(hosts, dbid, poolsize, reconnect, True, - charset, password, connectTimeout, + charset, password, ssl_context_factory, connectTimeout, replyTimeout, convertNumbers) From 063e0c19b88fad1b476ef2d5f0af2cd1b27af21f Mon Sep 17 00:00:00 2001 From: Roel ter Maat Date: Wed, 1 Mar 2023 12:33:12 +0100 Subject: [PATCH 2/3] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a749ef0..b58c3d0 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ The arguments are: - hosts (for sharded): list of ``host:port`` pairs. [default: None] - paths (for sharded): list of ``pathnames``. [default: None] - password: password for the redis server. [default: None] -- use_ssl: boolean indicating wether to use SSL/TLS. [default: False] +- ssl_context_factory: Either a boolean indicating wether to use SSL/TLS or a specific `ClientContextFactory`. [default: False] ### Connection Handlers ### From f2ef3736b292ff1f70a9b54e4782194673931acf Mon Sep 17 00:00:00 2001 From: Roel ter Maat Date: Thu, 2 Mar 2023 20:10:54 +0100 Subject: [PATCH 3/3] Update txredisapi.py Co-authored-by: Ilya Skriblovsky --- txredisapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/txredisapi.py b/txredisapi.py index 3aae200..11c1d7b 100644 --- a/txredisapi.py +++ b/txredisapi.py @@ -2388,7 +2388,7 @@ def makeConnection(host, port, dbid, poolsize, reconnect, isLazy, charset, password, replyTimeout, convertNumbers) factory.continueTrying = reconnect for x in range(poolsize): - if isinstance(ssl_context_factory, bool) and ssl_context_factory is True: + if ssl_context_factory is True: ssl_context_factory = ssl.ClientContextFactory() if ssl_context_factory: reactor.connectSSL(host, port, factory, ssl_context_factory, connectTimeout)