summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamie Lennox <jamielennox@redhat.com>2015-06-19 10:50:18 +0800
committerJamie Lennox <jamielennox@redhat.com>2015-07-22 23:58:30 +0000
commitb529f0a782cc751a7ce35ebf9c2a929a8134aae6 (patch)
tree23d7efa9de7952b65c23e2706c0e632f68497198
parentcd04b9217f4581f9d1d6840bc6c3c4c916b330e8 (diff)
downloadglance_store-b529f0a782cc751a7ce35ebf9c2a929a8134aae6.tar.gz
Support V3 authentication with swift
This is the easiest way to support v3 authentication in the swift backend. It allows us to specify that we want v3 authentication and provide the extra authentication attributes to swiftclient. It allows specifying auth_version via individual authentication references so that you can mix v2 and v3 authentication. Ideally in future we would move to a session orientated solution. In which case we will need to handle the existing options in a backwards compatible way and so I don't think that the extra arguments will cause any additional problems there. Change-Id: Ifb7c9be805689e938ff0e99a18fa220993862001
-rw-r--r--glance_store/_drivers/swift/store.py29
-rw-r--r--glance_store/_drivers/swift/utils.py47
-rw-r--r--glance_store/tests/etc/glance-swift.conf3
-rw-r--r--glance_store/tests/unit/test_swift_store.py55
4 files changed, 121 insertions, 13 deletions
diff --git a/glance_store/_drivers/swift/store.py b/glance_store/_drivers/swift/store.py
index 4a4bc31..6ac7740 100644
--- a/glance_store/_drivers/swift/store.py
+++ b/glance_store/_drivers/swift/store.py
@@ -48,10 +48,6 @@ DEFAULT_LARGE_OBJECT_CHUNK_SIZE = 200 # 200M
ONE_MB = units.k * units.Ki # Here we used the mixed meaning of MB
_SWIFT_OPTS = [
- cfg.StrOpt('swift_store_auth_version', default='2',
- help=_('Version of the authentication service to use. '
- 'Valid versions are 2 for keystone and 1 for swauth '
- 'and rackspace. (deprecated)')),
cfg.BoolOpt('swift_store_auth_insecure', default=False,
help=_('If True, swiftclient won\'t check for a valid SSL '
'certificate when authenticating.')),
@@ -728,8 +724,14 @@ class SingleTenantStore(BaseStore):
self.ref_params = sutils.SwiftParams(self.conf).params
def configure(self, re_raise_bsc=False):
- super(SingleTenantStore, self).configure(re_raise_bsc=re_raise_bsc)
+ # set configuration before super so configure_add can override
self.auth_version = self._option_get('swift_store_auth_version')
+ self.user_domain_id = None
+ self.user_domain_name = None
+ self.project_domain_id = None
+ self.project_domain_name = None
+
+ super(SingleTenantStore, self).configure(re_raise_bsc=re_raise_bsc)
def configure_add(self):
default_ref = self.conf.glance_store.default_swift_reference
@@ -746,8 +748,15 @@ class SingleTenantStore(BaseStore):
else:
self.scheme = 'swift+https'
self.container = self.conf.glance_store.swift_store_container
+ self.auth_version = default_swift_reference.get('auth_version')
self.user = default_swift_reference.get('user')
self.key = default_swift_reference.get('key')
+ self.user_domain_id = default_swift_reference.get('user_domain_id')
+ self.user_domain_name = default_swift_reference.get('user_domain_name')
+ self.project_domain_id = default_swift_reference.get(
+ 'project_domain_id')
+ self.project_domain_name = default_swift_reference.get(
+ 'project_domain_name')
if not (self.user or self.key):
reason = _("A value for swift_store_ref_params is required.")
@@ -811,7 +820,7 @@ class SingleTenantStore(BaseStore):
if not auth_url.endswith('/'):
auth_url += '/'
- if self.auth_version == '2':
+ if self.auth_version in ('2', '3'):
try:
tenant_name, user = location.user.split(':')
except ValueError:
@@ -828,6 +837,14 @@ class SingleTenantStore(BaseStore):
os_options['region_name'] = self.region
os_options['endpoint_type'] = self.endpoint_type
os_options['service_type'] = self.service_type
+ if self.user_domain_id:
+ os_options['user_domain_id'] = self.user_domain_id
+ if self.user_domain_name:
+ os_options['user_domain_name'] = self.user_domain_name
+ if self.project_domain_id:
+ os_options['project_domain_id'] = self.project_domain_id
+ if self.project_domain_name:
+ os_options['project_domain_name'] = self.project_domain_name
return swiftclient.Connection(
auth_url, user, location.key, preauthurl=self.conf_endpoint,
diff --git a/glance_store/_drivers/swift/utils.py b/glance_store/_drivers/swift/utils.py
index 9489c17..d43abf8 100644
--- a/glance_store/_drivers/swift/utils.py
+++ b/glance_store/_drivers/swift/utils.py
@@ -27,23 +27,38 @@ swift_opts = [
default="ref1",
help=i18n._('The reference to the default swift account/backing'
' store parameters to use for adding new images.')),
+ cfg.StrOpt('swift_store_auth_version', default='2',
+ help=i18n._('Version of the authentication service to use. '
+ 'Valid versions are 2 and 3 for keystone and 1 '
+ '(deprecated) for swauth and rackspace. '
+ '(deprecated - use "auth_version" in '
+ 'swift_store_config_file)')),
cfg.StrOpt('swift_store_auth_address',
help=i18n._('The address where the Swift authentication '
- 'service is listening.(deprecated)')),
+ 'service is listening. (deprecated - use '
+ '"auth_address" in swift_store_config_file)')),
cfg.StrOpt('swift_store_user', secret=True,
help=i18n._('The user to authenticate against the Swift '
- 'authentication service (deprecated)')),
+ 'authentication service (deprecated - use "user" '
+ 'in swift_store_config_file)')),
cfg.StrOpt('swift_store_key', secret=True,
help=i18n._('Auth key for the user authenticating against the '
- 'Swift authentication service. (deprecated)')),
+ 'Swift authentication service. (deprecated - use '
+ '"key" in swift_store_config_file)')),
cfg.StrOpt('swift_store_config_file', secret=True,
help=i18n._('The config file that has the swift account(s)'
'configs.')),
]
+_config_defaults = {'user_domain_id': None,
+ 'user_domain_name': None,
+ 'project_domain_id': None,
+ 'project_domain_name': None}
+
# NOTE(bourke): The default dict_type is collections.OrderedDict in py27, but
# we must set manually for compatibility with py26
-CONFIG = configparser.SafeConfigParser(dict_type=OrderedDict)
+CONFIG = configparser.SafeConfigParser(defaults=_config_defaults,
+ dict_type=OrderedDict)
LOG = logging.getLogger(__name__)
@@ -74,6 +89,11 @@ class SwiftParams(object):
default['user'] = glance_store.swift_store_user
default['key'] = glance_store.swift_store_key
default['auth_address'] = glance_store.swift_store_auth_address
+ default['project_domain_id'] = None
+ default['project_domain_name'] = None
+ default['user_domain_id'] = None
+ default['user_domain_name'] = None
+ default['auth_version'] = glance_store.swift_store_auth_version
return {glance_store.default_swift_reference: default}
return {}
@@ -92,12 +112,25 @@ class SwiftParams(object):
reason=msg)
account_params = {}
account_references = CONFIG.sections()
+
for ref in account_references:
reference = {}
try:
- reference['auth_address'] = CONFIG.get(ref, 'auth_address')
- reference['user'] = CONFIG.get(ref, 'user')
- reference['key'] = CONFIG.get(ref, 'key')
+ for param in ('auth_address',
+ 'user',
+ 'key',
+ 'project_domain_id',
+ 'project_domain_name',
+ 'user_domain_id',
+ 'user_domain_name'):
+ reference[param] = CONFIG.get(ref, param)
+
+ try:
+ reference['auth_version'] = CONFIG.get(ref, 'auth_version')
+ except configparser.NoOptionError:
+ av = self.conf.glance_store.swift_store_auth_version
+ reference['auth_version'] = av
+
account_params[ref] = reference
except (ValueError, SyntaxError, configparser.NoOptionError) as e:
LOG.exception(i18n._("Invalid format of swift store config"
diff --git a/glance_store/tests/etc/glance-swift.conf b/glance_store/tests/etc/glance-swift.conf
index 956433b..c5af3dd 100644
--- a/glance_store/tests/etc/glance-swift.conf
+++ b/glance_store/tests/etc/glance-swift.conf
@@ -6,6 +6,9 @@ auth_address = example.com
[ref2]
user = user2
key = key2
+user_domain_id = default
+project_domain_id = default
+auth_version = 3
auth_address = http://example.com
[store_2]
diff --git a/glance_store/tests/unit/test_swift_store.py b/glance_store/tests/unit/test_swift_store.py
index b322576..f738cf9 100644
--- a/glance_store/tests/unit/test_swift_store.py
+++ b/glance_store/tests/unit/test_swift_store.py
@@ -1041,6 +1041,15 @@ class TestStoreAuthV2(TestStoreAuthV1):
self.assertEqual('swift', loc.store_name)
+class TestStoreAuthV3(TestStoreAuthV1):
+
+ def getConfig(self):
+ conf = super(TestStoreAuthV3, self).getConfig()
+ conf['swift_store_auth_version'] = '3'
+ conf['swift_store_user'] = 'tenant:user1'
+ return conf
+
+
class FakeConnection(object):
def __init__(self, authurl, user, key, retries=5, preauthurl=None,
preauthtoken=None, starting_backoff=1, tenant_name=None,
@@ -1202,6 +1211,52 @@ class TestSingleTenantStoreConnections(base.StoreBaseTest):
self.location.parse_uri,
self.location.uri)
+ def test_ref_overrides_defaults(self):
+ self.config(swift_store_auth_version='2',
+ swift_store_user='testuser',
+ swift_store_key='testpass',
+ swift_store_auth_address='testaddress',
+ swift_store_endpoint_type='internalURL',
+ swift_store_config_file='somefile')
+
+ self.store.ref_params = {'ref1': {'auth_address': 'authurl.com',
+ 'auth_version': '3',
+ 'user': 'user:pass',
+ 'user_domain_id': 'default',
+ 'user_domain_name': 'ignored',
+ 'project_domain_id': 'default',
+ 'project_domain_name': 'ignored'}}
+
+ self.store.configure()
+
+ self.assertEqual('user:pass', self.store.user)
+ self.assertEqual('3', self.store.auth_version)
+ self.assertEqual('authurl.com', self.store.auth_address)
+ self.assertEqual('default', self.store.user_domain_id)
+ self.assertEqual('ignored', self.store.user_domain_name)
+ self.assertEqual('default', self.store.project_domain_id)
+ self.assertEqual('ignored', self.store.project_domain_name)
+
+ def test_with_v3_auth(self):
+ self.store.ref_params = {'ref1': {'auth_address': 'authurl.com',
+ 'auth_version': '3',
+ 'user': 'user:pass',
+ 'key': 'password',
+ 'user_domain_id': 'default',
+ 'user_domain_name': 'ignored',
+ 'project_domain_id': 'default',
+ 'project_domain_name': 'ignored'}}
+ self.store.configure()
+ connection = self.store.get_connection(self.location)
+ self.assertEqual('3', connection.auth_version)
+ self.assertEqual(connection.os_options,
+ {'service_type': 'object-store',
+ 'endpoint_type': 'publicURL',
+ 'user_domain_id': 'default',
+ 'user_domain_name': 'ignored',
+ 'project_domain_id': 'default',
+ 'project_domain_name': 'ignored'})
+
class TestMultiTenantStoreConnections(base.StoreBaseTest):
def setUp(self):