Merge "Load wsgi app(api) with paste.deploy"
This commit is contained in:
commit
9bd983c3a7
|
@ -44,6 +44,7 @@ MAGNUM_AUTH_CACHE_DIR=${MAGNUM_AUTH_CACHE_DIR:-/var/cache/magnum}
|
|||
MAGNUM_CONF_DIR=/etc/magnum
|
||||
MAGNUM_CONF=$MAGNUM_CONF_DIR/magnum.conf
|
||||
MAGNUM_POLICY_JSON=$MAGNUM_CONF_DIR/policy.json
|
||||
MAGNUM_API_PASTE=$MAGNUM_CONF_DIR/api-paste.ini
|
||||
|
||||
if is_ssl_enabled_service "magnum" || is_service_enabled tls-proxy; then
|
||||
MAGNUM_SERVICE_PROTOCOL="https"
|
||||
|
@ -95,6 +96,8 @@ function configure_magnum {
|
|||
# Rebuild the config file from scratch
|
||||
create_magnum_conf
|
||||
|
||||
create_api_paste_conf
|
||||
|
||||
update_heat_policy
|
||||
}
|
||||
|
||||
|
@ -206,6 +209,11 @@ function create_magnum_conf {
|
|||
iniset $MAGNUM_CONF cinder_client region_name $REGION_NAME
|
||||
}
|
||||
|
||||
function create_api_paste_conf {
|
||||
# copy api_paste.ini
|
||||
cp $MAGNUM_DIR/etc/magnum/api-paste.ini $MAGNUM_API_PASTE
|
||||
}
|
||||
|
||||
function update_heat_policy {
|
||||
# enable stacks globel_index search so that magnum can use
|
||||
# list(global_tenant=True)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
[pipeline:main]
|
||||
pipeline = cors request_id authtoken api_v1
|
||||
|
||||
[app:api_v1]
|
||||
paste.app_factory = magnum.api.app:app_factory
|
||||
|
||||
[filter:authtoken]
|
||||
acl_public_routes = /, /v1
|
||||
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = magnum
|
||||
latent_allow_methods = GET, PUT, POST, DELETE, PATCH
|
||||
latent_allow_headers = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID
|
||||
latent_expose_headers = X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID
|
|
@ -9,14 +9,16 @@
|
|||
# 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 os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_middleware import cors
|
||||
from oslo_log import log
|
||||
from paste import deploy
|
||||
import pecan
|
||||
|
||||
from magnum.api import auth
|
||||
from magnum.api import config as api_config
|
||||
from magnum.api import middleware
|
||||
from magnum.i18n import _
|
||||
|
||||
# Register options for the service
|
||||
API_SERVICE_OPTS = [
|
||||
|
@ -29,7 +31,11 @@ API_SERVICE_OPTS = [
|
|||
cfg.IntOpt('max_limit',
|
||||
default=1000,
|
||||
help='The maximum number of items returned in a single '
|
||||
'response from a collection resource.')
|
||||
'response from a collection resource.'),
|
||||
cfg.StrOpt('api_paste_config',
|
||||
default="api-paste.ini",
|
||||
help="Configuration file for WSGI definition of API."
|
||||
)
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -38,6 +44,8 @@ opt_group = cfg.OptGroup(name='api',
|
|||
CONF.register_group(opt_group)
|
||||
CONF.register_opts(API_SERVICE_OPTS, opt_group)
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
def get_pecan_config():
|
||||
# Set up the pecan configuration
|
||||
|
@ -58,17 +66,22 @@ def setup_app(config=None):
|
|||
**app_conf
|
||||
)
|
||||
|
||||
app = auth.install(app, CONF, config.app.acl_public_routes)
|
||||
|
||||
# CORS must be the last one.
|
||||
app = cors.CORS(app, CONF)
|
||||
app.set_latent(
|
||||
allow_headers=['X-Auth-Token', 'X-Identity-Status', 'X-Roles',
|
||||
'X-Service-Catalog', 'X-User-Id', 'X-Tenant-Id',
|
||||
'X-OpenStack-Request-ID', 'X-Server-Management-Url'],
|
||||
allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'],
|
||||
expose_headers=['X-Auth-Token', 'X-Subject-Token', 'X-Service-Token',
|
||||
'X-OpenStack-Request-ID', 'X-Server-Management-Url']
|
||||
)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
def load_app():
|
||||
cfg_file = None
|
||||
cfg_path = cfg.CONF.api.api_paste_config
|
||||
if not os.path.isabs(cfg_path):
|
||||
cfg_file = CONF.find_file(cfg_path)
|
||||
elif os.path.exists(cfg_path):
|
||||
cfg_file = cfg_path
|
||||
|
||||
if not cfg_file:
|
||||
raise cfg.ConfigFilesNotFoundError([cfg.CONF.api_paste_config])
|
||||
LOG.info(_("Full WSGI config used: %s") % cfg_file)
|
||||
return deploy.loadapp("config:" + cfg_file)
|
||||
|
||||
|
||||
def app_factory(global_config, **local_conf):
|
||||
return setup_app()
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
# Copyright 2012 New Dream Network, LLC (DreamHost)
|
||||
#
|
||||
# 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 Lists (ACL's) control access the API server."""
|
||||
from oslo_config import cfg
|
||||
|
||||
from magnum.api.middleware import auth_token
|
||||
|
||||
|
||||
AUTH_OPTS = [
|
||||
cfg.BoolOpt('enable_authentication',
|
||||
default=True,
|
||||
help='This option enables or disables user authentication '
|
||||
'via Keystone. Default value is True.'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(AUTH_OPTS)
|
||||
|
||||
|
||||
def install(app, conf, public_routes):
|
||||
"""Install ACL check on application.
|
||||
|
||||
:param app: A WSGI application.
|
||||
:param conf: Settings. Dict'ified and passed to keystone middleware
|
||||
:param public_routes: The list of the routes which will be allowed to
|
||||
access without authentication.
|
||||
:return: The same WSGI application with ACL installed.
|
||||
|
||||
"""
|
||||
if not cfg.CONF.get('enable_authentication'):
|
||||
return app
|
||||
return auth_token.AuthTokenMiddleware(app,
|
||||
conf=dict(conf),
|
||||
public_api_routes=public_routes)
|
|
@ -58,3 +58,12 @@ class AuthTokenMiddleware(auth_token.AuthProtocol):
|
|||
return self._app(env, start_response)
|
||||
|
||||
return super(AuthTokenMiddleware, self).__call__(env, start_response)
|
||||
|
||||
@classmethod
|
||||
def factory(cls, global_config, **local_conf):
|
||||
public_routes = local_conf.get('acl_public_routes', '')
|
||||
public_api_routes = [path.strip() for path in public_routes.split(',')]
|
||||
|
||||
def _factory(app):
|
||||
return cls(app, global_config, public_api_routes=public_api_routes)
|
||||
return _factory
|
||||
|
|
|
@ -40,7 +40,7 @@ def main():
|
|||
# Enable object backporting via the conductor
|
||||
base.MagnumObject.indirection_api = base.MagnumObjectIndirectionAPI()
|
||||
|
||||
app = api_app.setup_app()
|
||||
app = api_app.load_app()
|
||||
|
||||
# Create the WSGI server and start it
|
||||
host, port = cfg.CONF.api.host, cfg.CONF.api.port
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
import itertools
|
||||
|
||||
import magnum.api.app
|
||||
import magnum.api.auth
|
||||
import magnum.api.validation
|
||||
import magnum.common.cert_manager
|
||||
from magnum.common.cert_manager import local_cert_manager
|
||||
|
@ -35,8 +34,7 @@ import magnum.db
|
|||
def list_opts():
|
||||
return [
|
||||
('DEFAULT',
|
||||
itertools.chain(magnum.api.auth.AUTH_OPTS,
|
||||
magnum.common.paths.PATH_OPTS,
|
||||
itertools.chain(magnum.common.paths.PATH_OPTS,
|
||||
magnum.common.utils.UTILS_OPTS,
|
||||
magnum.common.rpc_service.periodic_opts,
|
||||
magnum.common.service.service_opts,
|
||||
|
|
|
@ -54,8 +54,6 @@ class FunctionalTest(base.DbTestCase):
|
|||
'modules': ['magnum.api'],
|
||||
'static_root': '%s/public' % root_dir,
|
||||
'template_path': '%s/api/templates' % root_dir,
|
||||
'enable_acl': False,
|
||||
'acl_public_routes': ['/', '/v1'],
|
||||
'hooks': [
|
||||
hooks.ContextHook(),
|
||||
hooks.RPCHook(),
|
||||
|
@ -83,12 +81,10 @@ class FunctionalTest(base.DbTestCase):
|
|||
for attr in attrs:
|
||||
verify_method(attr, response)
|
||||
|
||||
def _make_app(self, config=None, enable_acl=False):
|
||||
def _make_app(self, config=None):
|
||||
if not config:
|
||||
config = self.config
|
||||
|
||||
config["app"]["enable_acl"] = enable_acl
|
||||
|
||||
return pecan.testing.load_test_app(config)
|
||||
|
||||
def _request_json(self, path, params, expect_errors=False, headers=None,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
[pipeline:main]
|
||||
pipeline = cors request_id authtoken api_v1
|
||||
|
||||
[app:api_v1]
|
||||
paste.app_factory = magnum.api.app:app_factory
|
||||
|
||||
[filter:authtoken]
|
||||
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = magnum
|
||||
latent_allow_methods = GET, PUT, POST, DELETE, PATCH
|
||||
latent_allow_headers = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID
|
||||
latent_expose_headers = X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID
|
|
@ -0,0 +1,19 @@
|
|||
[pipeline:main]
|
||||
pipeline = cors request_id authtoken api_v1
|
||||
|
||||
[app:api_v1]
|
||||
paste.app_factory = magnum.api.app:app_factory
|
||||
|
||||
[filter:authtoken]
|
||||
acl_public_routes = /
|
||||
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = magnum
|
||||
latent_allow_methods = GET, PUT, POST, DELETE, PATCH
|
||||
latent_allow_headers = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID
|
||||
latent_expose_headers = X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID
|
|
@ -0,0 +1,19 @@
|
|||
[pipeline:main]
|
||||
pipeline = cors request_id authtoken api_v1
|
||||
|
||||
[app:api_v1]
|
||||
paste.app_factory = magnum.api.app:app_factory
|
||||
|
||||
[filter:authtoken]
|
||||
acl_public_routes = /v1
|
||||
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = magnum
|
||||
latent_allow_methods = GET, PUT, POST, DELETE, PATCH
|
||||
latent_allow_headers = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID
|
||||
latent_expose_headers = X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID
|
|
@ -0,0 +1,19 @@
|
|||
[pipeline:main]
|
||||
pipeline = cors request_id api_v1
|
||||
|
||||
[app:api_v1]
|
||||
paste.app_factory = magnum.api.app:app_factory
|
||||
|
||||
[filter:authtoken]
|
||||
acl_public_routes = /
|
||||
paste.filter_factory = magnum.api.middleware.auth_token:AuthTokenMiddleware.factory
|
||||
|
||||
[filter:request_id]
|
||||
paste.filter_factory = oslo_middleware:RequestId.factory
|
||||
|
||||
[filter:cors]
|
||||
paste.filter_factory = oslo_middleware.cors:filter_factory
|
||||
oslo_config_project = magnum
|
||||
latent_allow_methods = GET, PUT, POST, DELETE, PATCH
|
||||
latent_allow_headers = X-Auth-Token, X-Identity-Status, X-Roles, X-Service-Catalog, X-User-Id, X-Tenant-Id, X-OpenStack-Request-ID
|
||||
latent_expose_headers = X-Auth-Token, X-Subject-Token, X-Service-Token, X-OpenStack-Request-ID
|
|
@ -10,12 +10,13 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from webob import exc as webob_exc
|
||||
|
||||
import webtest
|
||||
|
||||
from magnum.api import app
|
||||
from magnum.api.controllers import v1 as v1_api
|
||||
from magnum.tests import base as test_base
|
||||
from magnum.tests.unit.api import base as api_base
|
||||
|
@ -84,6 +85,11 @@ class TestRootController(api_base.FunctionalTest):
|
|||
{u'href': u'http://localhost/mservices/',
|
||||
u'rel': u'bookmark'}]}
|
||||
|
||||
def make_app(self, paste_file):
|
||||
file_name = self.get_path(paste_file)
|
||||
cfg.CONF.set_override("api_paste_config", file_name, group="api")
|
||||
return webtest.TestApp(app.load_app())
|
||||
|
||||
def test_version(self):
|
||||
response = self.app.get('/')
|
||||
self.assertEqual(self.root_expected, response.json)
|
||||
|
@ -96,13 +102,10 @@ class TestRootController(api_base.FunctionalTest):
|
|||
response = self.app.get('/a/bogus/url', expect_errors=True)
|
||||
assert response.status_int == 404
|
||||
|
||||
def test_acl_access_with_all(self):
|
||||
cfg.CONF.set_override("enable_authentication", True)
|
||||
|
||||
config = copy.deepcopy(self.config)
|
||||
# Both / and /v1 and access without auth
|
||||
config["app"]["acl_public_routes"] = ['/', '/v1']
|
||||
app = self._make_app(config=config)
|
||||
def test_noauth(self):
|
||||
# Don't need to auth
|
||||
paste_file = "magnum/tests/unit/api/controllers/noauth-paste.ini"
|
||||
app = self.make_app(paste_file)
|
||||
|
||||
response = app.get('/')
|
||||
self.assertEqual(self.root_expected, response.json)
|
||||
|
@ -110,13 +113,24 @@ class TestRootController(api_base.FunctionalTest):
|
|||
response = app.get('/v1/')
|
||||
self.assertEqual(self.v1_expected, response.json)
|
||||
|
||||
def test_acl_access_with_root(self):
|
||||
cfg.CONF.set_override("enable_authentication", True)
|
||||
response = app.get('/v1/baymodels')
|
||||
self.assertEqual(200, response.status_int)
|
||||
|
||||
config = copy.deepcopy(self.config)
|
||||
def test_auth_with_no_public_routes(self):
|
||||
# All apis need auth when access
|
||||
paste_file = "magnum/tests/unit/api/controllers/auth-paste.ini"
|
||||
app = self.make_app(paste_file)
|
||||
|
||||
response = app.get('/', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
response = app.get('/v1/', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
def test_auth_with_root_access(self):
|
||||
# Only / can access without auth
|
||||
config["app"]["acl_public_routes"] = ['/']
|
||||
app = self._make_app(config=config)
|
||||
paste_file = "magnum/tests/unit/api/controllers/auth-root-access.ini"
|
||||
app = self.make_app(paste_file)
|
||||
|
||||
response = app.get('/')
|
||||
self.assertEqual(self.root_expected, response.json)
|
||||
|
@ -124,13 +138,13 @@ class TestRootController(api_base.FunctionalTest):
|
|||
response = app.get('/v1/', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
def test_acl_access_with_v1(self):
|
||||
cfg.CONF.set_override("enable_authentication", True)
|
||||
response = app.get('/v1/baymodels', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
config = copy.deepcopy(self.config)
|
||||
def test_auth_with_v1_access(self):
|
||||
# Only /v1 can access without auth
|
||||
config["app"]["acl_public_routes"] = ['/v1']
|
||||
app = self._make_app(config=config)
|
||||
paste_file = "magnum/tests/unit/api/controllers/auth-v1-access.ini"
|
||||
app = self.make_app(paste_file)
|
||||
|
||||
response = app.get('/', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
@ -138,17 +152,7 @@ class TestRootController(api_base.FunctionalTest):
|
|||
response = app.get('/v1/')
|
||||
self.assertEqual(self.v1_expected, response.json)
|
||||
|
||||
def test_acl_with_neither(self):
|
||||
cfg.CONF.set_override("enable_authentication", True)
|
||||
|
||||
config = copy.deepcopy(self.config)
|
||||
config["app"]["acl_public_routes"] = []
|
||||
app = self._make_app(config=config)
|
||||
|
||||
response = app.get('/', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
response = app.get('/v1/', expect_errors=True)
|
||||
response = app.get('/v1/baymodels', expect_errors=True)
|
||||
self.assertEqual(401, response.status_int)
|
||||
|
||||
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
# Copyright 2014
|
||||
# The Cloudscaling Group, 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 fixture
|
||||
|
||||
from magnum.api import auth
|
||||
from magnum.tests import base
|
||||
from magnum.tests import fakes
|
||||
|
||||
|
||||
@mock.patch('magnum.api.middleware.auth_token.AuthTokenMiddleware',
|
||||
new_callable=fakes.FakeAuthProtocol)
|
||||
class TestAuth(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAuth, self).setUp()
|
||||
self.CONF = self.useFixture(fixture.Config())
|
||||
self.app = fakes.FakeApp()
|
||||
|
||||
def test_check_auth_option_enabled(self, mock_auth):
|
||||
self.CONF.config(enable_authentication=True)
|
||||
result = auth.install(self.app, self.CONF.conf, ['/'])
|
||||
self.assertIsInstance(result, fakes.FakeAuthProtocol)
|
||||
|
||||
def test_check_auth_option_disabled(self, mock_auth):
|
||||
self.CONF.config(enable_authentication=False)
|
||||
result = auth.install(self.app, self.CONF.conf, ['/'])
|
||||
self.assertIsInstance(result, fakes.FakeApp)
|
|
@ -31,8 +31,6 @@ from magnum.tests import base
|
|||
|
||||
CONF = cfg.CONF
|
||||
|
||||
CONF.import_opt('enable_authentication', 'magnum.api.auth')
|
||||
|
||||
_DB_CACHE = None
|
||||
|
||||
|
||||
|
@ -88,7 +86,6 @@ class Database(fixtures.Fixture):
|
|||
class DbTestCase(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
cfg.CONF.set_override("enable_authentication", False)
|
||||
super(DbTestCase, self).setUp()
|
||||
|
||||
self.dbapi = dbapi.get_instance()
|
||||
|
|
Loading…
Reference in New Issue