Skip to content

Commit 51d3846

Browse files
Support auth param string for Basic authentication (#15)
* Support auth param string for Basic authentication ### Motivation apache/pulsar#17482 supported basic authentication for Python client, but the `AuthenticationBasic` class accepts two positional arguments as the username and password. It's not good for extension. We should accept an auth param string like `AuthenticationOauth2` so that no changes are needed if the upstream C++ client's implementation changed, like apache/pulsar-client-cpp#37. ### Modifications To be compatible with the existing API, change the first two arguments to keyword arguments. Then, add the 3rd keyword argument to represent the auth param string. ### Verifications `test_basic_auth` and `test_invalid_basic_auth` are extended for this change. * Add method argument and related tests * Add type check for method arg
1 parent ddb1229 commit 51d3846

File tree

3 files changed

+70
-16
lines changed

3 files changed

+70
-16
lines changed

pulsar/__init__.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,18 +347,39 @@ class AuthenticationBasic(Authentication):
347347
"""
348348
Basic Authentication implementation
349349
"""
350-
def __init__(self, username, password):
350+
def __init__(self, username=None, password=None, method='basic', auth_params_string=None):
351351
"""
352352
Create the Basic authentication provider instance.
353353
354-
**Args**
354+
For example, if you want to create a basic authentication instance whose
355+
username is "my-user" and password is "my-pass", there are two ways:
355356
356-
* `username`: Used to authentication as username
357-
* `password`: Used to authentication as password
358-
"""
359-
_check_type(str, username, 'username')
360-
_check_type(str, password, 'password')
361-
self.auth = _pulsar.AuthenticationBasic(username, password)
357+
```
358+
auth = AuthenticationBasic('my-user', 'my-pass')
359+
auth = AuthenticationBasic(auth_params_string='{"username": "my-user", "password": "my-pass"}')
360+
```
361+
362+
**Args**
363+
* username : str, optional
364+
* password : str, optional
365+
* method : str, optional
366+
The authentication method name (default is 'basic')
367+
* auth_params_string : str, optional
368+
The JSON presentation of all fields above (default is None)
369+
If it's not None, the other parameters will be ignored.
370+
Here is an example JSON presentation:
371+
{"username": "my-user", "password": "my-pass", "method": "oms3.0"}
372+
The `username` and `password` fields are required. If the "method" field is not set,
373+
it will be "basic" by default.
374+
"""
375+
if auth_params_string is not None:
376+
_check_type(str, auth_params_string, 'auth_params_string')
377+
self.auth = _pulsar.AuthenticationBasic('', '', '', auth_params_string)
378+
else:
379+
_check_type(str, username, 'username')
380+
_check_type(str, password, 'password')
381+
_check_type(str, method, 'method')
382+
self.auth = _pulsar.AuthenticationBasic(username, password, method, '')
362383

363384
class Client:
364385
"""

src/authentication.cc

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,14 @@ struct AuthenticationOauth2Wrapper : public AuthenticationWrapper {
9191
};
9292

9393
struct AuthenticationBasicWrapper : public AuthenticationWrapper {
94-
AuthenticationBasicWrapper(const std::string& username, const std::string& password)
94+
AuthenticationBasicWrapper(const std::string& username, const std::string& password,
95+
const std::string& method, const std::string& authParamsString)
9596
: AuthenticationWrapper() {
96-
this->auth = AuthBasic::create(username, password);
97+
if (authParamsString.empty()) {
98+
this->auth = AuthBasic::create(username, password, method);
99+
} else {
100+
this->auth = AuthBasic::create(authParamsString);
101+
}
97102
}
98103
};
99104

@@ -115,5 +120,6 @@ void export_authentication() {
115120
init<const std::string&>());
116121

117122
class_<AuthenticationBasicWrapper, bases<AuthenticationWrapper> >(
118-
"AuthenticationBasic", init<const std::string&, const std::string&>());
123+
"AuthenticationBasic",
124+
init<const std::string&, const std::string&, const std::string&, const std::string&>());
119125
}

tests/pulsar_test.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,12 +1301,10 @@ def _check_type_error(self, fun):
13011301
with self.assertRaises(TypeError):
13021302
fun()
13031303

1304-
def test_basic_auth(self):
1305-
username = "admin"
1306-
password = "123456"
1307-
client = Client(self.adminUrl, authentication=AuthenticationBasic(username, password))
1304+
def _test_basic_auth(self, id, auth):
1305+
client = Client(self.adminUrl, authentication=auth)
13081306

1309-
topic = "persistent://private/auth/my-python-topic-basic-auth"
1307+
topic = "persistent://private/auth/my-python-topic-basic-auth-" + str(id)
13101308
consumer = client.subscribe(topic, "my-sub", consumer_type=ConsumerType.Shared)
13111309
producer = client.create_producer(topic)
13121310
producer.send(b"hello")
@@ -1316,13 +1314,42 @@ def test_basic_auth(self):
13161314
self.assertEqual(msg.data(), b"hello")
13171315
client.close()
13181316

1317+
def test_basic_auth(self):
1318+
username = "admin"
1319+
password = "123456"
1320+
self._test_basic_auth(0, AuthenticationBasic(username, password))
1321+
self._test_basic_auth(1, AuthenticationBasic(
1322+
auth_params_string='{{"username": "{}","password": "{}"}}'.format(username, password)
1323+
))
1324+
1325+
def test_basic_auth_method(self):
1326+
username = "admin"
1327+
password = "123456"
1328+
self._test_basic_auth(2, AuthenticationBasic(username, password, 'basic'))
1329+
with self.assertRaises(pulsar.AuthorizationError):
1330+
self._test_basic_auth(3, AuthenticationBasic(username, password, 'unknown'))
1331+
self._test_basic_auth(4, AuthenticationBasic(
1332+
auth_params_string='{{"username": "{}","password": "{}", "method": "basic"}}'.format(username, password)
1333+
))
1334+
with self.assertRaises(pulsar.AuthorizationError):
1335+
self._test_basic_auth(5, AuthenticationBasic(
1336+
auth_params_string='{{"username": "{}","password": "{}", "method": "unknown"}}'.format(username, password)
1337+
))
1338+
13191339
def test_invalid_basic_auth(self):
13201340
username = "invalid"
13211341
password = "123456"
13221342
client = Client(self.adminUrl, authentication=AuthenticationBasic(username, password))
13231343
topic = "persistent://private/auth/my-python-topic-invalid-basic-auth"
13241344
with self.assertRaises(pulsar.ConnectError):
13251345
client.subscribe(topic, "my-sub", consumer_type=ConsumerType.Shared)
1346+
client = Client(self.adminUrl, authentication=AuthenticationBasic(
1347+
auth_params_string='{{"username": "{}","password": "{}"}}'.format(username, password)
1348+
))
1349+
with self.assertRaises(pulsar.ConnectError):
1350+
client.subscribe(topic, "my-sub", consumer_type=ConsumerType.Shared)
1351+
with self.assertRaises(RuntimeError):
1352+
AuthenticationBasic(auth_params_string='invalid auth params')
13261353

13271354
if __name__ == "__main__":
13281355
main()

0 commit comments

Comments
 (0)