diff options
author | Mike Fedosin <mfedosin@mirantis.com> | 2015-11-27 18:25:01 +0300 |
---|---|---|
committer | kairat_kushaev <kkushaev@mirantis.com> | 2016-01-19 13:38:20 +0300 |
commit | 3331ac6b4c86a73a060cf9a613537d364ac4bbb3 (patch) | |
tree | 7067c2a93c3cdf43358870c8c812eb7a4be4b541 | |
parent | e983e894d4fb6d03e1d54529da532db436c012a1 (diff) | |
download | glance_store-3331ac6b4c86a73a060cf9a613537d364ac4bbb3.tar.gz |
Remove unnecessary auth module
This commit removes common/auth.py file and changes
the way how endpoint for MultiTenant store is found.
Without having custom implementation it's better to
use an unified method 'url_for' that service_catalog
provides.
Co-Authored-By: kairat_kushaev <kkushaev@mirantis.com>
Change-Id: If8d07e378140a940317db1259a58477d7809948e
-rw-r--r-- | glance_store/_drivers/swift/store.py | 8 | ||||
-rw-r--r-- | glance_store/common/auth.py | 293 | ||||
-rw-r--r-- | glance_store/tests/unit/test_swift_store.py | 72 |
3 files changed, 44 insertions, 329 deletions
diff --git a/glance_store/_drivers/swift/store.py b/glance_store/_drivers/swift/store.py index 591b16d..603a435 100644 --- a/glance_store/_drivers/swift/store.py +++ b/glance_store/_drivers/swift/store.py @@ -33,7 +33,6 @@ except ImportError: import glance_store from glance_store._drivers.swift import utils as sutils from glance_store import capabilities -from glance_store.common import auth from glance_store.common import utils as cutils from glance_store import driver from glance_store import exceptions @@ -870,9 +869,10 @@ class MultiTenantStore(BaseStore): reason=reason) self.storage_url = self.conf_endpoint if not self.storage_url: - self.storage_url = auth.get_endpoint( - context.service_catalog, service_type=self.service_type, - endpoint_region=self.region, endpoint_type=self.endpoint_type) + + self.storage_url = context.service_catalog.url_for( + service_type=self.service_type, region_name=self.region, + endpoint_type=self.endpoint_type) if self.storage_url.startswith('http://'): self.scheme = 'swift+http' diff --git a/glance_store/common/auth.py b/glance_store/common/auth.py deleted file mode 100644 index b5fa06e..0000000 --- a/glance_store/common/auth.py +++ /dev/null @@ -1,293 +0,0 @@ -# Copyright 2011 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -""" -This auth module is intended to allow OpenStack client-tools to select from a -variety of authentication strategies, including NoAuth (the default), and -Keystone (an identity management system). - - > auth_plugin = AuthPlugin(creds) - - > auth_plugin.authenticate() - - > auth_plugin.auth_token - abcdefg - - > auth_plugin.management_url - http://service_endpoint/ -""" -import httplib2 -import logging - -from oslo_serialization import jsonutils -# NOTE(jokke): simplified transition to py3, behaves like py2 xrange -from six.moves import range -from six.moves import urllib - -from glance_store import exceptions -from glance_store.i18n import _ - - -LOG = logging.getLogger(__name__) - - -class BaseStrategy(object): - def __init__(self): - self.auth_token = None - # TODO(sirp): Should expose selecting public/internal/admin URL. - self.management_url = None - - def authenticate(self): - raise NotImplementedError - - @property - def is_authenticated(self): - raise NotImplementedError - - @property - def strategy(self): - raise NotImplementedError - - -class NoAuthStrategy(BaseStrategy): - def authenticate(self): - pass - - @property - def is_authenticated(self): - return True - - @property - def strategy(self): - return 'noauth' - - -class KeystoneStrategy(BaseStrategy): - MAX_REDIRECTS = 10 - - def __init__(self, creds, insecure=False, configure_via_auth=True): - self.creds = creds - self.insecure = insecure - self.configure_via_auth = configure_via_auth - super(KeystoneStrategy, self).__init__() - - def check_auth_params(self): - # Ensure that supplied credential parameters are as required - for required in ('username', 'password', 'auth_url', - 'strategy'): - if self.creds.get(required) is None: - raise exceptions.MissingCredentialError(required=required) - if self.creds['strategy'] != 'keystone': - raise exceptions.BadAuthStrategy(expected='keystone', - received=self.creds['strategy']) - # For v2.0 also check tenant is present - if self.creds['auth_url'].rstrip('/').endswith('v2.0'): - if self.creds.get("tenant") is None: - raise exceptions.MissingCredentialError(required='tenant') - - def authenticate(self): - """Authenticate with the Keystone service. - - There are a few scenarios to consider here: - - 1. Which version of Keystone are we using? v1 which uses headers to - pass the credentials, or v2 which uses a JSON encoded request body? - - 2. Keystone may respond back with a redirection using a 305 status - code. - - 3. We may attempt a v1 auth when v2 is what's called for. In this - case, we rewrite the url to contain /v2.0/ and retry using the v2 - protocol. - """ - def _authenticate(auth_url): - # If OS_AUTH_URL is missing a trailing slash add one - if not auth_url.endswith('/'): - auth_url += '/' - token_url = urllib.parse.urljoin(auth_url, "tokens") - # 1. Check Keystone version - is_v2 = auth_url.rstrip('/').endswith('v2.0') - if is_v2: - self._v2_auth(token_url) - else: - self._v1_auth(token_url) - - self.check_auth_params() - auth_url = self.creds['auth_url'] - for __ in range(self.MAX_REDIRECTS): - try: - _authenticate(auth_url) - except exceptions.AuthorizationRedirect as e: - # 2. Keystone may redirect us - auth_url = e.url - except exceptions.AuthorizationFailure: - # 3. In some configurations nova makes redirection to - # v2.0 keystone endpoint. Also, new location does not - # contain real endpoint, only hostname and port. - if 'v2.0' not in auth_url: - auth_url = urllib.parse.urljoin(auth_url, 'v2.0/') - else: - # If we successfully auth'd, then memorize the correct auth_url - # for future use. - self.creds['auth_url'] = auth_url - break - else: - # Guard against a redirection loop - raise exceptions.MaxRedirectsExceeded(redirects=self.MAX_REDIRECTS) - - def _v1_auth(self, token_url): - creds = self.creds - - headers = {} - headers['X-Auth-User'] = creds['username'] - headers['X-Auth-Key'] = creds['password'] - - tenant = creds.get('tenant') - if tenant: - headers['X-Auth-Tenant'] = tenant - - resp, resp_body = self._do_request(token_url, 'GET', headers=headers) - - def _management_url(self, resp): - for url_header in ('x-image-management-url', - 'x-server-management-url', - 'x-glance'): - try: - return resp[url_header] - except KeyError as e: - not_found = e - raise not_found - - if resp.status in (200, 204): - try: - if self.configure_via_auth: - self.management_url = _management_url(self, resp) - self.auth_token = resp['x-auth-token'] - except KeyError: - raise exceptions.AuthorizationFailure() - elif resp.status == 305: - raise exceptions.AuthorizationRedirect(uri=resp['location']) - elif resp.status == 400: - raise exceptions.AuthBadRequest(url=token_url) - elif resp.status == 401: - raise exceptions.NotAuthenticated() - elif resp.status == 404: - raise exceptions.AuthUrlNotFound(url=token_url) - else: - raise Exception(_('Unexpected response: %s') % resp.status) - - def _v2_auth(self, token_url): - - creds = self.creds - - creds = { - "auth": { - "tenantName": creds['tenant'], - "passwordCredentials": { - "username": creds['username'], - "password": creds['password'] - } - } - } - - headers = {} - headers['Content-Type'] = 'application/json' - req_body = jsonutils.dumps(creds) - - resp, resp_body = self._do_request( - token_url, 'POST', headers=headers, body=req_body) - - if resp.status == 200: - resp_auth = jsonutils.loads(resp_body)['access'] - creds_region = self.creds.get('region') - if self.configure_via_auth: - endpoint = get_endpoint(resp_auth['serviceCatalog'], - endpoint_region=creds_region) - self.management_url = endpoint - self.auth_token = resp_auth['token']['id'] - elif resp.status == 305: - raise exceptions.RedirectException(resp['location']) - elif resp.status == 400: - raise exceptions.AuthBadRequest(url=token_url) - elif resp.status == 401: - raise exceptions.NotAuthenticated() - elif resp.status == 404: - raise exceptions.AuthUrlNotFound(url=token_url) - else: - raise Exception(_('Unexpected response: %s') % resp.status) - - @property - def is_authenticated(self): - return self.auth_token is not None - - @property - def strategy(self): - return 'keystone' - - def _do_request(self, url, method, headers=None, body=None): - headers = headers or {} - conn = httplib2.Http() - conn.force_exception_to_status_code = True - conn.disable_ssl_certificate_validation = self.insecure - headers['User-Agent'] = 'glance-client' - resp, resp_body = conn.request(url, method, headers=headers, body=body) - return resp, resp_body - - -def get_plugin_from_strategy(strategy, creds=None, insecure=False, - configure_via_auth=True): - if strategy == 'noauth': - return NoAuthStrategy() - elif strategy == 'keystone': - return KeystoneStrategy(creds, insecure, - configure_via_auth=configure_via_auth) - else: - raise Exception(_("Unknown auth strategy '%s'") % strategy) - - -def get_endpoint(service_catalog, service_type='image', endpoint_region=None, - endpoint_type='publicURL'): - """ - Select an endpoint from the service catalog - - We search the full service catalog for services - matching both type and region. If the client - supplied no region then any 'image' endpoint - is considered a match. There must be one -- and - only one -- successful match in the catalog, - otherwise we will raise an exception. - """ - endpoint = None - for service in service_catalog: - s_type = None - try: - s_type = service['type'] - except KeyError: - msg = _('Encountered service with no "type": %s') % s_type - LOG.warn(msg) - continue - - if s_type == service_type: - for ep in service['endpoints']: - if endpoint_region is None or endpoint_region == ep['region']: - if endpoint is not None: - # This is a second match, abort - exc = exceptions.RegionAmbiguity - raise exc(region=endpoint_region) - endpoint = ep - if endpoint and endpoint.get(endpoint_type): - return endpoint[endpoint_type] - else: - raise exceptions.NoServiceEndpoint() diff --git a/glance_store/tests/unit/test_swift_store.py b/glance_store/tests/unit/test_swift_store.py index b749cce..f1f8769 100644 --- a/glance_store/tests/unit/test_swift_store.py +++ b/glance_store/tests/unit/test_swift_store.py @@ -37,7 +37,6 @@ from glance_store._drivers.swift import store as swift from glance_store import backend from glance_store import BackendException from glance_store import capabilities -from glance_store.common import auth from glance_store.common import utils from glance_store import exceptions from glance_store import location @@ -624,11 +623,11 @@ class SwiftTests(object): self.config(swift_store_container='container') self.config(swift_store_create_container_on_put=True) self.config(swift_store_multiple_containers_seed=2) - fake_get_endpoint = FakeGetEndpoint('https://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'https://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() location, size, checksum, _ = store.add(expected_image_id, image_swift, @@ -1298,15 +1297,8 @@ class TestMultiTenantStoreContext(base.StoreBaseTest): self.config(**conf) self.store.configure() self.register_store_schemes(self.store, 'swift') - self.service_catalog = [{ - "name": "Object Storage", - "type": "object-store", - "endpoints": [{ - "publicURL": "http://127.0.0.1:0", - "region": "region1", - "versionId": "1.0", - }] - }] + self.service_catalog = mock.MagicMock() + self.service_catalog.url_for.return_value = "http://127.0.0.1:0" self.addCleanup(self.conf.reset) @requests_mock.mock() @@ -1411,11 +1403,11 @@ class TestCreatingLocations(base.StoreBaseTest): def test_multi_tenant_location(self): self.config(swift_store_container='container') - fake_get_endpoint = FakeGetEndpoint('https://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'https://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() location = store.create_location('image-id', context=ctxt) @@ -1425,55 +1417,71 @@ class TestCreatingLocations(base.StoreBaseTest): self.assertEqual(location.obj, 'image-id') self.assertIsNone(location.user) self.assertIsNone(location.key) - self.assertEqual(fake_get_endpoint.service_type, 'object-store') + service_catalog.url_for.assert_called_once_with( + service_type=store.service_type, + region_name=store.region, + endpoint_type=store.endpoint_type) def test_multi_tenant_location_http(self): - fake_get_endpoint = FakeGetEndpoint('http://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'http://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() location = store.create_location('image-id', context=ctxt) self.assertEqual(location.scheme, 'swift+http') self.assertEqual(location.swift_url, 'http://some_endpoint') + service_catalog.url_for.assert_called_once_with( + service_type=store.service_type, + region_name=store.region, + endpoint_type=store.endpoint_type) def test_multi_tenant_location_with_region(self): self.config(swift_store_region='WestCarolina') - fake_get_endpoint = FakeGetEndpoint('https://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'https://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() store._get_endpoint(ctxt) - self.assertEqual(fake_get_endpoint.endpoint_region, 'WestCarolina') + service_catalog.url_for.assert_called_once_with( + service_type=store.service_type, + region_name=store.region, + endpoint_type=store.endpoint_type) def test_multi_tenant_location_custom_service_type(self): self.config(swift_store_service_type='toy-store') - fake_get_endpoint = FakeGetEndpoint('https://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'https://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() store._get_endpoint(ctxt) - self.assertEqual(fake_get_endpoint.service_type, 'toy-store') + service_catalog.url_for.assert_called_once_with( + service_type=store.service_type, + region_name=store.region, + endpoint_type=store.endpoint_type) def test_multi_tenant_location_custom_endpoint_type(self): self.config(swift_store_endpoint_type='InternalURL') - fake_get_endpoint = FakeGetEndpoint('https://some_endpoint') - self.stubs.Set(auth, 'get_endpoint', fake_get_endpoint) + service_catalog = mock.MagicMock() + service_catalog.url_for.return_value = 'https://some_endpoint' ctxt = mock.MagicMock( user='user', tenant='tenant', auth_token='123', - service_catalog={}) + service_catalog=service_catalog) store = swift.MultiTenantStore(self.conf) store.configure() store._get_endpoint(ctxt) - self.assertEqual(fake_get_endpoint.endpoint_type, 'InternalURL') + service_catalog.url_for.assert_called_once_with( + service_type=store.service_type, + region_name=store.region, + endpoint_type=store.endpoint_type) class TestChunkReader(base.StoreBaseTest): |