From a7919f85256be87b839c5131a2215dcf99577695 Mon Sep 17 00:00:00 2001 From: Joachim Burket Date: Thu, 2 Mar 2023 17:45:07 +0100 Subject: [PATCH 1/4] Passing username to redis arguments --- README.rst | 15 ++++++++++----- django_redis/pool.py | 4 ++++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9e403349..33c4fc49 100644 --- a/README.rst +++ b/README.rst @@ -106,8 +106,9 @@ There are several ways to specify a database number: - If using the ``redis://`` scheme, the path argument of the URL, e.g. ``redis://localhost/0`` -When using `Redis' ACLs `_, you will need to add the -username to the URL (and provide the password with the Cache ``OPTIONS``). +When using `Redis' ACLs `_, you can either pass the +username and password in the Cache ``OPTIONS``, or in the URL. + The login for the user ``django`` would look like this: .. code-block:: python @@ -115,9 +116,10 @@ The login for the user ``django`` would look like this: CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": "redis://django@localhost:6379/0", + "LOCATION": "redis://localhost:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", + "USERNAME": "django", "PASSWORD": "mysecret" } } @@ -693,8 +695,11 @@ In order to enable this functionality you should add the following: # Sentinels which are passed directly to redis Sentinel. "SENTINELS": SENTINELS, - # kwargs for redis Sentinel (optional). - "SENTINEL_KWARGS": {}, + # kwargs for redis Sentinel (optional). Example with auth on sentinels + "SENTINEL_KWARGS": { + "username": "sentinel-user", + "password": "sentinel-pass", + }, # You can still override the connection pool (optional). "CONNECTION_POOL_CLASS": "redis.sentinel.SentinelConnectionPool", diff --git a/django_redis/pool.py b/django_redis/pool.py index dda3b945..469538ec 100644 --- a/django_redis/pool.py +++ b/django_redis/pool.py @@ -43,6 +43,10 @@ def make_connection_params(self, url): "parser_class": self.get_parser_cls(), } + username = self.options.get("USERNAME", None) + if username: + kwargs["username"] = username + password = self.options.get("PASSWORD", None) if password: kwargs["password"] = password From 842b315c6e1f6cf2b99e14920939a9d738c16a43 Mon Sep 17 00:00:00 2001 From: Joachim Burket Date: Mon, 6 Mar 2023 12:10:41 +0100 Subject: [PATCH 2/4] added the changelog file --- changelog.d/657.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/657.feature diff --git a/changelog.d/657.feature b/changelog.d/657.feature new file mode 100644 index 00000000..37586e17 --- /dev/null +++ b/changelog.d/657.feature @@ -0,0 +1 @@ +Added the ability to pass redis username in the cache options \ No newline at end of file From 039432cd21748aa79dcc32541b657c289f74c306 Mon Sep 17 00:00:00 2001 From: Joachim Burket Date: Mon, 6 Mar 2023 14:04:35 +0100 Subject: [PATCH 3/4] Updated README documentation - merged examples --- README.rst | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 33c4fc49..517cee0e 100644 --- a/README.rst +++ b/README.rst @@ -107,7 +107,11 @@ There are several ways to specify a database number: ``redis://localhost/0`` When using `Redis' ACLs `_, you can either pass the -username and password in the Cache ``OPTIONS``, or in the URL. +username and password in the Cache ``OPTIONS`` dict, in the URL, or the username +in the URL and the password in the ``OPTIONS``. + +Be aware that if the username/password are passed both in the URL and ``OPTIONS`` +dict, the ones passed in the URL prime. The login for the user ``django`` would look like this: @@ -139,16 +143,14 @@ An alternative would be write both username and password into the URL: } } -In some circumstances the password you should use to connect Redis -is not URL-safe, in this case you can escape it or just use the -convenience option in ``OPTIONS`` dict: +The username can also be passed in the URL, and the password in the ``OPTIONS``: .. code-block:: python CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": "redis://127.0.0.1:6379/1", + "LOCATION": "redis://django@localhost:6379/0", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", "PASSWORD": "mysecret" @@ -156,8 +158,11 @@ convenience option in ``OPTIONS`` dict: } } -Take care, that this option does not overwrites the password in the uri, so if -you have set the password in the uri, this settings will be ignored. + +NOTE: In some circumstances the password you should use to connect Redis +is not URL-safe, in this case you can escape it or just use the +convenience option in ``OPTIONS`` dict. + Configure as session backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From fc3fd6cbdd94aa6f1f6dfa1d2e32a7c34f404558 Mon Sep 17 00:00:00 2001 From: Joachim Burket Date: Mon, 6 Mar 2023 16:56:34 +0100 Subject: [PATCH 4/4] added connection options test --- tests/test_connection_params.py | 38 +++++++++++++++++++++++++++++++++ tests/test_connection_string.py | 19 ----------------- 2 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 tests/test_connection_params.py delete mode 100644 tests/test_connection_string.py diff --git a/tests/test_connection_params.py b/tests/test_connection_params.py new file mode 100644 index 00000000..7a307672 --- /dev/null +++ b/tests/test_connection_params.py @@ -0,0 +1,38 @@ +import pytest + +from django_redis import pool + + +class TestConnectionParams: + @pytest.mark.parametrize( + "connection_string", + [ + "unix://tmp/foo.bar?db=1", + "redis://localhost/2", + "rediss://localhost:3333?db=2", + ], + ) + def test_make_connection_params_url(self, connection_string: str): + cf = pool.get_connection_factory( + path="django_redis.pool.ConnectionFactory", options={} + ) + res = cf.make_connection_params(connection_string) + assert res["url"] == connection_string + + def test_make_connection_params_options(self): + options = { + "USERNAME": "django", + "PASSWORD": "mysecret", + "SOCKET_TIMEOUT": 5, + } + cf = pool.get_connection_factory( + path="django_redis.pool.ConnectionFactory", options=options + ) + res = cf.make_connection_params("") + res.pop("url") + res.pop("parser_class") + assert res == { + "username": "django", + "password": "mysecret", + "socket_timeout": 5, + } diff --git a/tests/test_connection_string.py b/tests/test_connection_string.py deleted file mode 100644 index 81dffebe..00000000 --- a/tests/test_connection_string.py +++ /dev/null @@ -1,19 +0,0 @@ -import pytest - -from django_redis import pool - - -@pytest.mark.parametrize( - "connection_string", - [ - "unix://tmp/foo.bar?db=1", - "redis://localhost/2", - "rediss://localhost:3333?db=2", - ], -) -def test_connection_strings(connection_string: str): - cf = pool.get_connection_factory( - path="django_redis.pool.ConnectionFactory", options={} - ) - res = cf.make_connection_params(connection_string) - assert res["url"] == connection_string