FIX: support for special character in redis password(such like '@') (#155)

* FIX: if special character in redis password, we can set DEFENDER_REDIS_PASSWORD_QUOTE to True, and use quote password

* MOD:add test cases with password_quota = True
This commit is contained in:
calmkart 2020-03-13 20:13:54 +08:00 committed by GitHub
parent 8daa2d168d
commit 71312eb841
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 34 additions and 10 deletions

View file

@ -357,6 +357,8 @@ These should be defined in your ``settings.py`` file.
* ``DEFENDER_REDIS_URL``\ : String: the redis url for defender.
[Default: ``redis://localhost:6379/0``\ ]
(Example with password: ``redis://:mypassword@localhost:6379/0``\ )
* ``DEFENDER_REDIS_PASSWORD_QUOTE``\ : Boolean: if special character in redis password(like '@'), we can quote password(urllib.quote_plus("password!@#")), and set to True.
[Default: ``False``\ ]
* ``DEFENDER_REDIS_NAME``\ : String: the name of your cache client on the CACHES django setting. If set, ``DEFENDER_REDIS_URL`` will be ignored.
[Default: ``None``\ ]
* ``DEFENDER_STORE_ACCESS_ATTEMPTS``\ : Boolean: If you want to store the login

View file

@ -11,6 +11,9 @@ def get_setting(variable, default=None):
# redis server host
DEFENDER_REDIS_URL = get_setting("DEFENDER_REDIS_URL")
# redis password quote for special character
DEFENDER_REDIS_PASSWORD_QUOTE = get_setting("DEFENDER_REDIS_PASSWORD_QUOTE", False)
# reuse declared cache from django settings
DEFENDER_REDIS_NAME = get_setting("DEFENDER_REDIS_NAME")

View file

@ -35,7 +35,8 @@ def get_redis_connection():
# django_redis.cache.RedisCache case (django-redis package)
return cache.client.get_client(True)
else: # pragma: no cover
redis_config = parse_redis_url(config.DEFENDER_REDIS_URL)
redis_config = parse_redis_url(
config.DEFENDER_REDIS_URL, config.DEFENDER_REDIS_PASSWORD_QUOTE)
return redis.StrictRedis(
host=redis_config.get("HOST"),
port=redis_config.get("PORT"),
@ -45,7 +46,7 @@ def get_redis_connection():
)
def parse_redis_url(url):
def parse_redis_url(url, password_quote):
"""Parses a redis URL."""
# create config with some sane defaults
@ -68,7 +69,10 @@ def parse_redis_url(url):
if path:
redis_config.update({"DB": int(path)})
if url.password:
redis_config.update({"PASSWORD": url.password})
password = url.password
if password_quote:
password = urlparse.unquote(password)
redis_config.update({"PASSWORD": password})
if url.hostname:
redis_config.update({"HOST": url.hostname})
if url.port:

View file

@ -491,54 +491,69 @@ class AccessAttemptTest(DefenderTestCase):
def test_parse_redis_url(self):
""" test the parse_redis_url method """
# full regular
conf = parse_redis_url("redis://user:password@localhost2:1234/2")
conf = parse_redis_url("redis://user:password@localhost2:1234/2", False)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 2)
self.assertEqual(conf.get("PASSWORD"), "password")
self.assertEqual(conf.get("PORT"), 1234)
# full non local
conf = parse_redis_url("redis://user:pass@www.localhost.com:1234/2")
conf = parse_redis_url(
"redis://user:pass@www.localhost.com:1234/2", False)
self.assertEqual(conf.get("HOST"), "www.localhost.com")
self.assertEqual(conf.get("DB"), 2)
self.assertEqual(conf.get("PASSWORD"), "pass")
self.assertEqual(conf.get("PORT"), 1234)
# no user name
conf = parse_redis_url("redis://password@localhost2:1234/2")
conf = parse_redis_url("redis://password@localhost2:1234/2", False)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 2)
self.assertEqual(conf.get("PASSWORD"), None)
self.assertEqual(conf.get("PORT"), 1234)
# no user name 2 with colon
conf = parse_redis_url("redis://:password@localhost2:1234/2")
conf = parse_redis_url("redis://:password@localhost2:1234/2", False)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 2)
self.assertEqual(conf.get("PASSWORD"), "password")
self.assertEqual(conf.get("PORT"), 1234)
# Empty
conf = parse_redis_url(None)
conf = parse_redis_url(None, False)
self.assertEqual(conf.get("HOST"), "localhost")
self.assertEqual(conf.get("DB"), 0)
self.assertEqual(conf.get("PASSWORD"), None)
self.assertEqual(conf.get("PORT"), 6379)
# no db
conf = parse_redis_url("redis://:password@localhost2:1234")
conf = parse_redis_url("redis://:password@localhost2:1234", False)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 0)
self.assertEqual(conf.get("PASSWORD"), "password")
self.assertEqual(conf.get("PORT"), 1234)
# no password
conf = parse_redis_url("redis://localhost2:1234/0")
conf = parse_redis_url("redis://localhost2:1234/0", False)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 0)
self.assertEqual(conf.get("PASSWORD"), None)
self.assertEqual(conf.get("PORT"), 1234)
# password with special character and set the password_quote = True
conf = parse_redis_url("redis://:calmkart%23%40%21@localhost:6379/0", True)
self.assertEqual(conf.get("HOST"), "localhost")
self.assertEqual(conf.get("DB"), 0)
self.assertEqual(conf.get("PASSWORD"), "calmkart#@!")
self.assertEqual(conf.get("PORT"), 6379)
# password without special character and set the password_quote = True
conf = parse_redis_url("redis://:password@localhost2:1234", True)
self.assertEqual(conf.get("HOST"), "localhost2")
self.assertEqual(conf.get("DB"), 0)
self.assertEqual(conf.get("PASSWORD"), "password")
self.assertEqual(conf.get("PORT"), 1234)
@patch("defender.config.DEFENDER_REDIS_NAME", "default")
def test_get_redis_connection_django_conf(self):
""" get the redis connection """