Add keystone backend
This patch adds keystone support for the client. It is possible to either provide os_auth + endpoint through the CLI or all other required params to retrieve both or one of them. Implements blueprint keystone-integration Change-Id: Id059d075aabb26f92c82b0a863d85743d8564f69
This commit is contained in:
@@ -13,14 +13,26 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
from oslo.config import cfg
|
||||
import six
|
||||
|
||||
from marconiclient.auth import base
|
||||
|
||||
if not six.PY3:
|
||||
from marconiclient.auth import keystone
|
||||
else:
|
||||
keystone = None
|
||||
# NOTE(flaper87): Replace with logging
|
||||
print('Keystone auth does not support Py3K')
|
||||
|
||||
_BACKENDS = {
|
||||
'noauth': base.NoAuth,
|
||||
}
|
||||
|
||||
if keystone:
|
||||
_BACKENDS['keystone'] = keystone.KeystoneAuth
|
||||
|
||||
|
||||
def _register_opts(conf):
|
||||
"""Registers auth cli options.
|
||||
|
||||
126
marconiclient/auth/keystone.py
Normal file
126
marconiclient/auth/keystone.py
Normal file
@@ -0,0 +1,126 @@
|
||||
# Copyright (c) 2013 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
from oslo.config import cfg
|
||||
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
|
||||
from marconiclient.auth import base
|
||||
from marconiclient.common import utils
|
||||
|
||||
|
||||
# NOTE(flaper87): Some of the code below
|
||||
# was brought to you by the very unique
|
||||
# work of ceilometerclient.
|
||||
class KeystoneAuth(base.AuthBackend):
|
||||
|
||||
_CLI_OPTIONS = [
|
||||
cfg.StrOpt("os_username", default=utils.env('OS_USERNAME'),
|
||||
help='Defaults to env[OS_USERNAME]'),
|
||||
|
||||
cfg.StrOpt("os_password", default=utils.env('OS_PASSWORD'),
|
||||
help='Defaults to env[OS_PASSWORD]'),
|
||||
|
||||
cfg.StrOpt("os_project_id", default=utils.env('OS_PROJECT_ID'),
|
||||
help='Defaults to env[OS_PROJECT_ID]'),
|
||||
|
||||
cfg.StrOpt("os_project_name", default=utils.env('OS_PROJECT_NAME'),
|
||||
help='Defaults to env[OS_PROJECT_NAME]'),
|
||||
|
||||
cfg.StrOpt("os_auth_url", default=utils.env('OS_AUTH_URL'),
|
||||
help='Defaults to env[OS_AUTH_URL]'),
|
||||
|
||||
cfg.StrOpt("os_auth_token", default=utils.env('OS_AUTH_TOKEN'),
|
||||
help='Defaults to env[OS_AUTH_TOKEN]'),
|
||||
|
||||
cfg.StrOpt("os_region_name", default=utils.env('OS_REGION_NAME'),
|
||||
help='Defaults to env[OS_REGION_NAME]'),
|
||||
|
||||
cfg.StrOpt("os_service_type", default=utils.env('OS_SERVICE_TYPE'),
|
||||
help='Defaults to env[OS_SERVICE_TYPE]'),
|
||||
|
||||
cfg.StrOpt("os_service_type", default=utils.env('OS_SERVICE_TYPE'),
|
||||
help='Defaults to env[OS_SERVICE_TYPE]'),
|
||||
|
||||
cfg.StrOpt("os_endpoint_type", default=utils.env('OS_ENDPOINT_TYPE'),
|
||||
help='Defaults to env[OS_ENDPOINT_TYPE]'),
|
||||
|
||||
]
|
||||
|
||||
def __init__(self, conf):
|
||||
super(KeystoneAuth, self).__init__(conf)
|
||||
conf.register_cli_opts(self._CLI_OPTIONS)
|
||||
|
||||
def _get_ksclient(self, **kwargs):
|
||||
"""Get an endpoint and auth token from Keystone.
|
||||
|
||||
:param kwargs: keyword args containing credentials:
|
||||
* username: name of user
|
||||
* password: user's password
|
||||
* auth_url: endpoint to authenticate against
|
||||
* insecure: allow insecure SSL (no cert verification)
|
||||
* project_{name|id}: name or ID of project
|
||||
"""
|
||||
return ksclient.Client(username=self.conf.os_username,
|
||||
password=self.conf.os_password,
|
||||
tenant_id=self.conf.os_project_id,
|
||||
tenant_name=self.conf.os_project_name,
|
||||
auth_url=self.conf.os_auth_url,
|
||||
insecure=self.conf.insecure)
|
||||
|
||||
def _get_endpoint(self, client):
|
||||
"""Get an endpoint using the provided keystone client."""
|
||||
return client.service_catalog.url_for(
|
||||
service_type=self.conf.service_type or 'queuing',
|
||||
endpoint_type=self.conf.endpoint_type or 'publicURL')
|
||||
|
||||
def authenticate(self, api_version, request):
|
||||
"""Get an authtenticated client, based on the credentials
|
||||
in the keyword args.
|
||||
|
||||
:param api_version: the API version to use ('1' or '2')
|
||||
:param request: The request spec instance to modify with
|
||||
the auth information.
|
||||
"""
|
||||
|
||||
token = self.conf.os_auth_token
|
||||
if not self.conf.os_auth_token or not request.endpoint:
|
||||
# NOTE(flaper87): Lets assume all the
|
||||
# required information was provided
|
||||
# either through env variables or CLI
|
||||
# params. Let keystoneclient fail otherwise.
|
||||
ks_kwargs = {
|
||||
'username': self.conf.os_username,
|
||||
'password': self.conf.os_password,
|
||||
'tenant_id': self.conf.os_project_id,
|
||||
'tenant_name': self.conf.os_project_name,
|
||||
'auth_url': self.conf.os_auth_url,
|
||||
'service_type': self.conf.os_service_type,
|
||||
'endpoint_type': self.conf.os_endpoint_type,
|
||||
'insecure': self.conf.insecure,
|
||||
}
|
||||
|
||||
_ksclient = self._get_ksclient(**ks_kwargs)
|
||||
|
||||
if not token:
|
||||
token = _ksclient.auth_token
|
||||
|
||||
if not request.endpoint:
|
||||
request.endpoint = self._get_endpoint(_ksclient, **ks_kwargs)
|
||||
|
||||
# NOTE(flaper87): Update the request spec
|
||||
# with the final token.
|
||||
request.headers['X-Auth-Token'] = token
|
||||
return request
|
||||
71
tests/auth/test_keystone.py
Normal file
71
tests/auth/test_keystone.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# Copyright (c) 2013 Red Hat, Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
try:
|
||||
from keystoneclient.v2_0 import client as ksclient
|
||||
except ImportError:
|
||||
ksclient = None
|
||||
|
||||
from marconiclient import auth
|
||||
from marconiclient.tests import base
|
||||
from marconiclient.transport import request
|
||||
|
||||
|
||||
class _FakeKeystoneClient(object):
|
||||
auth_token = 'test-token'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
class TestKeystoneAuth(base.TestBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestKeystoneAuth, self).setUp()
|
||||
|
||||
if not ksclient:
|
||||
self.skipTest('Keystone client is not installed')
|
||||
|
||||
auth._register_opts(self.conf)
|
||||
self.config(auth_backend='keystone')
|
||||
self.auth = auth.get_backend(self.conf)
|
||||
|
||||
# FIXME(flaper87): Remove once insecure is added
|
||||
# in the right place.
|
||||
self.conf.register_opt(cfg.BoolOpt('insecure', default=False))
|
||||
|
||||
def test_no_token(self):
|
||||
test_endpoint = 'http://example.org:8888'
|
||||
|
||||
with mock.patch.object(ksclient, 'Client',
|
||||
new_callable=lambda: _FakeKeystoneClient):
|
||||
|
||||
with mock.patch.object(self.auth, '_get_endpoint') as get_endpoint:
|
||||
get_endpoint.return_value = test_endpoint
|
||||
|
||||
req = self.auth.authenticate(1, request.Request())
|
||||
self.assertEqual(req.endpoint, test_endpoint)
|
||||
self.assertIn('X-Auth-Token', req.headers)
|
||||
|
||||
def test_with_token(self):
|
||||
self.config(os_auth_token='test-token')
|
||||
req = request.Request(endpoint='http://example.org:8888')
|
||||
req = self.auth.authenticate(1, req)
|
||||
self.assertIn('X-Auth-Token', req.headers)
|
||||
self.assertIn(req.headers['X-Auth-Token'], 'test-token')
|
||||
Reference in New Issue
Block a user