diff options
author | Bar Shaul <88437685+barshaul@users.noreply.github.com> | 2022-11-10 12:38:47 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-10 12:38:47 +0200 |
commit | bb06ccd52924800ac501d17c8a42038c8e5c5770 (patch) | |
tree | df9fa0ae2c2553ecc3779b3f7166d6cad4855c03 /redis/connection.py | |
parent | fb647430f00cc7bb67c978e75f2dabc661567779 (diff) | |
download | redis-py-bb06ccd52924800ac501d17c8a42038c8e5c5770.tar.gz |
CredentialsProvider class added to support password rotation (#2261)
* A CredentialsProvider class has been added to allow the user to add his own provider for password rotation
* Moved CredentialsProvider to a separate file, added type hints
* Changed username and password to properties
* Added: StaticCredentialProvider, examples, tests
Changed: CredentialsProvider to CredentialProvider
Fixed: calling AUTH only with password
* Changed private members' prefix to __
* fixed linters
* fixed auth test
* fixed credential test
* Raise an error if username or password are passed along with credential_provider
* fixing linters
* fixing test
* Changed dundered to single per side underscore
* Changed Connection class members username and password to properties to enable backward compatibility with changing the members value on existing connection.
* Reverting last commit and adding backward compatibility to 'username' and 'password' inside on_connect function
* Refactored CredentialProvider class
* Fixing tuple type to Tuple
* Fixing optional string members in UsernamePasswordCredentialProvider
* Fixed credential test
* Added credential provider support to AsyncRedis
* linters
* linters
* linters
* linters - black
Co-authored-by: dvora-h <67596500+dvora-h@users.noreply.github.com>
Co-authored-by: dvora-h <dvora.heller@redis.com>
Diffstat (limited to 'redis/connection.py')
-rwxr-xr-x | redis/connection.py | 39 |
1 files changed, 30 insertions, 9 deletions
diff --git a/redis/connection.py b/redis/connection.py index fecb06b..a2b0074 100755 --- a/redis/connection.py +++ b/redis/connection.py @@ -8,9 +8,11 @@ import weakref from itertools import chain from queue import Empty, Full, LifoQueue from time import time +from typing import Optional from urllib.parse import parse_qs, unquote, urlparse from redis.backoff import NoBackoff +from redis.credentials import CredentialProvider, UsernamePasswordCredentialProvider from redis.exceptions import ( AuthenticationError, AuthenticationWrongNumberOfArgsError, @@ -502,6 +504,7 @@ class Connection: username=None, retry=None, redis_connect_func=None, + credential_provider: Optional[CredentialProvider] = None, ): """ Initialize a new Connection. @@ -510,13 +513,21 @@ class Connection: `retry` to a valid `Retry` object. To retry on TimeoutError, `retry_on_timeout` can also be set to `True`. """ + if (username or password) and credential_provider is not None: + raise DataError( + "'username' and 'password' cannot be passed along with 'credential_" + "provider'. Please provide only one of the following arguments: \n" + "1. 'password' and (optional) 'username'\n" + "2. 'credential_provider'" + ) self.pid = os.getpid() self.host = host self.port = int(port) self.db = db - self.username = username self.client_name = client_name + self.credential_provider = credential_provider self.password = password + self.username = username self.socket_timeout = socket_timeout self.socket_connect_timeout = socket_connect_timeout or socket_timeout self.socket_keepalive = socket_keepalive @@ -675,12 +686,13 @@ class Connection: "Initialize the connection, authenticate and select a database" self._parser.on_connect(self) - # if username and/or password are set, authenticate - if self.username or self.password: - if self.username: - auth_args = (self.username, self.password or "") - else: - auth_args = (self.password,) + # if credential provider or username and/or password are set, authenticate + if self.credential_provider or (self.username or self.password): + cred_provider = ( + self.credential_provider + or UsernamePasswordCredentialProvider(self.username, self.password) + ) + auth_args = cred_provider.get_credentials() # 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) @@ -692,7 +704,7 @@ class Connection: # 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) + self.send_command("AUTH", auth_args[-1], check_health=False) auth_response = self.read_response() if str_if_bytes(auth_response) != "OK": @@ -1050,6 +1062,7 @@ class UnixDomainSocketConnection(Connection): client_name=None, retry=None, redis_connect_func=None, + credential_provider: Optional[CredentialProvider] = None, ): """ Initialize a new UnixDomainSocketConnection. @@ -1058,12 +1071,20 @@ class UnixDomainSocketConnection(Connection): `retry` to a valid `Retry` object. To retry on TimeoutError, `retry_on_timeout` can also be set to `True`. """ + if (username or password) and credential_provider is not None: + raise DataError( + "'username' and 'password' cannot be passed along with 'credential_" + "provider'. Please provide only one of the following arguments: \n" + "1. 'password' and (optional) 'username'\n" + "2. 'credential_provider'" + ) self.pid = os.getpid() self.path = path self.db = db - self.username = username self.client_name = client_name + self.credential_provider = credential_provider self.password = password + self.username = username self.socket_timeout = socket_timeout self.retry_on_timeout = retry_on_timeout if retry_on_error is SENTINEL: |