Skip to content

Commit eebe6d3

Browse files
committed
Changed Connection class members username and password to properties to enable backward compatibility with changing the members value on existing connection.
1 parent 62a025e commit eebe6d3

File tree

3 files changed

+99
-13
lines changed

3 files changed

+99
-13
lines changed

redis/connection.py

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from itertools import chain
99
from queue import Empty, Full, LifoQueue
1010
from time import time
11+
from typing import Optional
1112
from urllib.parse import parse_qs, unquote, urlparse
1213

1314
from redis.backoff import NoBackoff
@@ -526,8 +527,6 @@ def __init__(
526527
)
527528

528529
self.credential_provider = credential_provider
529-
self.username = username
530-
self.password = password
531530
if username or password:
532531
# Keep backward compatibility by creating a static credential provider
533532
# for the passed username and password
@@ -564,6 +563,38 @@ def __init__(
564563
self._connect_callbacks = []
565564
self._buffer_cutoff = 6000
566565

566+
@property
567+
def password(self) -> Optional[str]:
568+
if self.credential_provider is not None:
569+
return self.credential_provider.password
570+
else:
571+
return None
572+
573+
@password.setter
574+
def password(self, value: Optional[str]):
575+
if value is None:
576+
# Delete the credential provider
577+
self.credential_provider = None
578+
return
579+
if self.credential_provider is not None:
580+
self.credential_provider.password = value
581+
else:
582+
self.credential_provider = StaticCredentialProvider(password=value)
583+
584+
@property
585+
def username(self) -> Optional[str]:
586+
if self.credential_provider is not None:
587+
return self.credential_provider.username
588+
else:
589+
return None
590+
591+
@username.setter
592+
def username(self, value: Optional[str]):
593+
if self.credential_provider is not None:
594+
self.credential_provider.username = value
595+
else:
596+
self.credential_provider = StaticCredentialProvider(username=value)
597+
567598
def __repr__(self):
568599
repr_args = ",".join([f"{k}={v}" for k, v in self.repr_pieces()])
569600
return f"{self.__class__.__name__}<{repr_args}>"
@@ -1087,8 +1118,6 @@ def __init__(
10871118
"2. 'credential_provider'"
10881119
)
10891120
self.credential_provider = credential_provider
1090-
self.username = username
1091-
self.password = password
10921121
if username or password:
10931122
# Keep backward compatibility by creating a static credential provider
10941123
# for the passed username and password
@@ -1121,6 +1150,38 @@ def __init__(
11211150
self._connect_callbacks = []
11221151
self._buffer_cutoff = 6000
11231152

1153+
@property
1154+
def password(self) -> Optional[str]:
1155+
if self.credential_provider is not None:
1156+
return self.credential_provider.password
1157+
else:
1158+
return None
1159+
1160+
@password.setter
1161+
def password(self, value: Optional[str]):
1162+
if value is None:
1163+
# Delete the credential provider
1164+
self.credential_provider = None
1165+
return
1166+
if self.credential_provider is not None:
1167+
self.credential_provider.password = value
1168+
else:
1169+
self.credential_provider = StaticCredentialProvider(password=value)
1170+
1171+
@property
1172+
def username(self) -> Optional[str]:
1173+
if self.credential_provider is not None:
1174+
return self.credential_provider.username
1175+
else:
1176+
return None
1177+
1178+
@username.setter
1179+
def username(self, value: Optional[str]):
1180+
if self.credential_provider is not None:
1181+
self.credential_provider.username = value
1182+
else:
1183+
self.credential_provider = StaticCredentialProvider(username=value)
1184+
11241185
def repr_pieces(self):
11251186
pieces = [("path", self.path), ("db", self.db)]
11261187
if self.client_name:

redis/credentials.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ def supplier(self, arg1, arg2, ...) -> (username, password)
1818
:param supplier_args: arguments to pass to the supplier function
1919
:param supplier_kwargs: keyword arguments to pass to the supplier function
2020
"""
21-
self._username_ = "" if username is None else username
22-
self._password_ = "" if password is None else password
21+
self._username_ = username if username is not None else ""
22+
self._password_ = password if password is not None else ""
2323
self.supplier = supplier
2424
self.supplier_args = supplier_args
2525
self.supplier_kwargs = supplier_kwargs
@@ -46,7 +46,7 @@ def password(self):
4646

4747
@password.setter
4848
def password(self, value):
49-
self._password_ = value
49+
self._password_ = value if value is not None else ""
5050

5151
@property
5252
def username(self):
@@ -58,7 +58,7 @@ def username(self):
5858

5959
@username.setter
6060
def username(self, value):
61-
self._username_ = value
61+
self._username_ = value if value is not None else ""
6262

6363

6464
class StaticCredentialProvider(CredentialProvider):
@@ -70,8 +70,4 @@ class StaticCredentialProvider(CredentialProvider):
7070
def __init__(
7171
self, username: Union[str, None] = "", password: Union[str, None] = ""
7272
):
73-
super().__init__(
74-
username=username,
75-
password=password,
76-
credential_provider=lambda self: (self.username, self.password),
77-
)
73+
super().__init__(username=username, password=password)

tests/test_credentials.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import redis
77
from redis import AuthenticationError, DataError, ResponseError
88
from redis.credentials import CredentialProvider, StaticCredentialProvider
9+
from redis.utils import str_if_bytes
910
from tests.conftest import _get_client, skip_if_redis_enterprise
1011

1112

@@ -179,6 +180,34 @@ def test_password_and_username_together_with_cred_provider_raise_error(
179180
"'credential_provider'."
180181
)
181182

183+
@pytest.mark.onlynoncluster
184+
def test_change_username_password_on_existing_connection(self, r, request):
185+
username = "origin_username"
186+
password = "origin_password"
187+
new_username = "new_username"
188+
new_password = "new_password"
189+
init_acl_user(r, request, username, password)
190+
r2 = _get_client(
191+
redis.Redis, request, flushdb=False, username=username, password=password
192+
)
193+
assert r2.ping()
194+
conn = r2.connection_pool.get_connection("_")
195+
conn.send_command("PING")
196+
assert str_if_bytes(conn.read_response()) == "PONG"
197+
assert conn.username == username
198+
assert conn.password == password
199+
init_acl_user(r, request, new_username, new_password)
200+
conn.password = new_password
201+
conn.username = new_username
202+
assert conn.credential_provider.password == new_password
203+
assert conn.credential_provider.username == new_username
204+
conn.send_command("PING")
205+
assert str_if_bytes(conn.read_response()) == "PONG"
206+
conn.username = None
207+
assert conn.credential_provider.username == ""
208+
conn.password = None
209+
assert conn.credential_provider is None
210+
182211

183212
class TestStaticCredentialProvider:
184213
def test_static_credential_provider_acl_user_and_pass(self, r, request):

0 commit comments

Comments
 (0)