Adding keystoneauth sessions support

This patch allows authentication in swiftclient with a keystonauth
session.

Co-Authored-By: Tim Burke <tim@swiftstack.com>

Change-Id: Ia3fd947ff619c11ff0ce474897533dcf7b49d9b3
Closes-Bug: 1518938
This commit is contained in:
Paulo Ewerton 2016-03-29 19:10:42 +00:00 committed by Tim Burke
parent f9d0657e70
commit 73e4296a38
3 changed files with 89 additions and 10 deletions
doc/source
swiftclient
tests/unit

@ -18,6 +18,28 @@ version are detailed below, but are
just a subset of those that can be used to successfully authenticate. These just a subset of those that can be used to successfully authenticate. These
are the most common and recommended combinations. are the most common and recommended combinations.
Keystone Session
~~~~~~~~~~~~~~~~
.. code-block:: python
from keystoneauth1 import session
from keystoneauth1 import v3
# Create a password auth plugin
auth = v3.Password(auth_url='http://127.0.0.1:5000/v3/',
username='tester',
password='testing',
user_domain_name='Default',
project_name='Default',
project_domain_name='Default')
# Create session
session = session.Session(auth=auth)
# Create swiftclient Connection
swift_conn = Connection(session=session)
Keystone v3 Keystone v3
~~~~~~~~~~~ ~~~~~~~~~~~

