Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit 1a905fb

Browse files
committed
Allow CouchDB.session_login to take credentials as arguments
1 parent fcbe630 commit 1a905fb

File tree

4 files changed

+92
-2
lines changed

4 files changed

+92
-2
lines changed

src/cloudant/_common_util.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,19 @@ def request(self, method, url, **kwargs): # pylint: disable=W0221
370370

371371
return resp
372372

373+
def set_credentials(self, username, password):
374+
"""
375+
Set a new username and password.
376+
377+
:param str username: New username.
378+
:param str password: New password.
379+
"""
380+
if username is not None:
381+
self._username = username
382+
383+
if password is not None:
384+
self._password = password
385+
373386

374387
class IAMSession(ClientSession):
375388
"""
@@ -443,6 +456,16 @@ def request(self, method, url, **kwargs): # pylint: disable=W0221
443456

444457
return resp
445458

459+
def set_credentials(self, username, api_key):
460+
"""
461+
Set a new IAM API key.
462+
463+
:param str username: Username parameter is unused.
464+
:param str api_key: New IAM API key.
465+
"""
466+
if api_key is not None:
467+
self._api_key = api_key
468+
446469
def _get_access_token(self):
447470
"""
448471
Get IAM access token using API key.

src/cloudant/client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,15 @@ def session_cookie(self):
161161
return None
162162
return self.r_session.cookies.get('AuthSession')
163163

164-
def session_login(self):
164+
def session_login(self, user=None, passwd=None):
165165
"""
166166
Performs a session login by posting the auth information
167167
to the _session endpoint.
168168
"""
169169
if self.admin_party:
170170
return
171171

172+
self.r_session.set_credentials(user, passwd)
172173
self.r_session.login()
173174

174175
def session_logout(self):

tests/unit/client_tests.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
import os
2929
import datetime
3030

31-
from requests import ConnectTimeout
31+
from requests import ConnectTimeout, HTTPError
32+
from time import sleep
3233

3334
from cloudant import cloudant, cloudant_bluemix, couchdb, couchdb_admin_party
3435
from cloudant.client import Cloudant, CouchDB
@@ -492,6 +493,30 @@ class CloudantClientTests(UnitTestDbBase):
492493
Cloudant specific client unit tests
493494
"""
494495

496+
def test_cloudant_session_login(self):
497+
"""
498+
Test that the Cloudant client session successfully authenticates.
499+
"""
500+
self.client.connect()
501+
old_cookie = self.client.session_cookie()
502+
503+
sleep(5) # ensure we get a different cookie back
504+
505+
self.client.session_login()
506+
self.assertNotEqual(self.client.session_cookie(), old_cookie)
507+
508+
def test_cloudant_session_login_with_new_credentials(self):
509+
"""
510+
Test that the Cloudant client session fails to authenticate when
511+
passed incorrect credentials.
512+
"""
513+
self.client.connect()
514+
515+
with self.assertRaises(HTTPError) as cm:
516+
self.client.session_login('invalid-user-123', 'pa$$w0rd01')
517+
518+
self.assertTrue(str(cm.exception).find('Name or password is incorrect'))
519+
495520
def test_cloudant_context_helper(self):
496521
"""
497522
Test that the cloudant context helper works as expected.

tests/unit/iam_auth_tests.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ def _mock_cookie(expires_secs=300):
8888
rest={'HttpOnly': None},
8989
rfc2109=True)
9090

91+
def test_iam_set_credentials(self):
92+
iam = IAMSession(MOCK_API_KEY, 'http://127.0.0.1:5984')
93+
self.assertEquals(iam._api_key, MOCK_API_KEY)
94+
95+
new_api_key = 'some_new_api_key'
96+
iam.set_credentials(None, new_api_key)
97+
98+
self.assertEquals(iam._api_key, new_api_key)
99+
91100
@mock.patch('cloudant._common_util.ClientSession.request')
92101
def test_iam_get_access_token(self, m_req):
93102
m_response = mock.MagicMock()
@@ -306,6 +315,38 @@ def test_iam_client_create(self, m_req, m_login):
306315
self.assertEqual(m_req.call_count, 1)
307316
self.assertEqual(dbs, ['animaldb'])
308317

318+
@mock.patch('cloudant._common_util.IAMSession.login')
319+
@mock.patch('cloudant._common_util.IAMSession.set_credentials')
320+
def test_iam_client_session_login(self, m_set, m_login):
321+
# create IAM client
322+
client = Cloudant.iam('foo', MOCK_API_KEY)
323+
client.connect()
324+
325+
# add a valid cookie to jar
326+
client.r_session.cookies.set_cookie(self._mock_cookie())
327+
328+
client.session_login()
329+
330+
m_set.assert_called_with(None, None)
331+
self.assertEqual(m_login.call_count, 2)
332+
self.assertEqual(m_set.call_count, 2)
333+
334+
@mock.patch('cloudant._common_util.IAMSession.login')
335+
@mock.patch('cloudant._common_util.IAMSession.set_credentials')
336+
def test_iam_client_session_login_with_new_credentials(self, m_set, m_login):
337+
# create IAM client
338+
client = Cloudant.iam('foo', MOCK_API_KEY)
339+
client.connect()
340+
341+
# add a valid cookie to jar
342+
client.r_session.cookies.set_cookie(self._mock_cookie())
343+
344+
client.session_login('bar', 'baz') # new creds
345+
346+
m_set.assert_called_with('bar', 'baz')
347+
self.assertEqual(m_login.call_count, 2)
348+
self.assertEqual(m_set.call_count, 2)
349+
309350

310351
if __name__ == '__main__':
311352
unittest.main()

0 commit comments

Comments
 (0)