Merge "Remove unnecessary auth module"

This commit is contained in:
Jenkins 2016-01-20 19:27:27 +00:00 committed by Gerrit Code Review
commit 56290c3c0d
3 changed files with 44 additions and 329 deletions
glance_store
_drivers/swift
common
tests/unit

View File

@ -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'

View File

@ -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()

View File

@ -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):