diff options
author | Andy McCurdy <andy@andymccurdy.com> | 2020-01-31 12:18:12 -0800 |
---|---|---|
committer | Andy McCurdy <andy@andymccurdy.com> | 2020-01-31 12:18:12 -0800 |
commit | e39ae455dfa259cdbb0c1cee2e4cb50c17ee22bf (patch) | |
tree | 342683bbb84d6d55c54bc166cae1dac82becf8a1 | |
parent | ab1c659aaac8a59c4e4055e1598920706192fa42 (diff) | |
download | redis-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-- | CHANGES | 9 | ||||
-rw-r--r-- | redis/__init__.py | 2 | ||||
-rwxr-xr-x | redis/connection.py | 16 | ||||
-rw-r--r-- | redis/exceptions.py | 8 |
4 files changed, 34 insertions, 1 deletions
@@ -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 |