Added keystone token authorization
Change-Id: I585dfd3e2ece26dab7f18d1117cad0dca0baf72b
This commit is contained in:
parent
bb6dd45ec4
commit
49643c4ae8
62
mistral/api/access_control.py
Normal file
62
mistral/api/access_control.py
Normal file
@ -0,0 +1,62 @@
|
||||
# Copyright 2013 - Mirantis, 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.
|
||||
|
||||
"""Access Control API server."""
|
||||
|
||||
from keystoneclient.middleware import auth_token
|
||||
from oslo.config import cfg
|
||||
|
||||
|
||||
_ENFORCER = None
|
||||
OPT_GROUP_NAME = 'keystone_authtoken'
|
||||
|
||||
|
||||
def register_opts(conf):
|
||||
"""Register keystoneclient middleware options
|
||||
"""
|
||||
conf.register_opts(auth_token.opts,
|
||||
group=OPT_GROUP_NAME)
|
||||
auth_token.CONF = conf
|
||||
|
||||
|
||||
register_opts(cfg.CONF)
|
||||
|
||||
|
||||
def install(app, conf):
|
||||
if conf.app.auth_enable:
|
||||
return auth_token.AuthProtocol(app,
|
||||
conf=dict(cfg.CONF.keystone_authtoken))
|
||||
else:
|
||||
return app
|
||||
|
||||
|
||||
def get_limited_to(headers):
|
||||
"""Return the user and project the request should be limited to.
|
||||
|
||||
:param headers: HTTP headers dictionary
|
||||
:return: A tuple of (user, project), set to None if there's no limit on
|
||||
one of these.
|
||||
|
||||
"""
|
||||
return headers.get('X-User-Id'), headers.get('X-Project-Id')
|
||||
|
||||
|
||||
def get_limited_to_project(headers):
|
||||
"""Return the project the request should be limited to.
|
||||
|
||||
:param headers: HTTP headers dictionary
|
||||
:return: A project, or None if there's no limit on it.
|
||||
|
||||
"""
|
||||
return get_limited_to(headers)[1]
|
@ -17,12 +17,12 @@ import pecan
|
||||
from mistral.api import config as api_config
|
||||
from mistral.db import api as db_api
|
||||
from mistral.services import periodic
|
||||
from mistral.api import access_control as ac
|
||||
|
||||
|
||||
def get_pecan_config():
|
||||
# Set up the pecan configuration
|
||||
filename = api_config.__file__.replace('.pyc', '.py')
|
||||
|
||||
return pecan.configuration.conf_from_file(filename)
|
||||
|
||||
|
||||
@ -36,8 +36,12 @@ def setup_app(config=None):
|
||||
##TODO(akuznetsov) move this to event scheduling to separate process
|
||||
periodic.setup()
|
||||
|
||||
return pecan.make_app(
|
||||
app = pecan.make_app(
|
||||
app_conf.pop('root'),
|
||||
logging=getattr(config, 'logging', {}),
|
||||
**app_conf
|
||||
)
|
||||
|
||||
app = ac.install(app, config)
|
||||
|
||||
return app
|
||||
|
@ -18,6 +18,7 @@ app = {
|
||||
'root': 'mistral.api.controllers.root.RootController',
|
||||
'modules': ['mistral.api'],
|
||||
'debug': True,
|
||||
'auth_enable': True
|
||||
}
|
||||
|
||||
# Custom Configurations must be in Python dictionary format::
|
||||
|
@ -34,7 +34,8 @@ class FunctionalTest(BaseTest):
|
||||
'app': {
|
||||
'root': 'mistral.api.controllers.root.RootController',
|
||||
'modules': ['mistral.api'],
|
||||
'debug': False
|
||||
'debug': False,
|
||||
'auth_enable': False
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
import six
|
||||
|
||||
from keystoneclient.v3 import client as keystone_client
|
||||
|
||||
from mistralclient.api import httpclient
|
||||
from mistralclient.api import workbooks
|
||||
from mistralclient.api import executions
|
||||
@ -24,19 +26,55 @@ from mistralclient.api import listeners
|
||||
|
||||
|
||||
class Client(object):
|
||||
def __init__(self, mistral_url=None):
|
||||
# TODO: add all required parameters for Keystone authentication
|
||||
|
||||
def __init__(self, username=None, api_key=None, project_id=None,
|
||||
project_name=None, auth_url=None, mistral_url=None,
|
||||
endpoint_type='publicURL', service_type='workflow',
|
||||
input_auth_token=None):
|
||||
if mistral_url and not isinstance(mistral_url, six.string_types):
|
||||
raise RuntimeError('Mistral url should be a string.')
|
||||
raise RuntimeError('Mistral url should be string')
|
||||
if (isinstance(project_name, six.string_types) or
|
||||
isinstance(project_id, six.string_types)):
|
||||
if project_name and project_id:
|
||||
raise RuntimeError('Only project name or '
|
||||
'project id should be set')
|
||||
|
||||
if "v2.0" in auth_url:
|
||||
raise RuntimeError('Mistral support only v3 '
|
||||
'kyestone api')
|
||||
print keystone_client
|
||||
keystone = keystone_client.Client(username=username,
|
||||
password=api_key,
|
||||
token=input_auth_token,
|
||||
tenant_id=project_id,
|
||||
tenant_name=project_name,
|
||||
auth_url=auth_url)
|
||||
|
||||
keystone.authenticate()
|
||||
token = keystone.auth_token
|
||||
user_id = keystone.user_id
|
||||
if project_name and not project_id:
|
||||
if keystone.tenants.find(name=project_name):
|
||||
project_id = str(keystone.tenants.find(
|
||||
name=project_name).id)
|
||||
else:
|
||||
raise RuntimeError('Project name or project id should'
|
||||
' not be empty and should be string')
|
||||
|
||||
if not mistral_url:
|
||||
mistral_url = "http://localhost:8989/v1"
|
||||
catalog = keystone.service_catalog.get_endpoints(service_type)
|
||||
if service_type in catalog:
|
||||
for e_type, endpoint in catalog.get[service_type][0].items():
|
||||
if str(e_type).lower() == str(endpoint_type).lower():
|
||||
mistral_url = endpoint
|
||||
break
|
||||
|
||||
# TODO: add Keystone authentication later
|
||||
token = "TBD"
|
||||
|
||||
self.http_client = httpclient.HTTPClient(mistral_url, token)
|
||||
if not mistral_url:
|
||||
mistral_url = "http://localhost:8386/v1.0"
|
||||
self.http_client = httpclient.HTTPClient(mistral_url,
|
||||
token,
|
||||
project_id,
|
||||
user_id)
|
||||
|
||||
# Create all resource managers.
|
||||
self.workbooks = workbooks.WorkbookManager(self)
|
||||
|
@ -18,38 +18,45 @@ import requests
|
||||
|
||||
|
||||
class HTTPClient(object):
|
||||
def __init__(self, base_url, token):
|
||||
def __init__(self, base_url, token, project_id, user_id):
|
||||
self.base_url = base_url
|
||||
self.token = token
|
||||
self.project_id = project_id
|
||||
self.user_id = user_id
|
||||
|
||||
def get(self, url, headers=None):
|
||||
if not headers:
|
||||
headers = {}
|
||||
|
||||
headers['x-auth-token'] = self.token
|
||||
headers = self._update_headers(headers)
|
||||
|
||||
return requests.get(self.base_url + url, headers=headers)
|
||||
|
||||
def post(self, url, body, headers=None):
|
||||
if not headers:
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
headers['x-auth-token'] = self.token
|
||||
headers = self._update_headers(headers)
|
||||
content_type = headers.get('content-type', 'application/json')
|
||||
headers['content-type'] = content_type
|
||||
|
||||
return requests.post(self.base_url + url, body, headers=headers)
|
||||
|
||||
def put(self, url, body, headers=None):
|
||||
if not headers:
|
||||
headers = {'content-type': 'application/json'}
|
||||
|
||||
headers['x-auth-token'] = self.token
|
||||
headers = self._update_headers(headers)
|
||||
content_type = headers.get('content-type', 'application/json')
|
||||
headers['content-type'] = content_type
|
||||
|
||||
return requests.put(self.base_url + url, body, headers=headers)
|
||||
|
||||
def delete(self, url, headers=None):
|
||||
if not headers:
|
||||
headers = {}
|
||||
|
||||
headers['x-auth-token'] = self.token
|
||||
headers = self._update_headers(headers)
|
||||
|
||||
return requests.delete(self.base_url + url, headers=headers)
|
||||
|
||||
def _update_headers(self, headers):
|
||||
if not headers:
|
||||
headers = {}
|
||||
token = headers.get('x-auth-token', self.token)
|
||||
headers['x-auth-token'] = token
|
||||
|
||||
project_id = headers.get('X-Project-Id', self.project_id)
|
||||
headers['X-Project-Id'] = project_id
|
||||
|
||||
user_id = headers.get('X-User-Id', self.user_id)
|
||||
headers['X-User-Id'] = user_id
|
||||
return headers
|
||||
|
@ -32,25 +32,29 @@ class FakeResponse(object):
|
||||
|
||||
|
||||
class BaseClientTest(unittest2.TestCase):
|
||||
def setUp(self):
|
||||
self._client = client.Client()
|
||||
@mock.patch('keystoneclient.v3.client.Client')
|
||||
def setUp(self, keystone):
|
||||
keystone.return_value = mock.Mock()
|
||||
self._client = client.Client(project_name="test",
|
||||
auth_url="v3.0",
|
||||
mistral_url="test")
|
||||
self.workbooks = self._client.workbooks
|
||||
self.executions = self._client.executions
|
||||
self.tasks = self._client.tasks
|
||||
self.listeners = self._client.listeners
|
||||
|
||||
def mock_http_get(self, json, status_code=200):
|
||||
self._client.http_client.get =\
|
||||
self._client.http_client.get = \
|
||||
mock.MagicMock(return_value=FakeResponse(status_code, json))
|
||||
|
||||
def mock_http_post(self, json, status_code=201):
|
||||
self._client.http_client.post =\
|
||||
self._client.http_client.post = \
|
||||
mock.MagicMock(return_value=FakeResponse(status_code, json))
|
||||
|
||||
def mock_http_put(self, json, status_code=200):
|
||||
self._client.http_client.put =\
|
||||
self._client.http_client.put = \
|
||||
mock.MagicMock(return_value=FakeResponse(status_code, json))
|
||||
|
||||
def mock_http_delete(self, status_code=204):
|
||||
self._client.http_client.delete =\
|
||||
self._client.http_client.delete = \
|
||||
mock.MagicMock(return_value=FakeResponse(status_code))
|
||||
|
@ -8,4 +8,5 @@ argparse
|
||||
croniter
|
||||
oslo.config>=1.2.0
|
||||
requests
|
||||
python-keystoneclient>=0.3.2
|
||||
pika>=0.9.13
|
Loading…
Reference in New Issue
Block a user