summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndy McCurdy <andy@andymccurdy.com>2020-01-31 12:18:12 -0800
committerAndy McCurdy <andy@andymccurdy.com>2020-01-31 12:18:12 -0800
commite39ae455dfa259cdbb0c1cee2e4cb50c17ee22bf (patch)
tree342683bbb84d6d55c54bc166cae1dac82becf8a1
parentab1c659aaac8a59c4e4055e1598920706192fa42 (diff)
downloadredis-py-e39ae455dfa259cdbb0c1cee2e4cb50c17ee22bf.tar.gz
Provide AUTH fallback support for connection URLs with a username component
Prior to ACL support, redis-py ignored the username component of Connection URLs. With ACL support, usernames are no longer ignored and are used to authenticate against an ACL rule. Some cloud vendors with managed Redis instances (like Heroku) provide connection URLs with a username component pre-ACL that is not intended to be used. Sending that username to Redis servers < 6.0.0 results in an error. Attempt to detect this condition and retry the AUTH command with only the password such that authentication continues to work for these users. Fixes #1274
-rw-r--r--CHANGES9
-rw-r--r--redis/__init__.py2
-rwxr-xr-xredis/connection.py16
-rw-r--r--redis/exceptions.py8
4 files changed, 34 insertions, 1 deletions
diff --git a/CHANGES b/CHANGES
index b0641e9..bbf51e8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,12 @@
+* 3.4.1 (in development)
+ * Prior to ACL support, redis-py ignored the username component of
+ Connection URLs. With ACL support, usernames are no longer ignored and
+ are used to authenticate against an ACL rule. Some cloud vendors with
+ managed Redis instances (like Heroku) provide connection URLs with a
+ username component pre-ACL that is not intended to be used. Sending that
+ username to Redis servers < 6.0.0 results in an error. Attempt to detect
+ this condition and retry the AUTH command with only the password such
+ that authentication continues to work for these users. #1274
* 3.4.0
* Allow empty pipelines to be executed if there are WATCHed keys.
This is a convenient way to test if any of the watched keys changed
diff --git a/redis/__init__.py b/redis/__init__.py
index 369d39e..57deb86 100644
--- a/redis/__init__.py
+++ b/redis/__init__.py
@@ -9,6 +9,7 @@ from redis.connection import (
from redis.utils import from_url
from redis.exceptions import (
AuthenticationError,
+ AuthenticationWrongNumberOfArgsError,
BusyLoadingError,
ChildDeadlockedError,
ConnectionError,
@@ -35,6 +36,7 @@ VERSION = tuple(map(int_or_str, __version__.split('.')))
__all__ = [
'AuthenticationError',
+ 'AuthenticationWrongNumberOfArgsError',
'BlockingConnectionPool',
'BusyLoadingError',
'ChildDeadlockedError',
diff --git a/redis/connection.py b/redis/connection.py
index 7efb0a2..a013145 100755
--- a/redis/connection.py
+++ b/redis/connection.py
@@ -17,6 +17,7 @@ from redis._compat import (xrange, imap, byte_to_chr, unicode, long,
sendall, shutdown, ssl_wrap_socket)
from redis.exceptions import (
AuthenticationError,
+ AuthenticationWrongNumberOfArgsError,
BusyLoadingError,
ChildDeadlockedError,
ConnectionError,
@@ -135,6 +136,8 @@ class BaseParser(object):
'max number of clients reached': ConnectionError,
'Client sent AUTH, but no password is set': AuthenticationError,
'invalid password': AuthenticationError,
+ 'wrong number of arguments for \'auth\' command':
+ AuthenticationWrongNumberOfArgsError,
},
'EXECABORT': ExecAbortError,
'LOADING': BusyLoadingError,
@@ -630,7 +633,18 @@ class Connection(object):
# avoid checking health here -- PING will fail if we try
# to check the health prior to the AUTH
self.send_command('AUTH', *auth_args, check_health=False)
- if nativestr(self.read_response()) != 'OK':
+
+ try:
+ auth_response = self.read_response()
+ except AuthenticationWrongNumberOfArgsError:
+ # a username and password were specified but the Redis
+ # server seems to be < 6.0.0 which expects a single password
+ # arg. retry auth with just the password.
+ # https://github.com/andymccurdy/redis-py/issues/1274
+ self.send_command('AUTH', self.password, check_health=False)
+ auth_response = self.read_response()
+
+ if nativestr(auth_response) != 'OK':
raise AuthenticationError('Invalid Username or Password')
# if a client_name is given, set it
diff --git a/redis/exceptions.py b/redis/exceptions.py
index 6f47024..760af66 100644
--- a/redis/exceptions.py
+++ b/redis/exceptions.py
@@ -72,3 +72,11 @@ class LockNotOwnedError(LockError):
class ChildDeadlockedError(Exception):
"Error indicating that a child process is deadlocked after a fork()"
pass
+
+
+class AuthenticationWrongNumberOfArgsError(ResponseError):
+ """
+ An error to indicate that the wrong number of args
+ were sent to the AUTH command
+ """
+ pass