Repository: zestedesavoir/django-cors-middleware Branch: master Commit: 50d07ca143ea Files: 20 Total size: 35.9 KB Directory structure: gitextract_o3_czq0e/ ├── .coveragerc ├── .github/ │ └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── MANIFEST.in ├── README.rst ├── corsheaders/ │ ├── __init__.py │ ├── defaults.py │ ├── middleware.py │ ├── migrations/ │ │ ├── 0001_initial.py │ │ └── __init__.py │ ├── models.py │ ├── signals.py │ └── tests.py ├── flake8.cfg ├── setup.cfg ├── setup.py ├── tests.py └── tox.ini ================================================ FILE CONTENTS ================================================ ================================================ FILE: .coveragerc ================================================ [run] source=corsheaders [report] omit=*tests*,*__init__* show_missing=True exclude_lines = # Have to re-enable the standard pragma pragma: no cover # Don't complain about missing debug-only code: def __repr__ if self\.debug # Don't complain if tests don't hit defensive assertion code: raise AssertionError raise NotImplementedError # Don't complain if non-runnable code isn't run: if 0: if __name__ == .__main__.: ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ **Issue(s)** : ================================================ FILE: .gitignore ================================================ *.pyc *.egg-info build/ dist/ .eggs/ .idea/ .tox/ # Packages *.egg *.egg-info .coverage dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 ================================================ FILE: .travis.yml ================================================ language: python python: - 2.7 - 3.4 - 3.5 - 3.6 sudo: false install: - pip install tox-travis script: - tox # Coverage data generation - pip install "Django<2.2" flake8 coverage codecov - flake8 --config flake8.cfg . - coverage run setup.py test - coverage report after_success: - codecov ================================================ FILE: LICENSE.txt ================================================ Original work Copyright 2013 Otto Yiu and other contributors Modified work Copyright 2016 Zeste de Savoir Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ include README.rst include LICENSE.txt ================================================ FILE: README.rst ================================================ django-cors-middleware ====================== ``django-cors-middleware`` is **depreciated**, please use `adamchainz/django-cors-headers `_ instead. It was created at a time when ``django-cors-headers`` was not maintained, but it is well maintained now and there is no need to have two different packages for that. Extract from `adamchainz/django-cors-headers README's About section `_: **django-cors-headers** was created in January 2013 by Otto Yiu. It went unmaintained from August 2015 and was forked in January 2016 to the package `django-cors-middleware `_ by Laville Augustin at Zeste de Savoir. In September 2016, Adam Johnson, Ed Morley, and others gained maintenance responsibility for **django-cors-headers** (`Issue 110 `__) from Otto Yiu. Basically all of the changes in the forked **django-cors-middleware** were merged back, or re-implemented in a different way, so it should be possible to switch back. If there's a feature that hasn't been merged, please open an issue about it. ================================================ FILE: corsheaders/__init__.py ================================================ __version__ = '999' ================================================ FILE: corsheaders/defaults.py ================================================ from django.conf import settings default_headers = ( 'x-requested-with', 'content-type', 'accept', 'origin', 'authorization', 'x-csrftoken', 'user-agent', 'accept-encoding', 'cache-control', ) CORS_ALLOW_HEADERS = getattr(settings, 'CORS_ALLOW_HEADERS', default_headers) default_methods = ( 'GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', ) CORS_ALLOW_METHODS = getattr(settings, 'CORS_ALLOW_METHODS', default_methods) CORS_ALLOW_CREDENTIALS = getattr(settings, 'CORS_ALLOW_CREDENTIALS', False) CORS_PREFLIGHT_MAX_AGE = getattr(settings, 'CORS_PREFLIGHT_MAX_AGE', 86400) CORS_ORIGIN_ALLOW_ALL = getattr(settings, 'CORS_ORIGIN_ALLOW_ALL', False) CORS_ORIGIN_WHITELIST = getattr(settings, 'CORS_ORIGIN_WHITELIST', ()) CORS_ORIGIN_REGEX_WHITELIST = getattr( settings, 'CORS_ORIGIN_REGEX_WHITELIST', ()) CORS_EXPOSE_HEADERS = getattr(settings, 'CORS_EXPOSE_HEADERS', ()) CORS_URLS_REGEX = getattr(settings, 'CORS_URLS_REGEX', '^.*$') CORS_MODEL = getattr(settings, 'CORS_MODEL', None) CORS_REPLACE_HTTPS_REFERER = getattr( settings, 'CORS_REPLACE_HTTPS_REFERER', False) CORS_URLS_ALLOW_ALL_REGEX = getattr(settings, 'CORS_URLS_ALLOW_ALL_REGEX', ()) ================================================ FILE: corsheaders/middleware.py ================================================ import re from django import http try: from urlparse import urlparse except ImportError: from urllib.parse import urlparse from django.apps import apps from django.utils.cache import patch_vary_headers from django.utils.deprecation import MiddlewareMixin from corsheaders import defaults as settings from corsheaders import signals get_model = apps.get_model ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin' ACCESS_CONTROL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers' ACCESS_CONTROL_ALLOW_CREDENTIALS = 'Access-Control-Allow-Credentials' ACCESS_CONTROL_ALLOW_HEADERS = 'Access-Control-Allow-Headers' ACCESS_CONTROL_ALLOW_METHODS = 'Access-Control-Allow-Methods' ACCESS_CONTROL_MAX_AGE = 'Access-Control-Max-Age' class CorsPostCsrfMiddleware(MiddlewareMixin): def _https_referer_replace_reverse(self, request): """ Put the HTTP_REFERER back to its original value and delete the temporary storage """ if (settings.CORS_REPLACE_HTTPS_REFERER and 'ORIGINAL_HTTP_REFERER' in request.META): http_referer = request.META['ORIGINAL_HTTP_REFERER'] request.META['HTTP_REFERER'] = http_referer del request.META['ORIGINAL_HTTP_REFERER'] def process_request(self, request): self._https_referer_replace_reverse(request) return None def process_view(self, request, callback, callback_args, callback_kwargs): self._https_referer_replace_reverse(request) return None class CorsMiddleware(MiddlewareMixin): def _https_referer_replace(self, request): """ When https is enabled, django CSRF checking includes referer checking which breaks when using CORS. This function updates the HTTP_REFERER header to make sure it matches HTTP_HOST, provided that our cors logic succeeds """ origin = request.META.get('HTTP_ORIGIN') if (request.is_secure() and origin and 'ORIGINAL_HTTP_REFERER' not in request.META): url = urlparse(origin) if (not settings.CORS_ORIGIN_ALLOW_ALL and self.origin_not_found_in_white_lists(origin, url)): return try: http_referer = request.META['HTTP_REFERER'] http_host = "https://%s/" % request.META['HTTP_HOST'] request.META = request.META.copy() request.META['ORIGINAL_HTTP_REFERER'] = http_referer request.META['HTTP_REFERER'] = http_host except KeyError: pass def process_request(self, request): """ If CORS preflight header, then create an empty body response (200 OK) and return it Django won't bother calling any other request view/exception middleware along with the requested view; it will call any response middlewares """ if self.is_enabled(request) and settings.CORS_REPLACE_HTTPS_REFERER: self._https_referer_replace(request) if (self.is_enabled(request) and request.method == 'OPTIONS' and "HTTP_ACCESS_CONTROL_REQUEST_METHOD" in request.META): response = http.HttpResponse() return response return None def process_view(self, request, callback, callback_args, callback_kwargs): """ Do the referer replacement here as well """ if self.is_enabled(request) and settings.CORS_REPLACE_HTTPS_REFERER: self._https_referer_replace(request) return None def process_response(self, request, response): """ Add the respective CORS headers """ origin = request.META.get('HTTP_ORIGIN') if self.is_enabled(request) and origin: # todo: check hostname from db instead url = urlparse(origin) if settings.CORS_MODEL is not None: model = get_model(*settings.CORS_MODEL.split('.')) if model.objects.filter(cors=url.netloc).exists(): response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin if (not settings.CORS_ORIGIN_ALLOW_ALL and self.origin_not_found_in_white_lists(origin, url) and not self.regex_url_allow_all_match(request.path) and not self.check_signal(request)): return response if settings.CORS_ORIGIN_ALLOW_ALL and not settings.CORS_ALLOW_CREDENTIALS: response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*" else: response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin patch_vary_headers(response, ['Origin']) if len(settings.CORS_EXPOSE_HEADERS): response[ACCESS_CONTROL_EXPOSE_HEADERS] = ', '.join( settings.CORS_EXPOSE_HEADERS) if settings.CORS_ALLOW_CREDENTIALS: response[ACCESS_CONTROL_ALLOW_CREDENTIALS] = 'true' if request.method == 'OPTIONS': response[ACCESS_CONTROL_ALLOW_HEADERS] = ', '.join( settings.CORS_ALLOW_HEADERS) response[ACCESS_CONTROL_ALLOW_METHODS] = ', '.join( settings.CORS_ALLOW_METHODS) if settings.CORS_PREFLIGHT_MAX_AGE: response[ACCESS_CONTROL_MAX_AGE] = \ settings.CORS_PREFLIGHT_MAX_AGE return response def origin_not_found_in_white_lists(self, origin, url): return (url.netloc not in settings.CORS_ORIGIN_WHITELIST and not self.regex_domain_match(origin)) def regex_domain_match(self, origin): for domain_pattern in settings.CORS_ORIGIN_REGEX_WHITELIST: if re.match(domain_pattern, origin): return origin def is_enabled(self, request): return re.match(settings.CORS_URLS_REGEX, request.path) or \ self.regex_url_allow_all_match(request.path) or \ self.check_signal(request) def check_signal(self, request): signal_response = signals.check_request_enabled.send( sender=None, request=request ) for function, return_value in signal_response: if return_value: return True return False def regex_url_allow_all_match(self, path): for url_pattern in settings.CORS_URLS_ALLOW_ALL_REGEX: if re.match(url_pattern, path): return path ================================================ FILE: corsheaders/migrations/0001_initial.py ================================================ # -*- coding: utf-8 -*- # Generated by Django 1.10.1 on 2017-01-18 21:36 from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ ] operations = [ migrations.CreateModel( name='CorsModel', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('cors', models.CharField(max_length=255)), ], ), ] ================================================ FILE: corsheaders/migrations/__init__.py ================================================ ================================================ FILE: corsheaders/models.py ================================================ from django.db import models # For signal registration from .signals import check_request_enabled # noqa class CorsModel(models.Model): cors = models.CharField(max_length=255) ================================================ FILE: corsheaders/signals.py ================================================ import django.dispatch # Return Truthy values to enable a specific request. # This allows users to build custom logic into the request handling check_request_enabled = django.dispatch.Signal( providing_args=["request"] ) ================================================ FILE: corsheaders/tests.py ================================================ from django.conf.urls import url from django.http import HttpResponse from django.test import TestCase from corsheaders.middleware import CorsMiddleware, CorsPostCsrfMiddleware from corsheaders.middleware import ACCESS_CONTROL_ALLOW_ORIGIN from corsheaders.middleware import ACCESS_CONTROL_EXPOSE_HEADERS from corsheaders.middleware import ACCESS_CONTROL_ALLOW_CREDENTIALS from corsheaders.middleware import ACCESS_CONTROL_ALLOW_HEADERS from corsheaders.middleware import ACCESS_CONTROL_ALLOW_METHODS from corsheaders.middleware import ACCESS_CONTROL_MAX_AGE from corsheaders import defaults as settings from corsheaders import signals from mock import Mock from mock import patch def test_view(request): return HttpResponse("Test view") def test_view_http401(request): return HttpResponse('Unauthorized', status=401) urlpatterns = [ url(r'^test-view/$', test_view, name='test-view'), url(r'^test-view-http401/$', test_view_http401, name='test-view-http401'), ] class settings_override(object): def __init__(self, **kwargs): self.overrides = kwargs def __enter__(self): self.old = dict((key, getattr(settings, key)) for key in self.overrides) settings.__dict__.update(self.overrides) def __exit__(self, exc, value, tb): settings.__dict__.update(self.old) class TestCorsMiddlewareProcessRequest(TestCase): def setUp(self): self.middleware = CorsMiddleware() def test_process_request(self): request = Mock(path='/') request.method = 'OPTIONS' request.META = {'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'value'} with settings_override(CORS_URLS_REGEX='^.*$'): response = self.middleware.process_request(request) self.assertIsInstance(response, HttpResponse) def test_process_request_empty_header(self): request = Mock(path='/') request.method = 'OPTIONS' request.META = {'HTTP_ACCESS_CONTROL_REQUEST_METHOD': ''} with settings_override(CORS_URLS_REGEX='^.*$'): response = self.middleware.process_request(request) self.assertIsInstance(response, HttpResponse) def test_process_request_no_header(self): request = Mock(path='/') request.method = 'OPTIONS' request.META = {} response = self.middleware.process_request(request) self.assertIsNone(response) def test_process_request_not_options(self): request = Mock(path='/') request.method = 'GET' request.META = {'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'value'} response = self.middleware.process_request(request) self.assertIsNone(response) def test_process_request_replace_https_referer(self): post_middleware = CorsPostCsrfMiddleware() request = Mock(path='/') request.method = 'GET' request.is_secure = lambda: True # make sure it doesnt blow up when HTTP_REFERER is not present request.META = { 'HTTP_HOST': 'foobar.com', 'HTTP_ORIGIN': 'https://foo.google.com', } with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_request(request) self.assertIsNone(response) # make sure it doesnt blow up when HTTP_HOST is not present request.META = { 'HTTP_REFERER': 'http://foo.google.com/', 'HTTP_ORIGIN': 'https://foo.google.com', } with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_request(request) self.assertIsNone(response) request.is_secure = lambda: False request.META = { 'HTTP_REFERER': 'http://foo.google.com/', 'HTTP_HOST': 'foobar.com', 'HTTP_ORIGIN': 'http://foo.google.com', } # test that we won't replace if the request is not secure with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_request(request) self.assertIsNone(response) self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META) self.assertEquals(request.META['HTTP_REFERER'], 'http://foo.google.com/') request.is_secure = lambda: True request.META = { 'HTTP_REFERER': 'https://foo.google.com/', 'HTTP_HOST': 'foobar.com', 'HTTP_ORIGIN': 'https://foo.google.com', } # test that we won't replace with the setting off with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*'): response = self.middleware.process_request(request) self.assertIsNone(response) self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META) self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/') with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_request(request) self.assertIsNone(response) self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/') self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/') # make sure the replace code is idempotent with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_view(request, None, None, None) self.assertIsNone(response) self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/') self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/') with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True): post_middleware.process_request(request) self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META) self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/') with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True): response = post_middleware.process_request(request) self.assertIsNone(response) def test_process_view_replace_https_referer(self): post_middleware = CorsPostCsrfMiddleware() request = Mock(path='/') request.method = 'GET' request.is_secure = lambda: True request.META = { 'HTTP_REFERER': 'https://foo.google.com/', 'HTTP_HOST': 'foobar.com', 'HTTP_ORIGIN': 'https://foo.google.com', } with settings_override(CORS_URLS_REGEX='^.*$', CORS_ORIGIN_REGEX_WHITELIST='.*google.*', CORS_REPLACE_HTTPS_REFERER=True): response = self.middleware.process_view(request, None, None, None) self.assertIsNone(response) self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/') self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/') with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True): post_middleware.process_view(request, None, None, None) self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META) self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/') with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True): response = post_middleware.process_view(request, None, None, None) self.assertIsNone(response) @patch('corsheaders.middleware.settings') class TestCorsMiddlewareProcessResponse(TestCase): def setUp(self): self.middleware = CorsMiddleware() def assertAccessControlAllowOriginEquals(self, response, header): self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, response, "Response %r does " "NOT have %r header" % (response, ACCESS_CONTROL_ALLOW_ORIGIN)) self.assertEqual(response[ACCESS_CONTROL_ALLOW_ORIGIN], header) def test_process_response_no_origin(self, settings): settings.CORS_MODEL = None settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={}) processed = self.middleware.process_response(request, response) self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed) def test_process_response_not_in_whitelist(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ORIGIN_WHITELIST = ['example.com'] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foobar.it'}) processed = self.middleware.process_response(request, response) self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed) def test_process_response_signal_works(self, settings): def handler(sender, request, **kwargs): return True settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ORIGIN_WHITELIST = ['example.com'] settings.CORS_URLS_REGEX = '^.*$' signals.check_request_enabled.connect(handler) response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foobar.it'}) processed = self.middleware.process_response(request, response) self.assertIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed) def test_process_response_in_whitelist(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ORIGIN_WHITELIST = ['example.com', 'foobar.it'] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foobar.it'}) processed = self.middleware.process_response(request, response) self.assertAccessControlAllowOriginEquals(processed, 'http://foobar.it') def test_process_response_expose_headers(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_EXPOSE_HEADERS = ['accept', 'origin', 'content-type'] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'}) processed = self.middleware.process_response(request, response) self.assertEqual(processed[ACCESS_CONTROL_EXPOSE_HEADERS], 'accept, origin, content-type') def test_process_response_dont_expose_headers(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_EXPOSE_HEADERS = [] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'}) processed = self.middleware.process_response(request, response) self.assertNotIn(ACCESS_CONTROL_EXPOSE_HEADERS, processed) def test_process_response_allow_credentials(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_ALLOW_CREDENTIALS = True settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'}) processed = self.middleware.process_response(request, response) self.assertEqual(processed[ACCESS_CONTROL_ALLOW_CREDENTIALS], 'true') def test_process_response_dont_allow_credentials(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_ALLOW_CREDENTIALS = False settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'}) processed = self.middleware.process_response(request, response) self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, processed) def test_process_response_options_method(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_ALLOW_HEADERS = ['content-type', 'origin'] settings.CORS_ALLOW_METHODS = ['GET', 'OPTIONS'] settings.CORS_PREFLIGHT_MAX_AGE = 1002 settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request_headers = {'HTTP_ORIGIN': 'http://example.com'} request = Mock(path='/', META=request_headers, method='OPTIONS') processed = self.middleware.process_response(request, response) self.assertEqual(processed[ACCESS_CONTROL_ALLOW_HEADERS], 'content-type, origin') self.assertEqual(processed[ACCESS_CONTROL_ALLOW_METHODS], 'GET, OPTIONS') self.assertEqual(processed[ACCESS_CONTROL_MAX_AGE], '1002') def test_process_response_options_method_no_max_age(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_ALLOW_HEADERS = ['content-type', 'origin'] settings.CORS_ALLOW_METHODS = ['GET', 'OPTIONS'] settings.CORS_PREFLIGHT_MAX_AGE = 0 settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request_headers = {'HTTP_ORIGIN': 'http://example.com'} request = Mock(path='/', META=request_headers, method='OPTIONS') processed = self.middleware.process_response(request, response) self.assertEqual(processed[ACCESS_CONTROL_ALLOW_HEADERS], 'content-type, origin') self.assertEqual(processed[ACCESS_CONTROL_ALLOW_METHODS], 'GET, OPTIONS') self.assertNotIn(ACCESS_CONTROL_MAX_AGE, processed) def test_process_response_whitelist_with_port(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ALLOW_METHODS = ['OPTIONS'] settings.CORS_ORIGIN_WHITELIST = ('localhost:9000',) settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request_headers = {'HTTP_ORIGIN': 'http://localhost:9000'} request = Mock(path='/', META=request_headers, method='OPTIONS') processed = self.middleware.process_response(request, response) self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_CREDENTIALS, None), 'true') def test_process_response_adds_origin_when_domain_found_in_origin_regex_whitelist(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_REGEX_WHITELIST = ('^http?://(\w+\.)?google\.com$', ) settings.CORS_ALLOW_CREDENTIALS = True settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ALLOW_METHODS = ['OPTIONS'] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request_headers = {'HTTP_ORIGIN': 'http://foo.google.com'} request = Mock(path='/', META=request_headers, method='OPTIONS') processed = self.middleware.process_response(request, response) self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), 'http://foo.google.com') def test_process_response_will_not_add_origin_when_domain_not_found_in_origin_regex_whitelist(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_REGEX_WHITELIST = ('^http?://(\w+\.)?yahoo\.com$', ) settings.CORS_ALLOW_CREDENTIALS = True settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ALLOW_METHODS = ['OPTIONS'] settings.CORS_URLS_REGEX = '^.*$' response = HttpResponse() request_headers = {'HTTP_ORIGIN': 'http://foo.google.com'} request = Mock(path='/', META=request_headers, method='OPTIONS') processed = self.middleware.process_response(request, response) self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), None) def test_process_response_when_custom_model_enabled(self, settings): from corsheaders.models import CorsModel CorsModel.objects.create(cors='foo.google.com') settings.CORS_ORIGIN_REGEX_WHITELIST = () settings.CORS_ALLOW_CREDENTIALS = False settings.CORS_ORIGIN_ALLOW_ALL = False settings.CORS_ALLOW_METHODS = settings.default_methods settings.CORS_URLS_REGEX = '^.*$' settings.CORS_MODEL = 'corsheaders.CorsModel' response = HttpResponse() request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foo.google.com'}) processed = self.middleware.process_response(request, response) self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), 'http://foo.google.com') def test_process_response_in_allow_all_path(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False # settings.CORS_ORIGIN_WHITELIST = ['example.com', 'foobar.it'] settings.CORS_URLS_REGEX = '^.*$' settings.CORS_URLS_ALLOW_ALL_REGEX = (r'^/api/.*$',) response = HttpResponse() request = Mock(path='/api/data', META={'HTTP_ORIGIN': 'http://foobar.it'}) processed = self.middleware.process_response(request, response) self.assertAccessControlAllowOriginEquals(processed, 'http://foobar.it') def test_process_response_not_in_allow_all_path(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = False # settings.CORS_ORIGIN_WHITELIST = ['example.com', 'foobar.it'] settings.CORS_URLS_REGEX = '^.*$' settings.CORS_URLS_ALLOW_ALL_REGEX = (r'^/api/.*$',) response = HttpResponse() request = Mock(path='/data', META={'HTTP_ORIGIN': 'http://foobar.it'}) processed = self.middleware.process_response(request, response) self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed) def test_middleware_integration_get(self, settings): settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_URLS_REGEX = '^.*$' response = self.client.get('/test-view/', HTTP_ORIGIN='http://foobar.it') self.assertEqual(response.status_code, 200) self.assertAccessControlAllowOriginEquals(response, 'http://foobar.it') def test_middleware_integration_options(self, settings): settings.CORS_MODEL = None settings.CORS_URLS_REGEX = '^.*$' settings.CORS_ALLOW_CREDENTIALS = True settings.CORS_ORIGIN_ALLOW_ALL = True response = self.client.options( '/test-view/', HTTP_ORIGIN='http://foobar.it', HTTP_ACCESS_CONTROL_REQUEST_METHOD='value', ) self.assertEqual(response.status_code, 200) self.assertEqual(response[ACCESS_CONTROL_ALLOW_ORIGIN], 'http://foobar.it') self.assertEqual(response['Vary'], 'Origin') def test_middleware_integration_get_auth_view(self, settings): """ It's not clear whether the header should still be set for non-HTTP200 when not a preflight request. However this is the existing behaviour for django-cors-middleware, so at least this test makes that explicit, especially since for the switch to Django 1.10, special-handling will need to be put in place to preserve this behaviour. See `ExceptionMiddleware` mention here: https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware """ settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_URLS_REGEX = '^.*$' response = self.client.get('/test-view-http401/', HTTP_ORIGIN='http://foobar.it') self.assertEqual(response.status_code, 401) self.assertAccessControlAllowOriginEquals(response, 'http://foobar.it') def test_middleware_integration_preflight_auth_view(self, settings): """ Ensure HTTP200 and header still set, for preflight requests to views requiring authentication. See: https://github.com/ottoyiu/django-cors-headers/issues/3 """ settings.CORS_MODEL = None settings.CORS_ORIGIN_ALLOW_ALL = True settings.CORS_URLS_REGEX = '^.*$' response = self.client.options('/test-view-http401/', HTTP_ORIGIN='http://foobar.it', HTTP_ACCESS_CONTROL_REQUEST_METHOD='value') self.assertEqual(response.status_code, 200) self.assertAccessControlAllowOriginEquals(response, 'http://foobar.it') ================================================ FILE: flake8.cfg ================================================ [flake8] max-complexity=12 exclude=tests.py,.tox,*.egg max-line-length=120 ================================================ FILE: setup.cfg ================================================ [metadata] description-file = README.rst ================================================ FILE: setup.py ================================================ #!/usr/bin/env python from corsheaders import __version__ from setuptools import setup, find_packages setup( name='django-cors-middleware', version=__version__, description='django-cors-middleware is a Django application for handling the server headers required for Cross-Orig' 'in Resource Sharing (CORS). Fork of django-cors-headers.', author='Zeste de Savoir', author_email='dev@gustavi.net', url='https://github.com/zestedesavoir/django-cors-middleware', packages=find_packages(), license='MIT License', keywords='django cors middleware rest api', platforms=['any'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Framework :: Django', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Topic :: Software Development :: Libraries :: Application Frameworks', 'Topic :: Software Development :: Libraries :: Python Modules', ], install_requires=[], tests_require=['mock >= 1.0'], test_suite='tests.main', ) ================================================ FILE: tests.py ================================================ #!/usr/bin/env python """ """ import sys def run_tests(): import django from django.conf import global_settings from django.conf import settings if django.VERSION >= (1, 10): middleware_setting = 'MIDDLEWARE' else: middleware_setting = 'MIDDLEWARE_CLASSES' middleware = list(getattr(global_settings, middleware_setting) or []) middleware.append('corsheaders.middleware.CorsMiddleware') config = { 'INSTALLED_APPS': [ 'corsheaders', ], 'DATABASES': { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'TEST_NAME': ':memory:', }, }, 'ROOT_URLCONF': 'corsheaders.tests', middleware_setting: middleware, } settings.configure(**config) if hasattr(django, 'setup'): django.setup() try: from django.test.runner import DiscoverRunner as Runner except ImportError: from django.test.simple import DjangoTestSuiteRunner as Runner test_runner = Runner(verbosity=1) return test_runner.run_tests(['corsheaders']) def main(): failures = run_tests() sys.exit(failures) if __name__ == '__main__': main() ================================================ FILE: tox.ini ================================================ [tox] downloadcache = {toxworkdir}/cache/ envlist = py{27,34,35,36,37}-django110 py{27,34,35,36,37}-django111 # First Django version that drops Python 2 support py{34,35,36,37}-django20 # First Django version that drops support for Python versions below 3.5 py{35,36,37}-django21 [testenv] commands = python setup.py test deps = django18: Django >=1.8, <1.9 django19: Django >=1.9, <2.0 django110: Django >=1.10, <1.11 django111: Django >=1.11, <2.0 django20: Django >=2.0, <2.1 django21: Django >=2.1, <2.2