diff options
author | Luca Cillario <luca.cillario.95@gmail.com> | 2022-08-30 01:07:28 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-08-30 02:07:28 +0300 |
commit | bd0e8f26d8af5722a8b040bed6a75831bade81df (patch) | |
tree | 080326627cbf210ffb779a8420646074c6bb6d2a | |
parent | fb54bddae9d460e227bfff77724e066b4a0ca522 (diff) | |
download | redis-py-bd0e8f26d8af5722a8b040bed6a75831bade81df.tar.gz |
Handle auth errors for newer versions of Redis. (#2325) (#2329)
-rw-r--r-- | redis/asyncio/connection.py | 11 | ||||
-rwxr-xr-x | redis/connection.py | 12 | ||||
-rw-r--r-- | tests/test_commands.py | 26 | ||||
-rw-r--r-- | tests/test_connection_pool.py | 28 |
4 files changed, 67 insertions, 10 deletions
diff --git a/redis/asyncio/connection.py b/redis/asyncio/connection.py index 6823dcb..db8c240 100644 --- a/redis/asyncio/connection.py +++ b/redis/asyncio/connection.py @@ -87,6 +87,15 @@ MODULE_EXPORTS_DATA_TYPES_ERROR = ( "exports one or more module-side data " "types, can't unload" ) +# user send an AUTH cmd to a server without authorization configured +NO_AUTH_SET_ERROR = { + # Redis >= 6.0 + "AUTH <password> called without any password " + "configured for the default user. Are you sure " + "your configuration is correct?": AuthenticationError, + # Redis < 6.0 + "Client sent AUTH, but no password is set": AuthenticationError, +} class _HiredisReaderArgs(TypedDict, total=False): @@ -160,7 +169,9 @@ class BaseParser: MODULE_EXPORTS_DATA_TYPES_ERROR: ModuleError, NO_SUCH_MODULE_ERROR: ModuleError, MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError, + **NO_AUTH_SET_ERROR, }, + "WRONGPASS": AuthenticationError, "EXECABORT": ExecAbortError, "LOADING": BusyLoadingError, "NOSCRIPT": NoScriptError, diff --git a/redis/connection.py b/redis/connection.py index 96645ba..3281b14 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -80,6 +80,15 @@ MODULE_EXPORTS_DATA_TYPES_ERROR = ( "exports one or more module-side data " "types, can't unload" ) +# user send an AUTH cmd to a server without authorization configured +NO_AUTH_SET_ERROR = { + # Redis >= 6.0 + "AUTH <password> called without any password " + "configured for the default user. Are you sure " + "your configuration is correct?": AuthenticationError, + # Redis < 6.0 + "Client sent AUTH, but no password is set": AuthenticationError, +} class Encoder: @@ -127,7 +136,6 @@ class BaseParser: EXCEPTION_CLASSES = { "ERR": { "max number of clients reached": ConnectionError, - "Client sent AUTH, but no password is set": AuthenticationError, "invalid password": AuthenticationError, # some Redis server versions report invalid command syntax # in lowercase @@ -141,7 +149,9 @@ class BaseParser: MODULE_EXPORTS_DATA_TYPES_ERROR: ModuleError, NO_SUCH_MODULE_ERROR: ModuleError, MODULE_UNLOAD_NOT_POSSIBLE_ERROR: ModuleError, + **NO_AUTH_SET_ERROR, }, + "WRONGPASS": AuthenticationError, "EXECABORT": ExecAbortError, "LOADING": BusyLoadingError, "NOSCRIPT": NoScriptError, diff --git a/tests/test_commands.py b/tests/test_commands.py index de8020c..1c9a5c2 100644 --- a/tests/test_commands.py +++ b/tests/test_commands.py @@ -68,6 +68,14 @@ class TestResponseCallbacks: class TestRedisCommands: @skip_if_redis_enterprise() def test_auth(self, r, request): + # sending an AUTH command before setting a user/password on the + # server should return an AuthenticationError + with pytest.raises(exceptions.AuthenticationError): + r.auth("some_password") + + with pytest.raises(exceptions.AuthenticationError): + r.auth("some_password", "some_user") + # first, test for default user (`username` is supposed to be optional) default_username = "default" temp_pass = "temp_pass" @@ -81,9 +89,19 @@ class TestRedisCommands: def teardown(): try: - r.auth(temp_pass) - except exceptions.ResponseError: - r.auth("default", "") + # this is needed because after an AuthenticationError the connection + # is closed, and if we send an AUTH command a new connection is + # created, but in this case we'd get an "Authentication required" + # error when switching to the db 9 because we're not authenticated yet + # setting the password on the connection itself triggers the + # authentication in the connection's `on_connect` method + r.connection.password = temp_pass + except AttributeError: + # connection field is not set in Redis Cluster, but that's ok + # because the problem discussed above does not apply to Redis Cluster + pass + + r.auth(temp_pass) r.config_set("requirepass", "") r.acl_deluser(username) @@ -95,7 +113,7 @@ class TestRedisCommands: assert r.auth(username=username, password="strong_password") is True - with pytest.raises(exceptions.ResponseError): + with pytest.raises(exceptions.AuthenticationError): r.auth(username=username, password="wrong_password") def test_command_on_invalid_key_type(self, r): diff --git a/tests/test_connection_pool.py b/tests/test_connection_pool.py index 636b04e..73eb6e1 100644 --- a/tests/test_connection_pool.py +++ b/tests/test_connection_pool.py @@ -553,22 +553,40 @@ class TestConnection: ) @skip_if_redis_enterprise() - def test_connect_no_auth_supplied_when_required(self, r): + def test_connect_no_auth_configured(self, r): """ - AuthenticationError should be raised when the server requires a - password but one isn't supplied. + AuthenticationError should be raised when the server is not configured with auth + but credentials are supplied by the user. """ + # Redis < 6 with pytest.raises(redis.AuthenticationError): r.execute_command( "DEBUG", "ERROR", "ERR Client sent AUTH, but no password is set" ) + # Redis >= 6 + with pytest.raises(redis.AuthenticationError): + r.execute_command( + "DEBUG", + "ERROR", + "ERR AUTH <password> called without any password " + "configured for the default user. Are you sure " + "your configuration is correct?", + ) + @skip_if_redis_enterprise() - def test_connect_invalid_password_supplied(self, r): - "AuthenticationError should be raised when sending the wrong password" + def test_connect_invalid_auth_credentials_supplied(self, r): + """ + AuthenticationError should be raised when sending invalid username/password + """ + # Redis < 6 with pytest.raises(redis.AuthenticationError): r.execute_command("DEBUG", "ERROR", "ERR invalid password") + # Redis >= 6 + with pytest.raises(redis.AuthenticationError): + r.execute_command("DEBUG", "ERROR", "WRONGPASS") + @pytest.mark.onlynoncluster class TestMultiConnectionClient: |