diff options
author | Jamie Lennox <jamielennox@redhat.com> | 2015-06-19 10:50:18 +0800 |
---|---|---|
committer | Jamie Lennox <jamielennox@redhat.com> | 2015-07-22 23:58:30 +0000 |
commit | b529f0a782cc751a7ce35ebf9c2a929a8134aae6 (patch) | |
tree | 23d7efa9de7952b65c23e2706c0e632f68497198 | |
parent | cd04b9217f4581f9d1d6840bc6c3c4c916b330e8 (diff) | |
download | glance_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.py | 29 | ||||
-rw-r--r-- | glance_store/_drivers/swift/utils.py | 47 | ||||
-rw-r--r-- | glance_store/tests/etc/glance-swift.conf | 3 | ||||
-rw-r--r-- | glance_store/tests/unit/test_swift_store.py | 55 |
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): |