@ -609,6 +609,7 @@ def get_auth(auth_url, user, key, **kwargs):
use of this network path causes no bandwidth charges but requires the use of this network path causes no bandwidth charges but requires the
client to be running on Rackspace's ServiceNet network. client to be running on Rackspace's ServiceNet network.
""" """
session = kwargs.get('session', None)
auth_version = kwargs.get('auth_version', '1') auth_version = kwargs.get('auth_version', '1')
os_options = kwargs.get('os_options', {}) os_options = kwargs.get('os_options', {})
@ -617,7 +618,14 @@ def get_auth(auth_url, user, key, **kwargs):
cert = kwargs.get('cert') cert = kwargs.get('cert')
cert_key = kwargs.get('cert_key') cert_key = kwargs.get('cert_key')
timeout = kwargs.get('timeout', None) timeout = kwargs.get('timeout', None)
if auth_version in AUTH_VERSIONS_V1:
if session:
service_type = os_options.get('service_type', 'object-store')
interface = os_options.get('endpoint_type', 'public')
storage_url = session.get_endpoint(service_type=service_type,
interface=interface)
token = session.get_token()
elif auth_version in AUTH_VERSIONS_V1:
storage_url, token = get_auth_1_0(auth_url, storage_url, token = get_auth_1_0(auth_url,
user, user,
key, key,
@ -654,8 +662,8 @@ def get_auth(auth_url, user, key, **kwargs):
timeout=timeout, timeout=timeout,
auth_version=auth_version) auth_version=auth_version)
else: else:
raise ClientException('Unknown auth_version %s specified.' raise ClientException('Unknown auth_version %s specified and no '
% auth_version) 'session found.' % auth_version)
# Override storage url, if necessary # Override storage url, if necessary
if os_options.get('object_storage_url'): if os_options.get('object_storage_url'):
@ -1414,7 +1422,7 @@ class Connection(object):
os_options=None, auth_version="1", cacert=None, os_options=None, auth_version="1", cacert=None,
insecure=False, cert=None, cert_key=None, insecure=False, cert=None, cert_key=None,
ssl_compression=True, retry_on_ratelimit=False, ssl_compression=True, retry_on_ratelimit=False,
timeout=None): timeout=None, session=None):
""" """
:param authurl: authentication URL :param authurl: authentication URL
:param user: user name to authenticate as :param user: user name to authenticate as
@ -1449,7 +1457,9 @@ class Connection(object):
this parameter to True will cause a retry this parameter to True will cause a retry
after a backoff. after a backoff.
:param timeout: The connect timeout for the HTTP connection. :param timeout: The connect timeout for the HTTP connection.
:param session: A keystoneauth session object.
""" """
self.session = session
self.authurl = authurl self.authurl = authurl
self.user = user self.user = user
self.key = key self.key = key
@ -1493,7 +1503,7 @@ class Connection(object):
def get_auth(self): def get_auth(self):
self.url, self.token = get_auth(self.authurl, self.user, self.key, self.url, self.token = get_auth(self.authurl, self.user, self.key,
snet=self.snet, session=self.session, snet=self.snet,
auth_version=self.auth_version, auth_version=self.auth_version,
os_options=self.os_options, os_options=self.os_options,
cacert=self.cacert, cacert=self.cacert,
@ -1512,8 +1522,8 @@ class Connection(object):
None) None)
service_user = opts.get('service_username', None) service_user = opts.get('service_username', None)
service_key = opts.get('service_key', None) service_key = opts.get('service_key', None)
return get_auth(self.authurl, service_user, return get_auth(self.authurl, service_user, service_key,
service_key, session=self.session,
snet=self.snet, snet=self.snet,
auth_version=self.auth_version, auth_version=self.auth_version,
os_options=service_options, os_options=service_options,
@ -1577,10 +1587,15 @@ class Connection(object):
logger.exception(err) logger.exception(err)
raise raise
if err.http_status == 401: if err.http_status == 401:
if self.session:
should_retry = self.session.invalidate()
else:
# Without a proper session, just check for auth creds
should_retry = all((self.authurl, self.user, self.key))
self.url = self.token = self.service_token = None self.url = self.token = self.service_token = None
if retried_auth or not all((self.authurl,
self.user, if retried_auth or not should_retry:
self.key)):
logger.exception(err) logger.exception(err)
raise raise
retried_auth = True retried_auth = True

@ -561,6 +561,15 @@ class TestGetAuth(MockHttpTest):
self.assertTrue(url.startswith("http")) self.assertTrue(url.startswith("http"))
self.assertTrue(token) self.assertTrue(token)
def test_auth_with_session(self):
mock_session = mock.MagicMock()
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
mock_session.get_token.return_value = 'token'
url, token = c.get_auth('http://www.test.com', 'asdf', 'asdf',
session=mock_session)
self.assertEqual(url, 'http://storagehost/v1/acct')
self.assertTrue(token)
class TestGetAccount(MockHttpTest): class TestGetAccount(MockHttpTest):
@ -1868,6 +1877,39 @@ class TestConnection(MockHttpTest):
('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'token'}), ('HEAD', '/v1/AUTH_test', '', {'x-auth-token': 'token'}),
]) ])
def test_session_no_invalidate(self):
mock_session = mock.MagicMock()
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
mock_session.get_token.return_value = 'expired'
mock_session.invalidate.return_value = False
conn = c.Connection(session=mock_session)
fake_conn = self.fake_http_connection(401)
with mock.patch.multiple('swiftclient.client',
http_connection=fake_conn,
sleep=mock.DEFAULT):
self.assertRaises(c.ClientException, conn.head_account)
self.assertEqual(mock_session.get_token.mock_calls, [mock.call()])
self.assertEqual(mock_session.invalidate.mock_calls, [mock.call()])
def test_session_can_invalidate(self):
mock_session = mock.MagicMock()
mock_session.get_endpoint.return_value = 'http://storagehost/v1/acct'
mock_session.get_token.side_effect = ['expired', 'token']
mock_session.invalidate.return_value = True
conn = c.Connection(session=mock_session)
fake_conn = self.fake_http_connection(401, 200)
with mock.patch.multiple('swiftclient.client',
http_connection=fake_conn,
sleep=mock.DEFAULT):
conn.head_account()
self.assertRequests([
('HEAD', '/v1/acct', '', {'x-auth-token': 'expired'}),
('HEAD', '/v1/acct', '', {'x-auth-token': 'token'}),
])
self.assertEqual(mock_session.get_token.mock_calls, [
mock.call(), mock.call()])
self.assertEqual(mock_session.invalidate.mock_calls, [mock.call()])
def test_preauth_token_with_no_storage_url_requires_auth(self): def test_preauth_token_with_no_storage_url_requires_auth(self):
conn = c.Connection( conn = c.Connection(
'http://auth.example.com', 'user', 'password', 'http://auth.example.com', 'user', 'password',