Added CORS support to Cue

This adds the CORS support middleware to Cue, allowing a deployer
to optionally configure rules under which a javascript client may
break the single-origin policy and access the API directly.

For cue, the middleware was added directly to the application
factory, including various default headers usually required by
keystone. Note that the CORS middleware must be the first one in
the middleware chain, so that it can annotate all responses -
even error responses - generated by other middleware.

oslo_config initialization was added to the functional test
initialization, in order to ensure that a fully initialized
config instance is available during tests.

OpenStack CrossProject Spec:
   http://specs.openstack.org/openstack/openstack-specs/specs/cors-support.html
Oslo_Middleware Docs:
   http://docs.openstack.org/developer/oslo.middleware/cors.html
OpenStack Cloud Admin Guide:
   http://docs.openstack.org/admin-guide-cloud/cross_project_cors.html

Change-Id: I7ff60a96f545ff991de06073a80a5750512ebd31
This commit is contained in:
Michael Krotscheck 2015-10-19 11:33:30 -07:00
parent e283ffc208
commit ebb06fb49f
6 changed files with 78 additions and 7 deletions

View File

@ -16,6 +16,7 @@
# under the License.
from oslo_config import cfg
from oslo_middleware import cors as cors_middleware
import pecan
from cue.api import acl
@ -35,9 +36,8 @@ API_OPTS = [
help='Pecan HTML Debug Interface'),
]
CONF = cfg.CONF
CONF.register_opts(auth_opts)
CONF.register_opts(API_OPTS, group='api')
cfg.CONF.register_opts(auth_opts)
cfg.CONF.register_opts(API_OPTS, group='api')
def list_opts():
@ -68,14 +68,23 @@ def setup_app(pecan_config=None, extra_hooks=None):
app = pecan.make_app(
pecan_config.app.root,
static_root=pecan_config.app.static_root,
debug=CONF.api.pecan_debug,
debug=cfg.CONF.api.pecan_debug,
force_canonical=getattr(pecan_config.app, 'force_canonical', True),
hooks=app_hooks,
wrap_app=middleware.ParsableErrorMiddleware,
)
if pecan_config.app.enable_acl:
return acl.install(app, cfg.CONF, pecan_config.app.acl_public_routes)
app = acl.install(app, cfg.CONF, pecan_config.app.acl_public_routes)
# Create a CORS wrapper, and attach ironic-specific defaults that must be
# included in all CORS responses.
app = cors_middleware.CORS(app, cfg.CONF)
app.set_latent(
allow_headers=['X-Auth-Token', 'X-Server-Management-Url'],
allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'],
expose_headers=['X-Auth-Token', 'X-Server-Management-Url']
)
return app
@ -83,7 +92,7 @@ def setup_app(pecan_config=None, extra_hooks=None):
class VersionSelectorApplication(object):
def __init__(self):
pc = get_pecan_config()
pc.app.enable_acl = (CONF.auth_strategy == 'keystone')
pc.app.enable_acl = (cfg.CONF.auth_strategy == 'keystone')
self.v1 = setup_app(pecan_config=pc)
def __call__(self, environ, start_response):

View File

@ -64,6 +64,9 @@ class APITest(base.FunctionalTestCase):
},
}
# Bootstrap the configuration, to ensure parsing has been handled.
cfg.CONF(project='cue', args=[])
return pecan.testing.load_test_app(self.config)
def tearDown(self):

View File

@ -18,6 +18,7 @@ from oslo_config import cfg
from cue.common import policy
from cue.tests.functional import api
from cue.tests.functional import base
class TestPolicyHarness(api.APITest):
@ -33,7 +34,7 @@ class TestPolicyHarness(api.APITest):
self.assertEqual(1, self.mock_init.call_count)
class TestPolicyOsloConfig(api.APITest):
class TestPolicyOsloConfig(base.FunctionalTestCase):
"""Test the policy harness initialization configuration states."""
def test_init_with_uninitialized_config_dir(self):

View File

@ -201,6 +201,62 @@
#pecan_debug = false
[cors]
#
# From oslo.middleware.cors
#
# Indicate whether this resource may be shared with the domain received in the
# requests "origin" header. (string value)
#allowed_origin = <None>
# Indicate that the actual request can include user credentials (boolean value)
#allow_credentials = true
# Indicate which headers are safe to expose to the API. Defaults to HTTP Simple
# Headers. (list value)
#expose_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma
# Maximum cache age of CORS preflight requests. (integer value)
#max_age = 3600
# Indicate which methods can be used during the actual request. (list value)
#allow_methods = GET,POST,PUT,DELETE,OPTIONS
# Indicate which header field names may be used during the actual request.
# (list value)
#allow_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma
[cors.subdomain]
#
# From oslo.middleware.cors
#
# Indicate whether this resource may be shared with the domain received in the
# requests "origin" header. (string value)
#allowed_origin = <None>
# Indicate that the actual request can include user credentials (boolean value)
#allow_credentials = true
# Indicate which headers are safe to expose to the API. Defaults to HTTP Simple
# Headers. (list value)
#expose_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma
# Maximum cache age of CORS preflight requests. (integer value)
#max_age = 3600
# Indicate which methods can be used during the actual request. (list value)
#allow_methods = GET,POST,PUT,DELETE,OPTIONS
# Indicate which header field names may be used during the actual request.
# (list value)
#allow_headers = Content-Type,Cache-Control,Content-Language,Expires,Last-Modified,Pragma
[cue_monitor]
#

View File

@ -13,6 +13,7 @@ stevedore>=1.5.0 # Apache-2.0
oslo.config>=2.7.0 # Apache-2.0
oslo.db>=4.1.0 # Apache-2.0
oslo.log>=1.12.0 # Apache-2.0
oslo.middleware>=3.0.0 # Apache-2.0
oslo.policy>=0.5.0 # Apache-2.0
oslo.rootwrap>=2.0.0 # Apache-2.0
oslo.i18n>=1.5.0 # Apache-2.0

View File

@ -10,6 +10,7 @@ namespace = cue.taskflow
namespace = keystonemiddleware.auth_token
namespace = oslo.db
namespace = oslo.messaging
namespace = oslo.middleware.cors
namespace = oslo.log
namespace = oslo.policy
namespace = oslo.service.service