From 994646673c0bd8b8419940eec33d590cd3b7b123 Mon Sep 17 00:00:00 2001 From: Lukas Holecek Date: May 15 2019 11:02:55 +0000 Subject: Use flask-cors library to support CORS headers Moves the CORS header support to the library. JIRA: FACTORY-4516 JIRA: FACTORY-4524 Signed-off-by: Lukas Holecek --- diff --git a/requirements.txt b/requirements.txt index f805c98..0e75e9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ fedora_messaging Flask Flask-RESTful!=0.3.6 Flask-SQLAlchemy +flask-cors SQLAlchemy gssapi flask-oidc diff --git a/tests/conftest.py b/tests/conftest.py index cf7eba5..3ba5f3b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -79,11 +79,6 @@ def enable_ssl(app, monkeypatch): @pytest.fixture() -def enable_cors(app, monkeypatch): - monkeypatch.setitem(app.config, 'CORS_URL', 'https://bodhi.fedoraproject.org') - - -@pytest.fixture() def enable_permission_mapping(app, monkeypatch): monkeypatch.setitem(app.config, 'PERMISSION_MAPPING', { diff --git a/tests/test_api_v10.py b/tests/test_api_v10.py index 38a3ece..8e02af6 100644 --- a/tests/test_api_v10.py +++ b/tests/test_api_v10.py @@ -638,68 +638,38 @@ def test_about_endpoint(client, trailing_slash): assert output['auth_method'] == client.application.config['AUTH_METHOD'] -@pytest.mark.usefixtures('enable_cors') -def test_cors_about(client, session): - r = client.get('/api/v1.0/about') - - assert 'Access-Control-Allow-Origin' in list(r.headers.keys()) - assert 'Access-Control-Allow-Headers' in list(r.headers.keys()) - assert 'Access-Control-Allow-Method' in list(r.headers.keys()) - assert r.headers['Access-Control-Allow-Origin'] == 'https://bodhi.fedoraproject.org' - assert r.headers['Access-Control-Allow-Headers'] == 'Content-Type' - assert r.headers['Access-Control-Allow-Method'] == 'POST, OPTIONS' - - output = json.loads(r.get_data(as_text=True)) - assert r.status_code == 200 - assert output['version'] == __version__ - - -def test_no_cors_about(client, session): - r = client.get('/api/v1.0/about') - - assert 'Access-Control-Allow-Origin' not in list(r.headers.keys()) - assert 'Access-Control-Allow-Headers' not in list(r.headers.keys()) - assert 'Access-Control-Allow-Method' not in list(r.headers.keys()) - - output = json.loads(r.get_data(as_text=True)) - assert r.status_code == 200 - assert output['version'] == __version__ - - -@pytest.mark.usefixtures('enable_cors') -def test_cors_waivers(client, session): - for i in range(0, 3): - create_waiver(session, subject_type='koji_build', subject_identifier="%d" % i, - testcase="case %d" % i, username='foo %d' % i, - product_version='foo-%d' % i, comment='bla bla bla') - r = client.get('/api/v1.0/waivers/') - - assert 'Access-Control-Allow-Origin' in list(r.headers.keys()) - assert 'Access-Control-Allow-Headers' in list(r.headers.keys()) - assert 'Access-Control-Allow-Method' in list(r.headers.keys()) - assert r.headers['Access-Control-Allow-Origin'] == 'https://bodhi.fedoraproject.org' - assert r.headers['Access-Control-Allow-Headers'] == 'Content-Type' - assert r.headers['Access-Control-Allow-Method'] == 'POST, OPTIONS' +def test_cors_good(client, session): + headers = { + 'Access-Control-Request-Method': 'POST', + 'Access-Control-Request-Headers': 'Content-Type', + 'Origin': 'https://bodhi.fedoraproject.org', + } + r = client.options( + '/api/v1.0/waivers/', + content_type='Content-Type', + headers=headers + ) - res_data = json.loads(r.get_data(as_text=True)) assert r.status_code == 200 - assert len(res_data['data']) == 3 + assert r.headers.get('Access-Control-Allow-Origin') == 'https://bodhi.fedoraproject.org' + assert 'POST' in r.headers.get('Access-Control-Allow-Methods', '').split(', ') -def test_no_cors_waivers(client, session): - for i in range(0, 3): - create_waiver(session, subject_type='koji_build', subject_identifier="%d" % i, - testcase="case %d" % i, username='foo %d' % i, - product_version='foo-%d' % i, comment='bla bla bla') - r = client.get('/api/v1.0/waivers/') - - assert 'Access-Control-Allow-Origin' not in list(r.headers.keys()) - assert 'Access-Control-Allow-Headers' not in list(r.headers.keys()) - assert 'Access-Control-Allow-Method' not in list(r.headers.keys()) +def test_cors_bad(client, session): + headers = { + 'Access-Control-Request-Method': 'POST', + 'Access-Control-Request-Headers': 'Content-Type', + 'Origin': 'localhost', + } + r = client.options( + '/api/v1.0/waivers/', + content_type='Content-Type', + headers=headers + ) - res_data = json.loads(r.get_data(as_text=True)) assert r.status_code == 200 - assert len(res_data['data']) == 3 + assert 'Access-Control-Allow-Origin' not in r.headers + assert 'Access-Control-Allow-Methods' not in r.headers @patch('waiverdb.auth.get_user', return_value=('foo', {})) diff --git a/waiverdb.spec b/waiverdb.spec index ef91d51..91a796f 100644 --- a/waiverdb.spec +++ b/waiverdb.spec @@ -31,6 +31,7 @@ BuildRequires: python3-sphinxcontrib-httpdomain BuildRequires: python3-sphinxcontrib-issuetracker BuildRequires: python3-flask BuildRequires: python3-sqlalchemy +BuildRequires: python3-flask-cors BuildRequires: python3-flask-restful BuildRequires: python3-flask-sqlalchemy BuildRequires: python3-psycopg2 @@ -47,6 +48,7 @@ BuildRequires: python3-prometheus_client BuildRequires: python3-six Requires: python3-flask Requires: python3-sqlalchemy +Requires: python3-flask-cors Requires: python3-flask-restful Requires: python3-flask-sqlalchemy Requires: python3-psycopg2 diff --git a/waiverdb/app.py b/waiverdb/app.py index b2181b1..96792ec 100644 --- a/waiverdb/app.py +++ b/waiverdb/app.py @@ -8,6 +8,7 @@ except ImportError: from urlparse import urlparse, urlunsplit from flask import Flask, current_app +from flask_cors import CORS from flask_migrate import Migrate from sqlalchemy import event from sqlalchemy.exc import ProgrammingError @@ -23,6 +24,18 @@ from werkzeug.exceptions import default_exceptions from waiverdb.monitor import db_hook_event_listeners +def enable_cors(app): + """ + Enables CORS headers. + """ + # backward compatibility with old CORS_URL option + cors_url = app.config.get('CORS_URL') + if cors_url: + app.config['CORS_ORIGINS'] = cors_url + + CORS(app) + + def load_config(app): # Load default config, then override that with a config file if os.getenv('DEV') == 'true': @@ -63,18 +76,6 @@ def populate_db_config(app): app.config['SQLALCHEMY_DATABASE_URI'] = dburi -def insert_headers(response): - """ Insert the CORS headers for the give response if there are any - configured for the application. - """ - cors_url = current_app.config.get('CORS_URL') - if cors_url: - response.headers['Access-Control-Allow-Origin'] = cors_url - response.headers['Access-Control-Allow-Headers'] = 'Content-Type' - response.headers['Access-Control-Allow-Method'] = 'POST, OPTIONS' - return response - - # applicaiton factory http://flask.pocoo.org/docs/0.12/patterns/appfactories/ def create_app(config_obj=None): app = Flask(__name__) @@ -107,11 +108,11 @@ def create_app(config_obj=None): app.add_url_rule('/healthcheck', view_func=healthcheck) register_event_handlers(app) - app.after_request(insert_headers) - # initialize DB event listeners from the monitor module app.before_first_request(db_hook_event_listeners) + enable_cors(app) + return app diff --git a/waiverdb/config.py b/waiverdb/config.py index 3a1fe61..0fa2338 100644 --- a/waiverdb/config.py +++ b/waiverdb/config.py @@ -62,3 +62,5 @@ class TestingConfig(Config): OIDC_REQUIRED_SCOPE = 'waiverdb_scope' OIDC_RESOURCE_SERVER_ONLY = True SUPERUSERS = ['bodhi'] + + CORS_ORIGINS = 'https://bodhi.fedoraproject.org'