diff --git a/cloudkitty/common/defaults.py b/cloudkitty/common/defaults.py new file mode 100644 index 00000000..534eb58b --- /dev/null +++ b/cloudkitty/common/defaults.py @@ -0,0 +1,46 @@ +# Copyright 2016 Hewlett Packard Enterprise Development Corporation, LP +# +# 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 oslo_middleware import cors + + +def set_config_defaults(): + """This method updates all configuration default values.""" + set_cors_middleware_defaults() + + +def set_cors_middleware_defaults(): + """Update default configuration options for oslo.middleware.""" + # CORS Defaults + # TODO(krotscheck): Update with https://review.openstack.org/#/c/285368/ + cfg.set_defaults(cors.CORS_OPTS, + allow_headers=['X-Auth-Token', + 'X-Identity-Status', + 'X-Roles', + 'X-Service-Catalog', + 'X-User-Id', + 'X-Tenant-Id', + 'X-OpenStack-Request-ID'], + expose_headers=['X-Auth-Token', + 'X-Subject-Token', + 'X-Service-Token', + 'X-OpenStack-Request-ID'], + allow_methods=['GET', + 'PUT', + 'POST', + 'DELETE', + 'PATCH'] + ) diff --git a/cloudkitty/service.py b/cloudkitty/service.py index 160e6567..d3d76355 100644 --- a/cloudkitty/service.py +++ b/cloudkitty/service.py @@ -21,6 +21,8 @@ import sys from oslo_config import cfg from oslo_log import log as logging +from cloudkitty.common import defaults + service_opts = [ cfg.StrOpt('host', @@ -38,5 +40,6 @@ cfg.CONF.register_opts(service_opts) def prepare_service(): logging.register_options(cfg.CONF) cfg.CONF(sys.argv[1:], project='cloudkitty') + defaults.set_config_defaults() logging.setup(cfg.CONF, 'cloudkitty') diff --git a/cloudkitty/tests/gabbi/fixtures.py b/cloudkitty/tests/gabbi/fixtures.py index afdc1032..9c5ffcb7 100644 --- a/cloudkitty/tests/gabbi/fixtures.py +++ b/cloudkitty/tests/gabbi/fixtures.py @@ -193,6 +193,10 @@ class ConfigFixture(fixture.GabbiFixture): os.path.abspath('etc/cloudkitty/policy.json'), group='oslo_policy', enforce_type=True) + conf.set_override('api_paste_config', + os.path.abspath( + 'cloudkitty/tests/gabbi/gabbi_paste.ini') + ) conf.import_group('storage', 'cloudkitty.storage') conf.set_override('backend', 'sqlalchemy', 'storage', enforce_type=True) @@ -323,6 +327,27 @@ class NowStorageDataFixture(BaseStorageDataFixture): '3d9a1b33-482f-42fd-aef9-b575a3da9369') +class CORSConfigFixture(fixture.GabbiFixture): + """Inject mock configuration for the CORS middleware.""" + + def start_fixture(self): + # Here we monkeypatch GroupAttr.__getattr__, necessary because the + # paste.ini method of initializing this middleware creates its own + # ConfigOpts instance, bypassing the regular config fixture. + + def _mock_getattr(instance, key): + if key != 'allowed_origin': + return self._original_call_method(instance, key) + return "http://valid.example.com" + + self._original_call_method = cfg.ConfigOpts.GroupAttr.__getattr__ + cfg.ConfigOpts.GroupAttr.__getattr__ = _mock_getattr + + def stop_fixture(self): + """Remove the monkeypatch.""" + cfg.ConfigOpts.GroupAttr.__getattr__ = self._original_call_method + + def setup_app(): rpc.init() - return app.setup_app() + return app.load_app() diff --git a/cloudkitty/tests/gabbi/gabbi_paste.ini b/cloudkitty/tests/gabbi/gabbi_paste.ini new file mode 100644 index 00000000..977c013b --- /dev/null +++ b/cloudkitty/tests/gabbi/gabbi_paste.ini @@ -0,0 +1,15 @@ +# This is a custom paste.ini for our gabbi tests. It enables all middleware +# except for auth tokens. + +[pipeline:main] +pipeline = cors request_id ck_api_v1 + +[app:ck_api_v1] +paste.app_factory = cloudkitty.api.app:app_factory + +[filter:request_id] +paste.filter_factory = oslo_middleware:RequestId.factory + +[filter:cors] +paste.filter_factory = oslo_middleware.cors:filter_factory +oslo_config_project = cloudkitty diff --git a/cloudkitty/tests/gabbi/gabbits/middleware.yaml b/cloudkitty/tests/gabbi/gabbits/middleware.yaml new file mode 100644 index 00000000..677967a8 --- /dev/null +++ b/cloudkitty/tests/gabbi/gabbits/middleware.yaml @@ -0,0 +1,40 @@ +fixtures: + - ConfigFixture + - CORSConfigFixture + +tests: + + - name: valid cors options + OPTIONS: / + status: 200 + request_headers: + origin: http://valid.example.com + access-control-request-method: GET + response_headers: + access-control-allow-origin: http://valid.example.com + + - name: invalid cors options + OPTIONS: / + status: 200 + request_headers: + origin: http://invalid.example.com + access-control-request-method: GET + response_forbidden_headers: + - access-control-allow-origin + + - name: valid cors get + GET: / + status: 200 + request_headers: + origin: http://valid.example.com + access-control-request-method: GET + response_headers: + access-control-allow-origin: http://valid.example.com + + - name: invalid cors get + GET: / + status: 200 + request_headers: + origin: http://invalid.example.com + response_forbidden_headers: + - access-control-allow-origin diff --git a/etc/cloudkitty/api_paste.ini b/etc/cloudkitty/api_paste.ini index 13a724a1..31990b5b 100644 --- a/etc/cloudkitty/api_paste.ini +++ b/etc/cloudkitty/api_paste.ini @@ -1,5 +1,5 @@ [pipeline:main] -pipeline = request_id authtoken ck_api_v1 +pipeline = cors request_id authtoken ck_api_v1 [app:ck_api_v1] paste.app_factory = cloudkitty.api.app:app_factory @@ -10,3 +10,7 @@ paste.filter_factory = cloudkitty.api.middleware: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 = cloudkitty diff --git a/etc/cloudkitty/cloudkitty.conf.sample b/etc/cloudkitty/cloudkitty.conf.sample index a53f1ff8..98d33dc3 100644 --- a/etc/cloudkitty/cloudkitty.conf.sample +++ b/etc/cloudkitty/cloudkitty.conf.sample @@ -126,6 +126,66 @@ #services = compute,image,volume,network.bw.in,network.bw.out,network.floating +[cors] + +# +# From oslo.middleware.cors +# + +# Indicate whether this resource may be shared with the domain +# received in the requests "origin" header. (list value) +#allowed_origin = + +# 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 = X-Auth-Token,X-Subject-Token,X-Service-Token,X-OpenStack-Request-ID,X-Server-Management-Url + +# 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,PUT,POST,DELETE,PATCH + +# Indicate which header field names may be used during the actual +# request. (list value) +#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 + + +[cors.subdomain] + +# +# From oslo.middleware.cors +# + +# Indicate whether this resource may be shared with the domain +# received in the requests "origin" header. (list value) +#allowed_origin = + +# 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 = X-Auth-Token,X-Subject-Token,X-Service-Token,X-OpenStack-Request-ID,X-Server-Management-Url + +# 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,PUT,POST,DELETE,PATCH + +# Indicate which header field names may be used during the actual +# request. (list value) +#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 + + [database] # diff --git a/etc/oslo-config-generator/cloudkitty.conf b/etc/oslo-config-generator/cloudkitty.conf index 513bbbe2..3b1a4c52 100644 --- a/etc/oslo-config-generator/cloudkitty.conf +++ b/etc/oslo-config-generator/cloudkitty.conf @@ -5,5 +5,6 @@ namespace = oslo.concurrency namespace = oslo.db namespace = oslo.log namespace = oslo.messaging +namespace = oslo.middleware.cors namespace = oslo.policy namespace = keystonemiddleware.auth_token diff --git a/setup.cfg b/setup.cfg index 572bb3ec..bd31c67a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,9 @@ console_scripts = oslo.config.opts = cloudkitty.common.config = cloudkitty.common.config:list_opts +oslo.config.opts.defaults = + cloudkitty.common.config = cloudkitty.common.defaults:set_cors_middleware_defaults + cloudkitty.collector.backends = fake = cloudkitty.collector.fake:CSVCollector ceilometer = cloudkitty.collector.ceilometer:CeilometerCollector