pax_global_header00006660000000000000000000000064146644337330014527gustar00rootroot0000000000000052 comment=c8514760cf03fcce16d77f6db7007aad429c4548 python-flask-cors-5.0.0/000077500000000000000000000000001466443373300151145ustar00rootroot00000000000000python-flask-cors-5.0.0/.github/000077500000000000000000000000001466443373300164545ustar00rootroot00000000000000python-flask-cors-5.0.0/.github/workflows/000077500000000000000000000000001466443373300205115ustar00rootroot00000000000000python-flask-cors-5.0.0/.github/workflows/release.yml000066400000000000000000000014271466443373300226600ustar00rootroot00000000000000name: Release to PyPi on: release: types: [published] jobs: deploy: name: upload release to PyPI runs-on: ubuntu-latest # Specifying a GitHub environment is optional, but strongly encouraged environment: release permissions: # IMPORTANT: this permission is mandatory for trusted publishing id-token: write steps: - uses: actions/checkout@v3 - name: Set up Python uses: actions/setup-python@v3 with: python-version: '3.11' - name: Install dependencies run: | python -m pip install --upgrade pip pip install build - name: Build package run: python -m build - name: Publish package distributions to PyPI uses: pypa/gh-action-pypi-publish@release/v1python-flask-cors-5.0.0/.github/workflows/unittests.yaml000066400000000000000000000016461466443373300234460ustar00rootroot00000000000000name: Python Unittests on: [push] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12"] dependencies: - "flask==1.1 Jinja2==3.0.3 itsdangerous==2.0.1 werkzeug==2.0.3" - "flask==2.3.2" - "flask==3.0.1" steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} cache: 'pip' # You can test your matrix by printing the current Python version - name: Install deps run: pip install -U setuptools pep8 coverage docutils pygments packaging pytest pytest-cov ${{ matrix.dependencies }} - name: Run tests run: | coverage erase python setup.py clean build install pytest --cov=flask_cors python-flask-cors-5.0.0/.gitignore000066400000000000000000000006031466443373300171030ustar00rootroot00000000000000# Virtualenv ENV env .Python include lib # Python Packages *.egg *.egg-info bin build dist sdist eggs env parts var develop-eggs .installed.cfg docs/.build docs/_build # Pip Installer Log pip-log.txt # Unit test / coverage .coverage .tox # Mac OS X .DS_Store # Generated files *.[oa] *.py[co] *.rbc #Temporary Files *.py~ *.html~ .*.sw* python-flask-cors-5.0.0/.readthedocs.yaml000066400000000000000000000003061466443373300203420ustar00rootroot00000000000000version: 2 build: os: 'ubuntu-22.04' tools: python: '3.12' sphinx: configuration: 'docs/conf.py' fail_on_warning: false python: install: - requirements: 'docs/requirements.txt' python-flask-cors-5.0.0/CHANGELOG.md000066400000000000000000000163011466443373300167260ustar00rootroot00000000000000# Change Log ## 4.0.1 ### Security * Address [CVE-2024-1681](https://github.com/advisories/GHSA-84pr-m4jr-85g5) which is a log injection vulnerability when the log level is set to debug by @aneshujevic in https://github.com/corydolphin/flask-cors/pull/351 ## 4.0.0 * Remove support for Python versions older than 3.8 by @WAKayser in https://github.com/corydolphin/flask-cors/pull/330 * Add GHA tooling by @corydolphin in https://github.com/corydolphin/flask-cors/pull/331 ## 3.1.01 * Include examples to specify that schema and port must be included in … by @YPCrumble in https://github.com/corydolphin/flask-cors/pull/294 * two small changes to the documentation, based on issue #290 by @bbbart in https://github.com/corydolphin/flask-cors/pull/291 * Fix typo by @sunarch in https://github.com/corydolphin/flask-cors/pull/304 * FIX: typo in CSRF by @sattamjh in https://github.com/corydolphin/flask-cors/pull/315 * Test against recent Python versions by @pylipp in https://github.com/corydolphin/flask-cors/pull/314 * Correct spelling mistakes by @EdwardBetts in https://github.com/corydolphin/flask-cors/pull/311 * 'Access-Control-Allow-Private-Network = true' header for http response by @chelo-kjml in https://github.com/corydolphin/flask-cors/pull/318 * docs: Fix a few typos by @timgates42 in https://github.com/corydolphin/flask-cors/pull/323 * [Docs] Fix typo in configuration documentation by @sachit-shroff in https://github.com/corydolphin/flask-cors/pull/316 ## 3.0.10 Adds support for PPC64 and ARM64 builds for distribution. Thanks @sreekanth370 ## 3.0.9 ### Security - Escape path before evaluating resource rules (thanks to Colby Morgan). Prior to this, flask-cors incorrectly evaluated CORS resource matching before path expansion. E.g. "/api/../foo.txt" would incorrectly match resources for "/api/*" whereas the path actually expands simply to "/foo.txt" ## 3.0.8 Fixes : DeprecationWarning: Using or importing the ABCs from 'collections' in Python 3.7. Thank you @juanmaneo and @jdevera for the contribution. ## 3.0.7 Updated logging.warn to logging.warning (#234) Thanks Vaibhav ## 3.0.6 Manual error in release process. Identical contents at 3.0.5. ## 3.0.5 Fixes incorrect handling of regexes containing `[`, and a few other special characters. Fixes Issue [#212](https://github.com/corydolphin/flask-cors/issues/212) ## 3.0.4 Handle response.headers being None. (Fixes issue #217) ## 3.0.3 Ensure that an Origin of '*' is never sent if supports_credentials is True (fixes Issue #202) * If `always_send=True`, and `'*'` is in the allowed origins, and a request is made without an Origin header, no `Access-Control-Allow-Origins` header will now be returned. This is breaking if you depended on it, but was a bug as it goes against the spec. ## 3.0.2 Fixes Issue #187: regression whereby header (and domain) matching was incorrectly case sensitive. Now it is not, making the behavior identical to 2.X and 1.X. ## 3.0.1 Fixes Issue #183: regression whereby regular expressions for origins with an "?" are not properly matched. ## 3.0.0 This release is largely a number of small bug fixes and improvements, along with a default change in behavior, which is technically a breaking change. **Breaking Change** We added an always_send option, enabled by default, which makes Flask-CORS inject headers even if the request did not have an 'Origin' header. Because this makes debugging far easier, and has very little downside, it has also been set as the default, making it technically a breaking change. If this actually broke something for you, please let me know, and I'll help you work around it. (#156) c7a1ecdad375a796155da6aca6a1f750337175f3 Other improvements: * Adds building of universal wheels (#175) 4674c3d54260f8897bd18e5502509363dcd0d0da * Makes Flask-CORS compatible with OAuthLib's custom header class ... (#172) aaaf904845997a3b684bc6677bdfc91656a85a04 * Fixes incorrect substring matches when strings are used as origins or headers (#165) 9cd3f295bd6b0ba87cc5f2afaca01b91ff43e72c * Fixes logging when unknown options are supplied (#152) bddb13ca6636c5d559ec67a95309c9607a3fcaba ## 2.1.3 Fixes Vary:Origin header sending behavior when regex origins are used. ## 2.1.2 Fixes package installation. Requirements.txt was not included in Manifest. ## 2.1.1 Stop dynamically referecing logger. Disable internal logging by default and reduce logging verbosity ## 2.1.0 Adds support for Flask Blueprints. ## 2.0.1 Fixes Issue #124 where only the first of multiple headers with the same name would be passed through. ## 2.0.0 **New Defaults** 1. New defaults allow all origins, all headers. **Breaking Changes** 1. Removed always_send option. 1. Removed 'headers' option as a backwards-compatible alias for 'allowed_headers' to reduce confusion. ## 2.0.0rc1 Would love to get some feedback to make sure there are no unexpected regressions. This should be backwards compatible for most people. Update default options and parameters in a backwards incompatible way. By default, all headers are now allowed, and only requests with an Origin header have CORS headers returned. If an Origin header is not present, no CORS headers are returned. Removed the following options: always_send, headers. Extension and decorator are now in separate modules sharing a core module. Test have been moved into the respective tests.extension and tests.decorator modules. More work to decompose these tests is needed. ## 1.10.3 Release Version 1.10.3 * Adds logging to Flask-Cors so it is easy to see what is going on and why * Adds support for compiled regexes as origins Big thanks to @michalbachowski and @digitizdat! ## 1.10.2 This release fixes the behavior of Access-Control-Allow-Headers and Access-Control-Expose-Headers, which was previously swapped since 1.9.0. To further fix the confusion, the `headers` parameter was renamed to more explicitly be `allow_headers`. Thanks @maximium for the bug report and implementation! ## 1.10.1 This is a bug fix release, fixing: Incorrect handling of resources and intercept_exceptions App Config options https://github.com/wcdolphin/flask-cors/issues/84 Issue with functools.partial in 1.10.0 using Python 2.7.9 https://github.com/wcdolphin/flask-cors/issues/83 Shoutout to @diiq and @joonathan for reporting these issues! ## 1.10.0 * Adds support for returning CORS headers with uncaught exceptions in production so 500s will have expected CORS headers set. This will allow clients to better surface the errors, rather than failing due to security. Reported and tested by @robertfw -- thanks! * Improved conformance of preflight request handling to W3C spec. * Code simplification and 100% test coverage :sunglasses: ## 1.9.0 * Improves API consistency, allowing a CORS resource of '*' * Improves documentation of the CORS app extension * Fixes test import errors on Python 3.4.1 (Thanks @wking ) ## 1.8.1 Thanks to @wking's work in PR https://github.com/wcdolphin/flask-cors/pull/71 `python setup.py test` will now work. ## v1.8.0 Adds support for regular expressions in the list of origins. This allows subdomain wildcarding and should be fully backwards compatible. Credit to @marcoqu for opening https://github.com/wcdolphin/flask-cors/issues/54 which inspired this work ## Earlier Prior version numbers were not kept track of in this system. python-flask-cors-5.0.0/LICENSE000066400000000000000000000020551466443373300161230ustar00rootroot00000000000000Copyright (C) 2016 Cory Dolphin, Olin College 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.python-flask-cors-5.0.0/MANIFEST.in000066400000000000000000000002721466443373300166530ustar00rootroot00000000000000include MANIFEST.in include README.rst include requirements.txt include LICENSE recursive-include docs * recursive-include examples *.py recursive-include tests *.py prune docs/_build python-flask-cors-5.0.0/README.rst000066400000000000000000000104761466443373300166130ustar00rootroot00000000000000Flask-CORS ========== |Build Status| |Latest Version| |Supported Python versions| |License| A Flask extension for handling Cross Origin Resource Sharing (CORS), making cross-origin AJAX possible. This package has a simple philosophy: when you want to enable CORS, you wish to enable it for all use cases on a domain. This means no mucking around with different allowed headers, methods, etc. By default, submission of cookies across domains is disabled due to the security implications. Please see the documentation for how to enable credential'ed requests, and please make sure you add some sort of `CSRF `__ protection before doing so! Installation ------------ Install the extension with using pip, or easy\_install. .. code:: bash $ pip install -U flask-cors Usage ----- This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods. It allows parameterization of all CORS headers on a per-resource level. The package also contains a decorator, for those who prefer this approach. Simple Usage ~~~~~~~~~~~~ In the simplest case, initialize the Flask-Cors extension with default arguments in order to allow CORS for all domains on all routes. See the full list of options in the `documentation `__. .. code:: python from flask import Flask from flask_cors import CORS app = Flask(__name__) CORS(app) @app.route("/") def helloWorld(): return "Hello, cross-origin-world!" Resource specific CORS ^^^^^^^^^^^^^^^^^^^^^^ Alternatively, you can specify CORS options on a resource and origin level of granularity by passing a dictionary as the `resources` option, mapping paths to a set of options. See the full list of options in the `documentation `__. .. code:: python app = Flask(__name__) cors = CORS(app, resources={r"/api/*": {"origins": "*"}}) @app.route("/api/v1/users") def list_users(): return "user example" Route specific CORS via decorator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This extension also exposes a simple decorator to decorate flask routes with. Simply add ``@cross_origin()`` below a call to Flask's ``@app.route(..)`` to allow CORS on a given route. See the full list of options in the `decorator documentation `__. .. code:: python @app.route("/") @cross_origin() def helloWorld(): return "Hello, cross-origin-world!" Documentation ------------- For a full list of options, please see the full `documentation `__ Troubleshooting --------------- If things aren't working as you expect, enable logging to help understand what is going on under the hood, and why. .. code:: python logging.getLogger('flask_cors').level = logging.DEBUG Tests ----- A simple set of tests is included in ``test/``. To run, install nose, and simply invoke ``nosetests`` or ``python setup.py test`` to exercise the tests. If nosetests does not work for you, due to it no longer working with newer python versions. You can use pytest to run the tests instead. Contributing ------------ Questions, comments or improvements? Please create an issue on `Github `__, tweet at `@corydolphin `__ or send me an email. I do my best to include every contribution proposed in any way that I can. Credits ------- This Flask extension is based upon the `Decorator for the HTTP Access Control `__ written by Armin Ronacher. .. |Build Status| image:: https://github.com/corydolphin/flask-cors/actions/workflows/unittests.yaml/badge.svg :target: https://travis-ci.org/corydolphin/flask-cors .. |Latest Version| image:: https://img.shields.io/pypi/v/Flask-Cors.svg :target: https://pypi.python.org/pypi/Flask-Cors/ .. |Supported Python versions| image:: https://img.shields.io/pypi/pyversions/Flask-Cors.svg :target: https://img.shields.io/pypi/pyversions/Flask-Cors.svg .. |License| image:: http://img.shields.io/:license-mit-blue.svg :target: https://pypi.python.org/pypi/Flask-Cors/ python-flask-cors-5.0.0/docs/000077500000000000000000000000001466443373300160445ustar00rootroot00000000000000python-flask-cors-5.0.0/docs/Makefile000066400000000000000000000127141466443373300175110ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Flask-Cors.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Flask-Cors.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/Flask-Cors" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Flask-Cors" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." python-flask-cors-5.0.0/docs/api.rst000066400000000000000000000047561466443373300173630ustar00rootroot00000000000000API Docs ========== This package exposes a Flask extension which by default enables CORS support on all routes, for all origins and methods. It allows parameterization of all CORS headers on a per-resource level. The package also contains a decorator, for those who prefer this approach. Extension ~~~~~~~~~ This is the suggested approach to enabling CORS. The default configuration will work well for most use cases. .. autoclass:: flask_cors.CORS Decorator ~~~~~~~~~ If the `CORS` extension does not satisfy your needs, you may find the decorator useful. It shares options with the extension, and should be simple to use. .. autofunction:: flask_cors.cross_origin Using `CORS` with cookies ~~~~~~~~~~~~~~~~~~~~~~~~~ By default, Flask-CORS does not allow cookies to be submitted across sites, since it has potential security implications. If you wish to enable cross-site cookies, you may wish to add some sort of `CSRF `__ protection to keep you and your users safe. To allow cookies or authenticated requests to be made cross origins, simply set the `supports_credentials` option to `True`. E.g. .. code:: python from flask import Flask, session from flask_cors import CORS app = Flask(__name__) CORS(app, supports_credentials=True) @app.route("/") def helloWorld(): return "Hello, %s" % session['username'] The above code enables Flask backend to accept cookies to be submitted from cross origin sites. But if you are sending Xhr requests (ajax calls) to a cross-origin server, by default chrome or any modern browser won't send cookies and session with the request. You should use ``withCredentials = True`` while sending Xhr request to enable that. You should keep in mind about the necessary security concerns. Related MDN doc: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/withCredentials Using `CORS` with Blueprints ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Flask-CORS supports blueprints out of the box. Simply pass a `blueprint` instance to the CORS extension, and everything will just work. .. literalinclude:: ../examples/blueprints_based_example.py :language: python :lines: 23- Examples ~~~~~~~~~ Using the `CORS` extension ^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../examples/app_based_example.py :language: python :lines: 29- Using the `cross_origin` decorator ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../examples/view_based_example.py :language: python :lines: 27- python-flask-cors-5.0.0/docs/conf.py000066400000000000000000000201041466443373300173400ustar00rootroot00000000000000# -*- coding: utf-8 -*- # # Flask-CORS documentation build configuration file, created by # sphinx-quickstart on Thu Aug 6 00:14:19 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('..')) import flask_cors # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinxcontrib.httpdomain'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Flask-Cors' copyright = u'2013, Cory Dolphin' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. version = flask_cors.__version__ release = version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". #html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Flask-Corsdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'Flask-Cors.tex', u'Flask-Cors Documentation', u'Cory Dolphin', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'flask-cors', u'Flask-Cors Documentation', [u'Cory Dolphin'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'Flask-Cors', u'Flask-Cors Documentation', u'Cory Dolphin', 'Flask-Cors', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'py': ('http://docs.python.org/', None)} python-flask-cors-5.0.0/docs/configuration.rst000066400000000000000000000151451466443373300214530ustar00rootroot00000000000000Configuration ============= Flask-CORS can be configured at four different locations. Configuration values are determined in the following order: #. Resource level settings (e.g when passed as a dictionary) #. Keyword argument settings #. App level configuration settings (e.g. CORS_*) #. :ref:`Default settings ` See :ref:`below ` for more information. Configuration options ~~~~~~~~~~~~~~~~~~~~~ Configuration options are consistently named across the various :ref:`locations ` where they can be set. A configuration option called *example* can be set with the resource dictionary key *example*, as the keyword argument *example* or as the Flask app configuration key *CORS_EXAMPLE*. The configuration options recognised by Flask-CORS are: CORS_ALLOW_HEADERS (:py:class:`~typing.List` or :py:class:`str`) Headers to accept from the client. Headers in the :http:header:`Access-Control-Request-Headers` request header (usually part of the preflight OPTIONS request) matching headers in this list will be included in the :http:header:`Access-Control-Allow-Headers` response header. CORS_ALLOW_PRIVATE_NETWORK (:py:class:`bool`) If True, the response header :http:header:`Access-Control-Allow-Private-Network` will be set with the value 'true' whenever the request header :http:header:`Access-Control-Request-Private-Network` has a value 'true'. If False, the reponse header :http:header:`Access-Control-Allow-Private-Network` will be set with the value 'false' whenever the request header :http:header:`Access-Control-Request-Private-Network` has a value of 'true'. If the request header :http:header:`Access-Control-Request-Private-Network` is not present or has a value other than 'true', the response header :http:header:`Access-Control-Allow-Private-Network` will not be set. CORS_ALWAYS_SEND (:py:class:`bool`) Usually, if a request doesn't include an :http:header:`Origin` header, the client did not request CORS. This means we can ignore this request. However, if this is true, a most-likely-to-be-correct value is still set. CORS_AUTOMATIC_OPTIONS (:py:class:`bool`) Only applies to the :py:meth:`flask_cors.cross_origin` decorator. If True, Flask-CORS will override Flask’s default OPTIONS handling to return CORS headers for OPTIONS requests. CORS_EXPOSE_HEADERS (:py:class:`~typing.List` or :py:class:`str`) The CORS spec requires the server to give explicit permissions for the client to read headers in CORS responses (via the :http:header:`Access-Control-Expose-Headers` header). This specifies the headers to include in this header. CORS_INTERCEPT_EXCEPTIONS (:py:class:`bool`) Whether to deal with Flask exception handlers or leave them alone (with respect to CORS headers). CORS_MAX_AGE (:py:class:`~datetime.timedelta`, :py:class:`int` or :py:class:`str`) The maximum time for which this CORS request may be cached. This value is set as the :http:header:`Access-Control-Max-Age` header. CORS_METHODS (:py:class:`~typing.List` or :py:class:`str`) The method(s) which the allowed origins are allowed to access. These are included in the :http:header:`Access-Control-Allow-Methods` response headers to the preflight OPTIONS requests. .. _cors_origins_setting: CORS_ORIGINS (:py:class:`~typing.List`, :py:class:`str` or :py:class:`re.Pattern`) The origin(s) to allow requests from. An origin configured here that matches the value of the :http:header:`Origin` header in a preflight OPTIONS request is returned as the value of the :http:header:`Access-Control-Allow-Origin` response header. CORS_RESOURCES (:py:class:`~typing.Dict`, :py:class:`~typing.List` or :py:class:`str`) The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. If the value is a dictionary, it's keys must be regular expressions matching resources, and the values must be another dictionary of configuration options, as described in this section. If the argument is a list, it is expected to be a list of regular expressions matching resources for which the app-wide configured options are applied. If the argument is a string, it is expected to be a regular expression matching resources for which the app-wide configured options are applied. CORS_SEND_WILDCARD (:py:class:`bool`) If :ref:`CORS_ORIGINS ` is ``"*"`` and this is true, then the :http:header:`Access-Control-Allow-Origin` response header's value with be ``"*"`` as well, instead of the value of the :http:header:`Origin` request header. CORS_SUPPORTS_CREDENTIALS (:py:class:`bool`) Allows users to make authenticated requests. If true, injects the :http:header:`Access-Control-Allow-Credentials` header in responses. This allows cookies and credentials to be submitted across domains. :note: This option cannot be used in conjunction with a "*" origin CORS_VARY_HEADER: (:py:class:`bool`) Enables or disables the injection of the :http:header:`Vary` response header is set to ``Origin``. This informs clients that our CORS headers are dynamic and cannot be cached. .. _cors_default_values: Default values ~~~~~~~~~~~~~~ * CORS_ALLOW_HEADERS: "*" * CORS_ALLOW_PRIVATE_NETWORK: False * CORS_ALWAYS_SEND: True * CORS_AUTOMATIC_OPTIONS: True * CORS_EXPOSE_HEADERS: None * CORS_INTERCEPT_EXCEPTIONS: True * CORS_MAX_AGE: None * CORS_METHODS: [":http:method:`get`", ":http:method:`head`", ":http:method:`post`", ":http:method:`options`", ":http:method:`put`", ":http:method:`patch`", ":http:method:`delete`"] * CORS_ORIGINS: "*" * CORS_RESOURCES: r"/\*" * CORS_SEND_WILDCARD: False * CORS_SUPPORTS_CREDENTIALS: False * CORS_VARY_HEADER: True .. _locations: Locations ~~~~~~~~~ Resource level settings ^^^^^^^^^^^^^^^^^^^^^^^ You can specify CORS options on a resource level of granularity by passing a dictionary as the *resources* keyword argument when instantiating the :py:class:`flask_cors.CORS` object (or when calling ``init_app`` on it), mapping paths to a set of options. Keyword argument settings ^^^^^^^^^^^^^^^^^^^^^^^^^ For options matching all resources, it's also possible to simply set the configuration options using keyword arguments when instantiating the :py:class:`flask_cors.CORS` object (or when calling ``init_app`` on it). App level configuration settings ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ It's good practice to keep your application configuration settings in one place. This is also possible with Flask-CORS using the same configuration options in the Flask application's config object. Default settings ^^^^^^^^^^^^^^^^ Finally, every setting has a :ref:`default value ` as well. python-flask-cors-5.0.0/docs/index.rst000066400000000000000000000001531466443373300177040ustar00rootroot00000000000000.. include:: ../README.rst .. toctree:: :hidden: :name: mastertoc self configuration api python-flask-cors-5.0.0/docs/requirements.txt000066400000000000000000000025361466443373300213360ustar00rootroot00000000000000alabaster==0.7.13 ; python_version >= "3.8" babel==2.14.0 ; python_version >= "3.8" certifi==2024.2.2 ; python_version >= "3.8" charset-normalizer==3.3.2 ; python_version >= "3.8" colorama==0.4.6 ; python_version >= "3.8" and sys_platform == "win32" docutils==0.20.1 ; python_version >= "3.8" idna==3.6 ; python_version >= "3.8" imagesize==1.4.1 ; python_version >= "3.8" importlib-metadata==7.0.1 ; python_version < "3.10" and python_version >= "3.8" jinja2==3.1.3 ; python_version >= "3.8" markupsafe==2.1.5 ; python_version >= "3.8" packaging==23.2 ; python_version >= "3.8" pygments==2.17.2 ; python_version >= "3.8" pytz==2024.1 ; python_version < "3.9" and python_version >= "3.8" requests==2.32.0 ; python_version >= "3.8" snowballstemmer==2.2.0 ; python_version >= "3.8" sphinx==7.1.2 ; python_version >= "3.8" sphinx-rtd-theme==2.0.0 ; python_version >= "3.8" sphinxcontrib-applehelp==1.0.4 ; python_version >= "3.8" sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.8" sphinxcontrib-htmlhelp==2.0.1 ; python_version >= "3.8" sphinxcontrib-httpdomain==1.8.1 ; python_version >= "3.8" sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.8" sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.8" sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.8" urllib3==2.2.1 ; python_version >= "3.8" zipp==3.17.0 ; python_version < "3.10" and python_version >= "3.8" python-flask-cors-5.0.0/examples/000077500000000000000000000000001466443373300167325ustar00rootroot00000000000000python-flask-cors-5.0.0/examples/app_based_example.py000066400000000000000000000114711466443373300227410ustar00rootroot00000000000000""" Flask-Cors example =================== This is a tiny Flask Application demonstrating Flask-Cors, making it simple to add cross origin support to your flask app! :copyright: (c) 2016 by Cory Dolphin. :license: MIT/X11, see LICENSE for more details. """ from flask import Flask, jsonify import logging try: from flask_cors import CORS # The typical way to import flask-cors except ImportError: # Path hack allows examples to be run without installation. import os parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) os.sys.path.insert(0, parentdir) from flask_cors import CORS app = Flask('FlaskCorsAppBasedExample') logging.basicConfig(level=logging.INFO) # To enable logging for flask-cors, logging.getLogger('flask_cors').level = logging.DEBUG # One of the simplest configurations. Exposes all resources matching /api/* to # CORS and allows the Content-Type header, which is necessary to POST JSON # cross origin. CORS(app, resources=r'/api/*') @app.route("/") def helloWorld(): """ Since the path '/' does not match the regular expression r'/api/*', this route does not have CORS headers set. """ return '''

Hello CORS!

End to end editable example with jquery!

JS Bin on jsbin.com ''' @app.route("/api/v1/users/") def list_users(): """ Since the path matches the regular expression r'/api/*', this resource automatically has CORS headers set. The expected result is as follows: $ curl --include -X GET http://127.0.0.1:5000/api/v1/users/ \ --header Origin:www.examplesite.com HTTP/1.0 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Origin: * Content-Length: 21 Content-Type: application/json Date: Sat, 09 Aug 2014 00:26:41 GMT Server: Werkzeug/0.9.4 Python/2.7.8 { "success": true } """ return jsonify(user="joe") @app.route("/api/v1/users/create", methods=['POST']) def create_user(): """ Since the path matches the regular expression r'/api/*', this resource automatically has CORS headers set. Browsers will first make a preflight request to verify that the resource allows cross-origin POSTs with a JSON Content-Type, which can be simulated as: $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \ --header Access-Control-Request-Method:POST \ --header Access-Control-Request-Headers:Content-Type \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Allow: POST, OPTIONS Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT Content-Length: 0 Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:22 GMT $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \ --header Content-Type:application/json \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: application/json Content-Length: 21 Access-Control-Allow-Origin: * Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:04 GMT { "success": true } """ return jsonify(success=True) @app.route("/api/exception") def get_exception(): """ Since the path matches the regular expression r'/api/*', this resource automatically has CORS headers set. Browsers will first make a preflight request to verify that the resource allows cross-origin POSTs with a JSON Content-Type, which can be simulated as: $ curl --include -X OPTIONS http://127.0.0.1:5000/api/exception \ --header Access-Control-Request-Method:POST \ --header Access-Control-Request-Headers:Content-Type \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Allow: POST, OPTIONS Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT Content-Length: 0 Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:22 GMT """ raise Exception("example") @app.errorhandler(500) def server_error(e): logging.exception('An error occurred during a request. %s', e) return "An internal error occurred", 500 if __name__ == "__main__": app.run(debug=True) python-flask-cors-5.0.0/examples/blueprints_based_example.py000066400000000000000000000070131466443373300243450ustar00rootroot00000000000000""" Flask-Cors example =================== This is a tiny Flask Application demonstrating Flask-Cors, making it simple to add cross origin support to your flask app! :copyright: (c) 2016 by Cory Dolphin. :license: MIT/X11, see LICENSE for more details. """ from flask import Flask, jsonify, Blueprint import logging try: from flask_cors import CORS # The typical way to import flask-cors except ImportError: # Path hack allows examples to be run without installation. import os parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) os.sys.path.insert(0, parentdir) from flask_cors import CORS api_v1 = Blueprint('API_v1', __name__) CORS(api_v1) # enable CORS on the API_v1 blue print @api_v1.route("/api/v1/users/") def list_users(): ''' Since the path matches the regular expression r'/api/*', this resource automatically has CORS headers set. The expected result is as follows: $ curl --include -X GET http://127.0.0.1:5000/api/v1/users/ \ --header Origin:www.examplesite.com HTTP/1.0 200 OK Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Origin: * Content-Length: 21 Content-Type: application/json Date: Sat, 09 Aug 2014 00:26:41 GMT Server: Werkzeug/0.9.4 Python/2.7.8 { "success": true } ''' return jsonify(user="joe") @api_v1.route("/api/v1/users/create", methods=['POST']) def create_user(): ''' Since the path matches the regular expression r'/api/*', this resource automatically has CORS headers set. Browsers will first make a preflight request to verify that the resource allows cross-origin POSTs with a JSON Content-Type, which can be simulated as: $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \ --header Access-Control-Request-Method:POST \ --header Access-Control-Request-Headers:Content-Type \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Allow: POST, OPTIONS Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT Content-Length: 0 Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:22 GMT $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \ --header Content-Type:application/json \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: application/json Content-Length: 21 Access-Control-Allow-Origin: * Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:04 GMT { "success": true } ''' return jsonify(success=True) public_routes = Blueprint('public', __name__) @public_routes.route("/") def helloWorld(): ''' Since the path '/' does not match the regular expression r'/api/*', this route does not have CORS headers set. ''' return '''

Hello CORS!

Read about my spec at the W3 Or, checkout my documentation on Github''' logging.basicConfig(level=logging.INFO) app = Flask('FlaskCorsBlueprintBasedExample') app.register_blueprint(api_v1) app.register_blueprint(public_routes) if __name__ == "__main__": app.run(debug=True) python-flask-cors-5.0.0/examples/view_based_example.py000066400000000000000000000065131466443373300231340ustar00rootroot00000000000000""" Flask-Cors example =================== This is a tiny Flask Application demonstrating Flask-Cors, making it simple to add cross origin support to your flask app! :copyright: (c) 2016 by Cory Dolphin. :license: MIT/X11, see LICENSE for more details. """ from flask import Flask, jsonify import logging try: # The typical way to import flask-cors from flask_cors import cross_origin except ImportError: # Path hack allows examples to be run without installation. import os parentdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) os.sys.path.insert(0, parentdir) from flask_cors import cross_origin app = Flask('FlaskCorsViewBasedExample') logging.basicConfig(level=logging.INFO) @app.route("/", methods=['GET']) @cross_origin() def helloWorld(): ''' This view has CORS enabled for all domains, representing the simplest configuration of view-based decoration. The expected result is as follows: $ curl --include -X GET http://127.0.0.1:5000/ \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 184 Access-Control-Allow-Origin: * Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:29:56 GMT

Hello CORS!

Read about my spec at the W3 Or, checkout my documentation on Github ''' return '''

Hello CORS!

Read about my spec at the W3 Or, checkout my documentation on Github''' @app.route("/api/v1/users/create", methods=['GET', 'POST']) @cross_origin(allow_headers=['Content-Type']) def cross_origin_json_post(): ''' This view has CORS enabled for all domains, and allows browsers to send the Content-Type header, allowing cross domain AJAX POST requests. Browsers will first make a preflight request to verify that the resource allows cross-origin POSTs with a JSON Content-Type, which can be simulated as: $ curl --include -X OPTIONS http://127.0.0.1:5000/api/v1/users/create \ --header Access-Control-Request-Method:POST \ --header Access-Control-Request-Headers:Content-Type \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Allow: POST, OPTIONS Access-Control-Allow-Origin: * Access-Control-Allow-Headers: Content-Type Access-Control-Allow-Methods: DELETE, GET, HEAD, OPTIONS, PATCH, POST, PUT Content-Length: 0 Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:22 GMT $ curl --include -X POST http://127.0.0.1:5000/api/v1/users/create \ --header Content-Type:application/json \ --header Origin:www.examplesite.com >> HTTP/1.0 200 OK Content-Type: application/json Content-Length: 21 Access-Control-Allow-Origin: * Server: Werkzeug/0.9.6 Python/2.7.9 Date: Sat, 31 Jan 2015 22:25:04 GMT { "success": true } ''' return jsonify(success=True) if __name__ == "__main__": app.run(debug=True) python-flask-cors-5.0.0/flask_cors/000077500000000000000000000000001466443373300172425ustar00rootroot00000000000000python-flask-cors-5.0.0/flask_cors/__init__.py000066400000000000000000000014301466443373300213510ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ flask_cors ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from .decorator import cross_origin from .extension import CORS from .version import __version__ __all__ = ['CORS', 'cross_origin'] # Set default logging handler to avoid "No handler found" warnings. import logging from logging import NullHandler # Set initial level to WARN. Users must manually enable logging for # flask_cors to see our logging. rootlogger = logging.getLogger(__name__) rootlogger.addHandler(NullHandler()) if rootlogger.level == logging.NOTSET: rootlogger.setLevel(logging.WARN) python-flask-cors-5.0.0/flask_cors/core.py000066400000000000000000000336541466443373300205570ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ core ~~~~ Core functionality shared between the extension and the decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import re import logging from collections.abc import Iterable from datetime import timedelta from flask import request, current_app from werkzeug.datastructures import Headers, MultiDict LOG = logging.getLogger(__name__) # Response Headers ACL_ORIGIN = 'Access-Control-Allow-Origin' ACL_METHODS = 'Access-Control-Allow-Methods' ACL_ALLOW_HEADERS = 'Access-Control-Allow-Headers' ACL_EXPOSE_HEADERS = 'Access-Control-Expose-Headers' ACL_CREDENTIALS = 'Access-Control-Allow-Credentials' ACL_MAX_AGE = 'Access-Control-Max-Age' ACL_RESPONSE_PRIVATE_NETWORK = 'Access-Control-Allow-Private-Network' # Request Header ACL_REQUEST_METHOD = 'Access-Control-Request-Method' ACL_REQUEST_HEADERS = 'Access-Control-Request-Headers' ACL_REQUEST_HEADER_PRIVATE_NETWORK = 'Access-Control-Request-Private-Network' ALL_METHODS = ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE'] CONFIG_OPTIONS = ['CORS_ORIGINS', 'CORS_METHODS', 'CORS_ALLOW_HEADERS', 'CORS_EXPOSE_HEADERS', 'CORS_SUPPORTS_CREDENTIALS', 'CORS_MAX_AGE', 'CORS_SEND_WILDCARD', 'CORS_AUTOMATIC_OPTIONS', 'CORS_VARY_HEADER', 'CORS_RESOURCES', 'CORS_INTERCEPT_EXCEPTIONS', 'CORS_ALWAYS_SEND', 'CORS_ALLOW_PRIVATE_NETWORK'] # Attribute added to request object by decorator to indicate that CORS # was evaluated, in case the decorator and extension are both applied # to a view. FLASK_CORS_EVALUATED = '_FLASK_CORS_EVALUATED' # Strange, but this gets the type of a compiled regex, which is otherwise not # exposed in a public API. RegexObject = type(re.compile('')) DEFAULT_OPTIONS = dict(origins='*', methods=ALL_METHODS, allow_headers='*', expose_headers=None, supports_credentials=False, max_age=None, send_wildcard=False, automatic_options=True, vary_header=True, resources=r'/*', intercept_exceptions=True, always_send=True, allow_private_network=False) def parse_resources(resources): if isinstance(resources, dict): # To make the API more consistent with the decorator, allow a # resource of '*', which is not actually a valid regexp. resources = [(re_fix(k), v) for k, v in resources.items()] # Sort by regex length to provide consistency of matching and # to provide a proxy for specificity of match. E.G. longer # regular expressions are tried first. def pattern_length(pair): maybe_regex, _ = pair return len(get_regexp_pattern(maybe_regex)) return sorted(resources, key=pattern_length, reverse=True) elif isinstance(resources, str): return [(re_fix(resources), {})] elif isinstance(resources, Iterable): return [(re_fix(r), {}) for r in resources] # Type of compiled regex is not part of the public API. Test for this # at runtime. elif isinstance(resources, RegexObject): return [(re_fix(resources), {})] else: raise ValueError("Unexpected value for resources argument.") def get_regexp_pattern(regexp): """ Helper that returns regexp pattern from given value. :param regexp: regular expression to stringify :type regexp: _sre.SRE_Pattern or str :returns: string representation of given regexp pattern :rtype: str """ try: return regexp.pattern except AttributeError: return str(regexp) def get_cors_origins(options, request_origin): origins = options.get('origins') wildcard = r'.*' in origins # If the Origin header is not present terminate this set of steps. # The request is outside the scope of this specification.-- W3Spec if request_origin: LOG.debug("CORS request received with 'Origin' %s", request_origin) # If the allowed origins is an asterisk or 'wildcard', always match if wildcard and options.get('send_wildcard'): LOG.debug("Allowed origins are set to '*'. Sending wildcard CORS header.") return ['*'] # If the value of the Origin header is a case-sensitive match # for any of the values in list of origins elif try_match_any(request_origin, origins): LOG.debug("The request's Origin header matches. Sending CORS headers.", ) # Add a single Access-Control-Allow-Origin header, with either # the value of the Origin header or the string "*" as value. # -- W3Spec return [request_origin] else: LOG.debug("The request's Origin header does not match any of allowed origins.") return None elif options.get('always_send'): if wildcard: # If wildcard is in the origins, even if 'send_wildcard' is False, # simply send the wildcard. Unless supports_credentials is True, # since that is forbidded by the spec.. # It is the most-likely to be correct thing to do (the only other # option is to return nothing, which almost certainly not what # the developer wants if the '*' origin was specified. if options.get('supports_credentials'): return None else: return ['*'] else: # Return all origins that are not regexes. return sorted([o for o in origins if not probably_regex(o)]) # Terminate these steps, return the original request untouched. else: LOG.debug("The request did not contain an 'Origin' header. This means the browser or client did not request CORS, ensure the Origin Header is set.") return None def get_allow_headers(options, acl_request_headers): if acl_request_headers: request_headers = [h.strip() for h in acl_request_headers.split(',')] # any header that matches in the allow_headers matching_headers = filter( lambda h: try_match_any(h, options.get('allow_headers')), request_headers ) return ', '.join(sorted(matching_headers)) return None def get_cors_headers(options, request_headers, request_method): origins_to_set = get_cors_origins(options, request_headers.get('Origin')) headers = MultiDict() if not origins_to_set: # CORS is not enabled for this route return headers for origin in origins_to_set: headers.add(ACL_ORIGIN, origin) headers[ACL_EXPOSE_HEADERS] = options.get('expose_headers') if options.get('supports_credentials'): headers[ACL_CREDENTIALS] = 'true' # case sensitive if ACL_REQUEST_HEADER_PRIVATE_NETWORK in request_headers \ and request_headers.get(ACL_REQUEST_HEADER_PRIVATE_NETWORK) == 'true': allow_private_network = 'true' if options.get('allow_private_network') else 'false' headers[ACL_RESPONSE_PRIVATE_NETWORK] = allow_private_network # This is a preflight request # http://www.w3.org/TR/cors/#resource-preflight-requests if request_method == 'OPTIONS': acl_request_method = request_headers.get(ACL_REQUEST_METHOD, '').upper() # If there is no Access-Control-Request-Method header or if parsing # failed, do not set any additional headers if acl_request_method and acl_request_method in options.get('methods'): # If method is not a case-sensitive match for any of the values in # list of methods do not set any additional headers and terminate # this set of steps. headers[ACL_ALLOW_HEADERS] = get_allow_headers(options, request_headers.get(ACL_REQUEST_HEADERS)) headers[ACL_MAX_AGE] = options.get('max_age') headers[ACL_METHODS] = options.get('methods') else: LOG.info("The request's Access-Control-Request-Method header does not match allowed methods. CORS headers will not be applied.") # http://www.w3.org/TR/cors/#resource-implementation if options.get('vary_header'): # Only set header if the origin returned will vary dynamically, # i.e. if we are not returning an asterisk, and there are multiple # origins that can be matched. if headers[ACL_ORIGIN] == '*': pass elif (len(options.get('origins')) > 1 or len(origins_to_set) > 1 or any(map(probably_regex, options.get('origins')))): headers.add('Vary', 'Origin') return MultiDict((k, v) for k, v in headers.items() if v) def set_cors_headers(resp, options): """ Performs the actual evaluation of Flask-CORS options and actually modifies the response object. This function is used both in the decorator and the after_request callback """ # If CORS has already been evaluated via the decorator, skip if hasattr(resp, FLASK_CORS_EVALUATED): LOG.debug('CORS have been already evaluated, skipping') return resp # Some libraries, like OAuthlib, set resp.headers to non Multidict # objects (Werkzeug Headers work as well). This is a problem because # headers allow repeated values. if (not isinstance(resp.headers, Headers) and not isinstance(resp.headers, MultiDict)): resp.headers = MultiDict(resp.headers) headers_to_set = get_cors_headers(options, request.headers, request.method) LOG.debug('Settings CORS headers: %s', str(headers_to_set)) for k, v in headers_to_set.items(): resp.headers.add(k, v) return resp def probably_regex(maybe_regex): if isinstance(maybe_regex, RegexObject): return True else: common_regex_chars = ['*', '\\', ']', '?', '$', '^', '[', ']', '(', ')'] # Use common characters used in regular expressions as a proxy # for if this string is in fact a regex. return any((c in maybe_regex for c in common_regex_chars)) def re_fix(reg): """ Replace the invalid regex r'*' with the valid, wildcard regex r'/.*' to enable the CORS app extension to have a more user friendly api. """ return r'.*' if reg == r'*' else reg def try_match_any(inst, patterns): return any(try_match(inst, pattern) for pattern in patterns) def try_match(request_origin, maybe_regex): """Safely attempts to match a pattern or string to a request origin.""" if isinstance(maybe_regex, RegexObject): return re.match(maybe_regex, request_origin) elif probably_regex(maybe_regex): return re.match(maybe_regex, request_origin, flags=re.IGNORECASE) else: try: return request_origin.lower() == maybe_regex.lower() except AttributeError: return request_origin == maybe_regex def get_cors_options(appInstance, *dicts): """ Compute CORS options for an application by combining the DEFAULT_OPTIONS, the app's configuration-specified options and any dictionaries passed. The last specified option wins. """ options = DEFAULT_OPTIONS.copy() options.update(get_app_kwarg_dict(appInstance)) if dicts: for d in dicts: options.update(d) return serialize_options(options) def get_app_kwarg_dict(appInstance=None): """Returns the dictionary of CORS specific app configurations.""" app = (appInstance or current_app) # In order to support blueprints which do not have a config attribute app_config = getattr(app, 'config', {}) return { k.lower().replace('cors_', ''): app_config.get(k) for k in CONFIG_OPTIONS if app_config.get(k) is not None } def flexible_str(obj): """ A more flexible str function which intelligently handles stringifying strings, lists and other iterables. The results are lexographically sorted to ensure generated responses are consistent when iterables such as Set are used. """ if obj is None: return None elif not isinstance(obj, str) and isinstance(obj, Iterable): return ", ".join(str(item) for item in sorted(obj)) else: return str(obj) def serialize_option(options_dict, key, upper=False): if key in options_dict: value = flexible_str(options_dict[key]) options_dict[key] = value.upper() if upper else value def ensure_iterable(inst): """ Wraps scalars or string types as a list, or returns the iterable instance. """ if isinstance(inst, str): return [inst] elif not isinstance(inst, Iterable): return [inst] else: return inst def sanitize_regex_param(param): return [re_fix(x) for x in ensure_iterable(param)] def serialize_options(opts): """ A helper method to serialize and processes the options dictionary. """ options = (opts or {}).copy() for key in opts.keys(): if key not in DEFAULT_OPTIONS: LOG.warning("Unknown option passed to Flask-CORS: %s", key) # Ensure origins is a list of allowed origins with at least one entry. options['origins'] = sanitize_regex_param(options.get('origins')) options['allow_headers'] = sanitize_regex_param(options.get('allow_headers')) # This is expressly forbidden by the spec. Raise a value error so people # don't get burned in production. if r'.*' in options['origins'] and options['supports_credentials'] and options['send_wildcard']: raise ValueError("Cannot use supports_credentials in conjunction with" "an origin string of '*'. See: " "http://www.w3.org/TR/cors/#resource-requests") serialize_option(options, 'expose_headers') serialize_option(options, 'methods', upper=True) if isinstance(options.get('max_age'), timedelta): options['max_age'] = str(int(options['max_age'].total_seconds())) return options python-flask-cors-5.0.0/flask_cors/decorator.py000066400000000000000000000116211466443373300215770ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ decorator ~~~~ This unit exposes a single decorator which should be used to wrap a Flask route with. It accepts all parameters and options as the CORS extension. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import logging from functools import update_wrapper from flask import make_response, request, current_app from .core import get_cors_options, set_cors_headers, FLASK_CORS_EVALUATED LOG = logging.getLogger(__name__) def cross_origin(*args, **kwargs): """ This function is the decorator which is used to wrap a Flask route with. In the simplest case, simply use the default parameters to allow all origins in what is the most permissive configuration. If this method modifies state or performs authentication which may be brute-forced, you should add some degree of protection, such as Cross Site Request Forgery protection. :param origins: The origin, or list of origins to allow requests from. The origin(s) may be regular expressions, case-sensitive strings, or else an asterisk Default : '*' :type origins: list, string or regex :param methods: The method or list of methods which the allowed origins are allowed to access for non-simple requests. Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE] :type methods: list or string :param expose_headers: The header or list which are safe to expose to the API of a CORS API specification. Default : None :type expose_headers: list or string :param allow_headers: The header or list of header field names which can be used when this resource is accessed by allowed origins. The header(s) may be regular expressions, case-sensitive strings, or else an asterisk. Default : '*', allow all headers :type allow_headers: list, string or regex :param supports_credentials: Allows users to make authenticated requests. If true, injects the `Access-Control-Allow-Credentials` header in responses. This allows cookies and credentials to be submitted across domains. :note: This option cannot be used in conjunction with a '*' origin Default : False :type supports_credentials: bool :param max_age: The maximum time for which this CORS request maybe cached. This value is set as the `Access-Control-Max-Age` header. Default : None :type max_age: timedelta, integer, string or None :param send_wildcard: If True, and the origins parameter is `*`, a wildcard `Access-Control-Allow-Origin` header is sent, rather than the request's `Origin` header. Default : False :type send_wildcard: bool :param vary_header: If True, the header Vary: Origin will be returned as per the W3 implementation guidelines. Setting this header when the `Access-Control-Allow-Origin` is dynamically generated (e.g. when there is more than one allowed origin, and an Origin than '*' is returned) informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached. If False, the Vary header will never be injected or altered. Default : True :type vary_header: bool :param automatic_options: Only applies to the `cross_origin` decorator. If True, Flask-CORS will override Flask's default OPTIONS handling to return CORS headers for OPTIONS requests. Default : True :type automatic_options: bool """ _options = kwargs def decorator(f): LOG.debug("Enabling %s for cross_origin using options:%s", f, _options) # If True, intercept OPTIONS requests by modifying the view function, # replicating Flask's default behavior, and wrapping the response with # CORS headers. # # If f.provide_automatic_options is unset or True, Flask's route # decorator (which is actually wraps the function object we return) # intercepts OPTIONS handling, and requests will not have CORS headers if _options.get('automatic_options', True): f.required_methods = getattr(f, 'required_methods', set()) f.required_methods.add('OPTIONS') f.provide_automatic_options = False def wrapped_function(*args, **kwargs): # Handle setting of Flask-Cors parameters options = get_cors_options(current_app, _options) if options.get('automatic_options') and request.method == 'OPTIONS': resp = current_app.make_default_options_response() else: resp = make_response(f(*args, **kwargs)) set_cors_headers(resp, options) setattr(resp, FLASK_CORS_EVALUATED, True) return resp return update_wrapper(wrapped_function, f) return decorator python-flask-cors-5.0.0/flask_cors/extension.py000066400000000000000000000206321466443373300216330ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ extension ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import logging from urllib.parse import unquote_plus from flask import request from .core import ( parse_resources, get_cors_options, get_regexp_pattern, ACL_ORIGIN, try_match, set_cors_headers ) LOG = logging.getLogger(__name__) class CORS(object): """ Initializes Cross Origin Resource sharing for the application. The arguments are identical to :py:func:`cross_origin`, with the addition of a `resources` parameter. The resources parameter defines a series of regular expressions for resource paths to match and optionally, the associated options to be applied to the particular resource. These options are identical to the arguments to :py:func:`cross_origin`. The settings for CORS are determined in the following order 1. Resource level settings (e.g when passed as a dictionary) 2. Keyword argument settings 3. App level configuration settings (e.g. CORS_*) 4. Default settings Note: as it is possible for multiple regular expressions to match a resource path, the regular expressions are first sorted by length, from longest to shortest, in order to attempt to match the most specific regular expression. This allows the definition of a number of specific resource options, with a wildcard fallback for all other resources. :param resources: The series of regular expression and (optionally) associated CORS options to be applied to the given resource path. If the argument is a dictionary, it's keys must be regular expressions, and the values must be a dictionary of kwargs, identical to the kwargs of this function. If the argument is a list, it is expected to be a list of regular expressions, for which the app-wide configured options are applied. If the argument is a string, it is expected to be a regular expression for which the app-wide configured options are applied. Default : Match all and apply app-level configuration :type resources: dict, iterable or string :param origins: The origin, or list of origins to allow requests from. The origin(s) may be regular expressions, case-sensitive strings, or else an asterisk. .. note:: origins must include the schema and the port (if not port 80), e.g., `CORS(app, origins=["http://localhost:8000", "https://example.com"])`. Default : '*' :type origins: list, string or regex :param methods: The method or list of methods which the allowed origins are allowed to access for non-simple requests. Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE] :type methods: list or string :param expose_headers: The header or list which are safe to expose to the API of a CORS API specification. Default : None :type expose_headers: list or string :param allow_headers: The header or list of header field names which can be used when this resource is accessed by allowed origins. The header(s) may be regular expressions, case-sensitive strings, or else an asterisk. Default : '*', allow all headers :type allow_headers: list, string or regex :param supports_credentials: Allows users to make authenticated requests. If true, injects the `Access-Control-Allow-Credentials` header in responses. This allows cookies and credentials to be submitted across domains. :note: This option cannot be used in conjunction with a '*' origin Default : False :type supports_credentials: bool :param max_age: The maximum time for which this CORS request maybe cached. This value is set as the `Access-Control-Max-Age` header. Default : None :type max_age: timedelta, integer, string or None :param send_wildcard: If True, and the origins parameter is `*`, a wildcard `Access-Control-Allow-Origin` header is sent, rather than the request's `Origin` header. Default : False :type send_wildcard: bool :param vary_header: If True, the header Vary: Origin will be returned as per the W3 implementation guidelines. Setting this header when the `Access-Control-Allow-Origin` is dynamically generated (e.g. when there is more than one allowed origin, and an Origin than '*' is returned) informs CDNs and other caches that the CORS headers are dynamic, and cannot be cached. If False, the Vary header will never be injected or altered. Default : True :type vary_header: bool :param allow_private_network: If True, the response header `Access-Control-Allow-Private-Network` will be set with the value 'true' whenever the request header `Access-Control-Request-Private-Network` has a value 'true'. If False, the reponse header `Access-Control-Allow-Private-Network` will be set with the value 'false' whenever the request header `Access-Control-Request-Private-Network` has a value of 'true'. If the request header `Access-Control-Request-Private-Network` is not present or has a value other than 'true', the response header `Access-Control-Allow-Private-Network` will not be set. Default : True :type allow_private_network: bool """ def __init__(self, app=None, **kwargs): self._options = kwargs if app is not None: self.init_app(app, **kwargs) def init_app(self, app, **kwargs): # The resources and options may be specified in the App Config, the CORS constructor # or the kwargs to the call to init_app. options = get_cors_options(app, self._options, kwargs) # Flatten our resources into a list of the form # (pattern_or_regexp, dictionary_of_options) resources = parse_resources(options.get('resources')) # Compute the options for each resource by combining the options from # the app's configuration, the constructor, the kwargs to init_app, and # finally the options specified in the resources dictionary. resources = [ (pattern, get_cors_options(app, options, opts)) for (pattern, opts) in resources ] # Create a human-readable form of these resources by converting the compiled # regular expressions into strings. resources_human = {get_regexp_pattern(pattern): opts for (pattern,opts) in resources} LOG.debug("Configuring CORS with resources: %s", resources_human) cors_after_request = make_after_request_function(resources) app.after_request(cors_after_request) # Wrap exception handlers with cross_origin # These error handlers will still respect the behavior of the route if options.get('intercept_exceptions', True): def _after_request_decorator(f): def wrapped_function(*args, **kwargs): return cors_after_request(app.make_response(f(*args, **kwargs))) return wrapped_function if hasattr(app, 'handle_exception'): app.handle_exception = _after_request_decorator( app.handle_exception) app.handle_user_exception = _after_request_decorator( app.handle_user_exception) def make_after_request_function(resources): def cors_after_request(resp): # If CORS headers are set in a view decorator, pass if resp.headers is not None and resp.headers.get(ACL_ORIGIN): LOG.debug('CORS have been already evaluated, skipping') return resp normalized_path = unquote_plus(request.path) for res_regex, res_options in resources: if try_match(normalized_path, res_regex): LOG.debug("Request to '%r' matches CORS resource '%s'. Using options: %s", request.path, get_regexp_pattern(res_regex), res_options) set_cors_headers(resp, res_options) break else: LOG.debug('No CORS rule matches') return resp return cors_after_request python-flask-cors-5.0.0/flask_cors/version.py000066400000000000000000000000261466443373300212770ustar00rootroot00000000000000__version__ = '5.0.0' python-flask-cors-5.0.0/requirements.txt000066400000000000000000000000131466443373300203720ustar00rootroot00000000000000Flask>=0.9 python-flask-cors-5.0.0/setup.cfg000066400000000000000000000000371466443373300167350ustar00rootroot00000000000000[bdist_wheel] universal = true python-flask-cors-5.0.0/setup.py000066400000000000000000000035051466443373300166310ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ setup ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from setuptools import setup from os.path import join, dirname with open(join(dirname(__file__), 'flask_cors/version.py'), 'r') as f: exec(f.read()) with open (join(dirname(__file__), 'requirements.txt'), 'r') as f: install_requires = f.read().split("\n") setup( name='Flask-Cors', version=__version__, url='https://github.com/corydolphin/flask-cors', license='MIT', author='Cory Dolphin', author_email='corydolphin@gmail.com', description="A Flask extension adding a decorator for CORS support", long_description=open('README.rst').read(), packages=['flask_cors'], zip_safe=False, include_package_data=True, platforms='any', install_requires=install_requires, tests_require=[ 'pytest', 'packaging' ], test_suite='tests', classifiers=[ 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ] ) python-flask-cors-5.0.0/tests/000077500000000000000000000000001466443373300162565ustar00rootroot00000000000000python-flask-cors-5.0.0/tests/__init__.py000066400000000000000000000004271466443373300203720ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ python-flask-cors-5.0.0/tests/base_test.py000066400000000000000000000055311466443373300206050ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from flask import Flask import unittest from flask_cors import * from flask_cors.core import * class FlaskCorsTestCase(unittest.TestCase): def shortDescription(self): """ Get's the one liner description to be displayed. Source: http://erikzaadi.com/2012/09/13/inheritance-within-python-unit-tests/ """ doc = self.id()[self.id().rfind('.')+1:] return "{}.{}".format(self.__class__.__name__, doc) def iter_verbs(self, c): ''' A simple helper method to iterate through a range of HTTP Verbs and return the test_client bound instance, keeping writing our tests as DRY as possible. ''' for verb in ['get', 'head', 'options']: yield getattr(c, verb) def iter_responses(self, path, verbs=['get', 'head', 'options'], **kwargs): for verb in verbs: yield self._request(verb.lower(), path, **kwargs) def _request(self, verb, *args, **kwargs): _origin = kwargs.pop('origin', None) headers = kwargs.pop('headers', {}) if _origin: headers.update(Origin=_origin) with self.app.test_client() as c: return getattr(c, verb)(*args, headers=headers, **kwargs) def get(self, *args, **kwargs): return self._request('get', *args, **kwargs) def head(self, *args, **kwargs): return self._request('head', *args, **kwargs) def post(self, *args, **kwargs): return self._request('post', *args, **kwargs) def options(self, *args, **kwargs): return self._request('options', *args, **kwargs) def put(self, *args, **kwargs): return self._request('put', *args, **kwargs) def patch(self, *args, **kwargs): return self._request('patch', *args, **kwargs) def delete(self, *args, **kwargs): return self._request('delete', *args, **kwargs) def preflight(self, path, method='GET', cors_request_headers=None, json=True, **kwargs): kwargs['headers'] = kwargs.get('headers', {}) if cors_request_headers: kwargs['headers'].update({'Access-Control-Request-Headers': ', '.join(cors_request_headers)}) if json: kwargs['headers'].update({'Content-Type':'application/json'}) kwargs['headers'].update({'Access-Control-Request-Method': method}) return self.options(path,**kwargs) def assertHasACLOrigin(self, resp, origin=None): if origin is None: self.assertTrue(ACL_ORIGIN in resp.headers) else: self.assertTrue(resp.headers.get(ACL_ORIGIN) == origin) python-flask-cors-5.0.0/tests/core/000077500000000000000000000000001466443373300172065ustar00rootroot00000000000000python-flask-cors-5.0.0/tests/core/__init__.py000066400000000000000000000004741466443373300213240ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Tests for the shared flask.ext.cores.core ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ python-flask-cors-5.0.0/tests/core/helper_tests.py000066400000000000000000000060561466443373300222700ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Tests for helper and utility methods TODO: move integration tests (e.g. all that test a full request cycle) into smaller, broken-up unit tests to simplify testing. ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import unittest from flask_cors.core import * class InternalsTestCase(unittest.TestCase): def test_try_match(self): self.assertFalse(try_match('www.com/foo', 'www.com/fo')) self.assertTrue(try_match('www.com/foo', 'www.com/fo*')) def test_flexible_str_str(self): self.assertEqual(flexible_str('Bar, Foo, Qux'), 'Bar, Foo, Qux') def test_flexible_str_set(self): self.assertEqual(flexible_str({'Foo', 'Bar', 'Qux'}), 'Bar, Foo, Qux') def test_serialize_options(self): try: serialize_options({ 'origins': r'*', 'allow_headers': True, 'supports_credentials': True, 'send_wildcard': True }) self.assertFalse(True, "A Value Error should have been raised.") except ValueError: pass def test_get_allow_headers_empty(self): options = serialize_options({'allow_headers': r'*'}) self.assertEqual(get_allow_headers(options, ''), None) self.assertEqual(get_allow_headers(options, None), None) def test_get_allow_headers_matching(self): options = serialize_options({'allow_headers': r'*'}) self.assertEqual(get_allow_headers(options, 'X-FOO'), 'X-FOO') self.assertEqual( get_allow_headers(options, 'X-Foo, X-Bar'), 'X-Bar, X-Foo' ) def test_get_allow_headers_matching_none(self): options = serialize_options({'allow_headers': r'X-FLASK-.*'}) self.assertEqual(get_allow_headers(options, 'X-FLASK-CORS'), 'X-FLASK-CORS') self.assertEqual( get_allow_headers(options, 'X-NOT-FLASK-CORS'), '' ) def test_parse_resources_sorted(self): resources = parse_resources({ '/foo': {'origins': 'http://foo.com'}, re.compile(r'/.*'): { 'origins': 'http://some-domain.com' }, re.compile(r'/api/v1/.*'): { 'origins': 'http://specific-domain.com' } }) self.assertEqual( [r[0] for r in resources], [re.compile(r'/api/v1/.*'), '/foo', re.compile(r'/.*')] ) def test_probably_regex(self): self.assertTrue(probably_regex("http://*.example.com")) self.assertTrue(probably_regex("*")) self.assertFalse(probably_regex("http://example.com")) self.assertTrue(probably_regex(r"http://[\w].example.com")) self.assertTrue(probably_regex(r"http://\w+.example.com")) self.assertTrue(probably_regex("https?://example.com")) python-flask-cors-5.0.0/tests/core/test_override_headers.py000066400000000000000000000014571466443373300241400ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-Cors tests module """ from ..base_test import FlaskCorsTestCase from flask import Flask, Response from flask_cors import * from flask_cors.core import * class ResponseHeadersOverrideTestCaseIntegration(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app) @self.app.route('/') def index(): response = Response(headers={"custom": "dictionary"}) return 'Welcome' def test_override_headers(self): ''' Ensure we work even if response.headers is set to something other than a MultiDict. ''' for resp in self.iter_responses('/'): self.assertTrue(ACL_ORIGIN in resp.headers) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/000077500000000000000000000000001466443373300202405ustar00rootroot00000000000000python-flask-cors-5.0.0/tests/decorator/__init__.py000066400000000000000000000004761466443373300223600ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Tests particular to flask_cors.cross_origin ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ python-flask-cors-5.0.0/tests/decorator/test_allow_headers.py000066400000000000000000000103431466443373300244630ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-Cors tests module """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class AllowHeadersTestCaseIntegration(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_default') @cross_origin() def test_default(): return 'Welcome!' @self.app.route('/test_allow_headers') @cross_origin(allow_headers=['X-Example-Header-B', 'X-Example-Header-A']) def test_allow_headers(): return 'Welcome!' @self.app.route('/test_allow_headers_regex') @cross_origin(allow_headers=[r'X-COMPANY-.*']) def test_allow_headers_regex(): return 'Welcome!' def test_default(self): for resp in self.iter_responses('/test_default'): self.assertTrue(resp.headers.get(ACL_ALLOW_HEADERS) is None, "Default should have no allowed headers") def test_allow_headers_no_request_headers(self): ''' No ACL_REQUEST_HEADERS sent, ACL_ALLOW_HEADERS should be empty ''' resp = self.preflight('/test_allow_headers', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), None) def test_allow_headers_with_request_headers(self): ''' If there is an Access-Control-Request-Method header in the request and Access-Control-Request-Method is allowed for cross origin requests and request method is OPTIONS, and every element in the Access-Control-Request-Headers is an allowed header, the Access-Control-Allow-Headers header should be echoed back. ''' resp = self.preflight('/test_allow_headers', origin='www.example.com', cors_request_headers=['X-Example-Header-A']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-Header-A') def test_allow_headers_with_request_headers_case_insensitive(self): ''' HTTP headers are case insensitive. We should respect that and match regardless of case, returning the casing sent by the client ''' resp = self.preflight('/test_allow_headers', origin='www.example.com', cors_request_headers=['X-Example-header-a']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-header-a') def test_allow_headers_with_unmatched_request_headers(self): ''' If every element in the Access-Control-Request-Headers is not an allowed header, then the matching headers should be returned. ''' resp = self.preflight('/test_allow_headers', origin='www.example.com', cors_request_headers=['X-Not-Found-Header']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), None) resp = self.preflight('/test_allow_headers', origin='www.example.com', cors_request_headers=['X-Example-Header-A', 'X-Not-Found-Header']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-Example-Header-A') def test_allow_headers_regex(self): ''' If every element in the Access-Control-Request-Headers is not an allowed header, then the matching headers should be returned. ''' resp = self.preflight('/test_allow_headers_regex', origin='www.example.com', cors_request_headers=['X-COMPANY-FOO']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), 'X-COMPANY-FOO') resp = self.preflight('/test_allow_headers_regex', origin='www.example.com', cors_request_headers=['X-Not-Found-Header']) self.assertEqual(resp.headers.get(ACL_ALLOW_HEADERS), None) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_credentials.py000066400000000000000000000040251466443373300241470ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class SupportsCredentialsCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_credentials_supported') @cross_origin(supports_credentials=True) def test_credentials_supported(): return 'Credentials!' @self.app.route('/test_credentials_unsupported') @cross_origin(supports_credentials=False) def test_credentials_unsupported(): return 'Credentials!' @self.app.route('/test_default') @cross_origin() def test_default(): return 'Open!' def test_credentials_supported(self): ''' The specified route should return the Access-Control-Allow-Credentials header. ''' resp = self.get('/test_credentials_supported', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_CREDENTIALS), 'true') def test_default(self): ''' The default behavior should be to disallow credentials. ''' resp = self.get('/test_default', origin='www.example.com') self.assertFalse(ACL_CREDENTIALS in resp.headers) resp = self.get('/test_default') self.assertFalse(ACL_CREDENTIALS in resp.headers) def test_credentials_unsupported(self): ''' The default behavior should be to disallow credentials. ''' resp = self.get('/test_credentials_unsupported', origin='www.example.com') self.assertFalse(ACL_CREDENTIALS in resp.headers) resp = self.get('/test_credentials_unsupported') self.assertFalse(ACL_CREDENTIALS in resp.headers) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_duplicate_headers.py000066400000000000000000000015241466443373300253200ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-Cors tests module """ from ..base_test import FlaskCorsTestCase from flask import Flask, Response from flask_cors import * from flask_cors.core import * class AllowsMultipleHeaderEntries(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_multiple_set_cookie_headers') @cross_origin() def test_multiple_set_cookie_headers(): resp = Response("Foo bar baz") resp.headers.add('set-cookie', 'foo') resp.headers.add('set-cookie', 'bar') return resp def test_multiple_set_cookie_headers(self): resp = self.get('/test_multiple_set_cookie_headers') self.assertEqual(len(resp.headers.getlist('set-cookie')), 2) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_exception_interception.py000066400000000000000000000173471466443373300264460ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import unittest import flask from packaging import version from ..base_test import FlaskCorsTestCase from flask import Flask, abort from flask_cors import * from flask_cors.core import * def add_routes(app): @app.route('/test_no_acl_abort_404') @app.route('/test_acl_abort_404') def test_acl_abort_404(): abort(404) @app.route('/test_no_acl_abort_500') @app.route('/test_acl_abort_500') def test_acl_abort_500(): abort(500) @app.route('/test_acl_uncaught_exception_500') def test_acl_uncaught_exception_500(): raise Exception("This could've been any exception") @app.route('/test_no_acl_uncaught_exception_500') def test_no_acl_uncaught_exception_500(): raise Exception("This could've been any exception") class ExceptionInterceptionDefaultTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, resources={ r'/test_acl*': {}, }) add_routes(self.app) def test_acl_abort_404(self): ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep path matches. This path matches. ''' resp = self.get('/test_acl_abort_404', origin='www.example.com') self.assertEqual(resp.status_code, 404) self.assertTrue(ACL_ORIGIN in resp.headers) def test_no_acl_abort_404(self): ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep path matches. This path does not match. ''' resp = self.get('/test_no_acl_abort_404', origin='www.example.com') self.assertEqual(resp.status_code, 404) self.assertFalse(ACL_ORIGIN in resp.headers) def test_acl_abort_500(self): ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep path matches. This path matches ''' resp = self.get('/test_acl_abort_500', origin='www.example.com') self.assertEqual(resp.status_code, 500) self.assertTrue(ACL_ORIGIN in resp.headers) def test_no_acl_abort_500(self): ''' HTTP Responses generated by calling abort are handled identically to normal responses, and should be wrapped by CORS headers if thep path matches. This path matches ''' resp = self.get('/test_no_acl_abort_500', origin='www.example.com') self.assertEqual(resp.status_code, 500) self.assertFalse(ACL_ORIGIN in resp.headers) def test_acl_uncaught_exception_500(self): ''' Uncaught exceptions will trigger Flask's internal exception handler, and should have ACL headers only if intercept_exceptions is set to True and if the request URL matches the resources pattern This url matches. ''' resp = self.get('/test_acl_uncaught_exception_500', origin='www.example.com') self.assertEqual(resp.status_code, 500) self.assertTrue(ACL_ORIGIN in resp.headers) def test_no_acl_uncaught_exception_500(self): ''' Uncaught exceptions will trigger Flask's internal exception handler, and should have ACL headers only if intercept_exceptions is set to True and if the request URL matches the resources pattern. This url does not match. ''' resp = self.get('/test_no_acl_uncaught_exception_500', origin='www.example.com') self.assertEqual(resp.status_code, 500) self.assertFalse(ACL_ORIGIN in resp.headers) def test_acl_exception_with_error_handler(self): ''' If a 500 handler is setup by the user, responses should have CORS matching rules applied, regardless of whether or not intercept_exceptions is enabled. ''' return_string = "Simple error handler" @self.app.errorhandler(404) @self.app.errorhandler(500) def catch_all_handler(error): ''' This error handler catches 404s and 500s and returns status 200 no matter what. It is not a good handler. ''' return return_string, 200 acl_paths = [ '/test_acl_abort_404', '/test_acl_abort_500', 'test_acl_uncaught_exception_500' ] no_acl_paths = [ '/test_no_acl_abort_404', '/test_no_acl_abort_500', 'test_no_acl_uncaught_exception_500' ] def get_with_origins(path): return self.get(path, origin='www.example.com') for resp in map(get_with_origins, acl_paths): self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) for resp in map(get_with_origins, no_acl_paths): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) class NoExceptionInterceptionTestCase(ExceptionInterceptionDefaultTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, intercept_exceptions=False, resources={ r'/test_acl*': {}, }) add_routes(self.app) def test_acl_exception_with_error_handler(self): ''' If a 500 handler is setup by the user, responses should have CORS matching rules applied, regardless of whether or not intercept_exceptions is enabled. ''' return_string = "Simple error handler" @self.app.errorhandler(404) @self.app.errorhandler(500) def catch_all_handler(error): ''' This error handler catches 404s and 500s and returns status 200 no matter what. It is not a good handler. ''' return return_string, 200 acl_paths = [ '/test_acl_abort_404', '/test_acl_abort_500', ] no_acl_paths = [ '/test_no_acl_abort_404', '/test_no_acl_abort_500', 'test_no_acl_uncaught_exception_500' 'test_acl_uncaught_exception_500' ] def get_with_origins(path): return self.get(path, origin='www.example.com') for resp in map(get_with_origins, acl_paths): self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) for resp in map(get_with_origins, no_acl_paths): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) @unittest.skipIf(version.parse(flask.__version__) >= version.parse("1.1"), "Flask 1.1 changed interception behavior, so after request handlers are always run. " "This obviates the need for our hacky interception") def test_acl_uncaught_exception_500(self): ''' Uncaught exceptions will trigger Flask's internal exception handler, and should have ACL headers only if intercept_exceptions is set to True. In this case it is not. ''' resp = self.get('/test_acl_uncaught_exception_500', origin='www.example.com') self.assertEqual(resp.status_code, 500) self.assertFalse(ACL_ORIGIN in resp.headers) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_expose_headers.py000066400000000000000000000024671466443373300246600ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-Cors tests module """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class ExposeHeadersTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_default') @cross_origin() def test_default(): return 'Welcome!' @self.app.route('/test_override') @cross_origin(expose_headers=["X-My-Custom-Header", "X-Another-Custom-Header"]) def test_override(): return 'Welcome!' def test_default(self): for resp in self.iter_responses('/test_default', origin='www.example.com'): self.assertTrue(resp.headers.get(ACL_EXPOSE_HEADERS) is None, "No Access-Control-Expose-Headers by default") def test_override(self): ''' The specified headers should be returned in the ACL_EXPOSE_HEADERS and correctly serialized if it is a list. ''' for resp in self.iter_responses('/test_override', origin='www.example.com'): self.assertEqual(resp.headers.get(ACL_EXPOSE_HEADERS), 'X-Another-Custom-Header, X-My-Custom-Header') if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_max_age.py000066400000000000000000000034631466443373300232600ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from datetime import timedelta from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class MaxAgeTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/defaults') @cross_origin() def defaults(): return 'Should only return headers on OPTIONS' @self.app.route('/test_string') @cross_origin(max_age=600) def test_string(): return 'Open!' @self.app.route('/test_time_delta') @cross_origin(max_age=timedelta(minutes=10)) def test_time_delta(): return 'Open!' def test_defaults(self): ''' By default, no max-age headers should be returned ''' for resp in self.iter_responses('/defaults', origin='www.example.com'): self.assertFalse(ACL_MAX_AGE in resp.headers) def test_string(self): ''' If the methods parameter is defined, always return the allowed methods defined by the user. ''' resp = self.preflight('/test_string', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_MAX_AGE), '600') def test_time_delta(self): ''' If the methods parameter is defined, always return the allowed methods defined by the user. ''' resp = self.preflight('/test_time_delta', origin='www.example.com') self.assertEqual(resp.headers.get(ACL_MAX_AGE), '600') if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_methods.py000066400000000000000000000041621466443373300233170ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class MethodsCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/defaults') @cross_origin() def defaults(): return 'Should only return headers on pre-flight OPTIONS request' @self.app.route('/test_methods_defined') @cross_origin(methods=['POST']) def test_get(): return 'Only allow POST' def test_defaults(self): ''' Access-Control-Allow-Methods headers should only be returned if the client makes an OPTIONS request. ''' self.assertFalse(ACL_METHODS in self.get('/defaults', origin='www.example.com').headers) self.assertFalse(ACL_METHODS in self.head('/defaults', origin='www.example.com').headers) res = self.preflight('/defaults', 'POST', origin='www.example.com') for method in ALL_METHODS: self.assertTrue(method in res.headers.get(ACL_METHODS)) def test_methods_defined(self): ''' If the methods parameter is defined, it should override the default methods defined by the user. ''' self.assertFalse(ACL_METHODS in self.get('/test_methods_defined').headers) self.assertFalse(ACL_METHODS in self.head('/test_methods_defined').headers) res = self.preflight('/test_methods_defined', 'POST', origin='www.example.com') self.assertTrue('POST' in res.headers.get(ACL_METHODS)) res = self.preflight('/test_methods_defined', 'PUT', origin='www.example.com') self.assertFalse(ACL_METHODS in res.headers) res = self.get('/test_methods_defined', origin='www.example.com') self.assertFalse(ACL_METHODS in res.headers) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_options.py000066400000000000000000000054621466443373300233530ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class OptionsTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_default') @cross_origin() def test_default(): return 'Welcome!' @self.app.route('/test_no_options_and_not_auto') @cross_origin(automatic_options=False) def test_no_options_and_not_auto(): return 'Welcome!' @self.app.route('/test_options_and_not_auto', methods=['OPTIONS']) @cross_origin(automatic_options=False) def test_options_and_not_auto(): return 'Welcome!' def test_defaults(self): ''' The default behavior should automatically provide OPTIONS and return CORS headers. ''' resp = self.options('/test_default', origin='http://foo.bar.com') self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) resp = self.options('/test_default', origin='http://foo.bar.com') self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) def test_no_options_and_not_auto(self): ''' If automatic_options is False, and the view func does not provide OPTIONS, then Flask's default handling will occur, and no CORS headers will be returned. ''' resp = self.options('/test_no_options_and_not_auto') self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) resp = self.options('/test_no_options_and_not_auto', origin='http://foo.bar.com') self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) def test_options_and_not_auto(self): ''' If OPTIONS is in methods, and automatic_options is False, the view function must return a response. ''' resp = self.options('/test_options_and_not_auto', origin='http://foo.bar.com') self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) self.assertEqual(resp.data.decode("utf-8"), u"Welcome!") resp = self.options('/test_options_and_not_auto', origin='http://foo.bar.com') self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) self.assertEqual(resp.data.decode("utf-8"), u"Welcome!") if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_origins.py000066400000000000000000000176051466443373300233340ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask import re from flask_cors import * from flask_cors.core import * letters = 'abcdefghijklmnopqrstuvwxyz' # string.letters is not PY3 compatible class OriginsTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/') @cross_origin() def wildcard(): return 'Welcome!' @self.app.route('/test_always_send') @cross_origin(always_send=True) def test_always_send(): return 'Welcome!' @self.app.route('/test_always_send_no_wildcard') @cross_origin(always_send=True, send_wildcard=False) def test_always_send_no_wildcard(): return 'Welcome!' @self.app.route('/test_send_wildcard_with_origin') @cross_origin(send_wildcard=True) def test_send_wildcard_with_origin(): return 'Welcome!' @self.app.route('/test_list') @cross_origin(origins=["http://foo.com", "http://bar.com"]) def test_list(): return 'Welcome!' @self.app.route('/test_string') @cross_origin(origins="http://foo.com") def test_string(): return 'Welcome!' @self.app.route('/test_set') @cross_origin(origins={"http://foo.com", "http://bar.com"}) def test_set(): return 'Welcome!' @self.app.route('/test_subdomain_regex') @cross_origin(origins=r"http?://\w*\.?example\.com:?\d*/?.*") def test_subdomain_regex(): return '' @self.app.route('/test_compiled_subdomain_regex') @cross_origin(origins=re.compile(r"http?://\w*\.?example\.com:?\d*/?.*")) def test_compiled_subdomain_regex(): return '' @self.app.route('/test_regex_list') @cross_origin(origins=[r".*.example.com", r".*.otherexample.com"]) def test_regex_list(): return '' @self.app.route('/test_regex_mixed_list') @cross_origin(origins=["http://example.com", r".*.otherexample.com"]) def test_regex_mixed_list(): return '' @self.app.route('/test_multiple_protocols') @cross_origin(origins="https?://example.com") def test_multiple_protocols(): return '' def test_defaults_no_origin(self): ''' If there is no Origin header in the request, the Access-Control-Allow-Origin header should be '*' by default. ''' for resp in self.iter_responses('/'): self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_defaults_with_origin(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' for resp in self.iter_responses('/', origin='http://example.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://example.com') def test_always_send_no_wildcard(self): ''' If send_wildcard=False, but the there is '*' in the allowed origins, we should send it anyways. ''' for resp in self.iter_responses('/'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_always_send_no_wildcard_origins(self): for resp in self.iter_responses('/'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_send_wildcard_with_origin(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' for resp in self.iter_responses('/test_send_wildcard_with_origin', origin='http://example.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_list_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed. ''' resp = self.get('/test_list', origin='http://bar.com') self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://bar.com') def test_string_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_string', origin='http://foo.com') self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') def test_set_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_set', origin='http://bar.com') allowed = resp.headers.get(ACL_ORIGIN) # Order is not guaranteed self.assertEqual(allowed, 'http://bar.com') def test_not_matching_origins(self): for resp in self.iter_responses('/test_list',origin="http://bazz.com"): self.assertFalse(ACL_ORIGIN in resp.headers) def test_subdomain_regex(self): for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) def test_compiled_subdomain_regex(self): for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_compiled_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) def test_regex_list(self): for parent in 'example.com', 'otherexample.com': for sub in letters: domain = "http://{}.{}.com".format(sub, parent) for resp in self.iter_responses('/test_regex_list', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) def test_regex_mixed_list(self): ''' Tests the corner case occurs when the send_always setting is True and no Origin header in the request, it is not possible to match the regular expression(s) to determine the correct Access-Control-Allow-Origin header to be returned. Instead, the list of origins is serialized, and any strings which seem like regular expressions (e.g. are not a '*' and contain either '*' or '?') will be skipped. Thus, the list of returned Access-Control-Allow-Origin header is guaranteed to be 'null', the origin or "*", as per the w3 http://www.w3.org/TR/cors/#access-control-allow-origin-response-header ''' for sub in letters: domain = "http://%s.otherexample.com" % sub for resp in self.iter_responses('/test_regex_mixed_list', origin=domain): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) self.assertEqual("http://example.com", self.get('/test_regex_mixed_list', origin='http://example.com').headers.get(ACL_ORIGIN)) def test_multiple_protocols(self): import logging logging.getLogger('flask_cors').level = logging.DEBUG resp = self.get('test_multiple_protocols', origin='https://example.com') self.assertEqual('https://example.com', resp.headers.get(ACL_ORIGIN)) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_private_network_headers.py000066400000000000000000000042221466443373300265670ustar00rootroot00000000000000# -*- coding: utf-8 -*- from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class SupportsCredentialsCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/test_allow_private_network_access_supported') @cross_origin(allow_private_network=True) def test_private_network_supported(): return 'Private network!' @self.app.route('/test_allow_private_network_access_unsupported') @cross_origin(allow_private_network=False) def test_credentials_unsupported(): return 'Private network!' @self.app.route('/test_default') @cross_origin() def test_default(): return 'Open!' def test_credentials_supported(self): """ The specified route should return the Access-Control-Allow-Credentials header. """ resp = self.get('/test_allow_private_network_access_supported', origin='www.example.com', headers={ACL_REQUEST_HEADER_PRIVATE_NETWORK:'true'}) self.assertEqual(resp.headers.get(ACL_RESPONSE_PRIVATE_NETWORK), 'true') def test_default(self): """ The default behavior should be to allow private network access. """ resp = self.get('/test_default', origin='www.example.com', headers={ACL_REQUEST_HEADER_PRIVATE_NETWORK:'true'}) self.assertFalse(resp.headers.get('ACL_RESPONSE_PRIVATE_NETWORK')) resp = self.get('/test_default') self.assertFalse(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers) def test_credentials_unsupported(self): """ If private network access is disabled, the header should never be sent.""" resp = self.get('/test_allow_private_network_access_unsupported', origin='www.example.com') self.assertFalse(ACL_RESPONSE_PRIVATE_NETWORK in resp.headers) resp = self.get('/test_allow_private_network_access_unsupported', origin='www.example.com', headers={ACL_REQUEST_HEADER_PRIVATE_NETWORK:'true'}) self.assertEqual(resp.headers.get(ACL_RESPONSE_PRIVATE_NETWORK), 'false') if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_vary_header.py000066400000000000000000000061001466443373300241370ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask, Response from flask_cors import * from flask_cors.core import * class VaryHeaderTestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/') @cross_origin() def wildcard(): return 'Welcome!' @self.app.route('/test_consistent_origin') @cross_origin(origins='http://foo.com') def test_consistent(): return 'Welcome!' @self.app.route('/test_vary') @cross_origin(origins=["http://foo.com", "http://bar.com"]) def test_vary(): return 'Welcome!' @self.app.route('/test_existing_vary_headers') @cross_origin(origins=["http://foo.com", "http://bar.com"]) def test_existing_vary_headers(): return Response('', status=200, headers={'Vary': 'Accept-Encoding'}) def test_default(self): ''' By default, allow all domains, which means the Vary:Origin header should be set. ''' for resp in self.iter_responses('/', origin="http://foo.com"): self.assertTrue('Vary' in resp.headers) def test_consistent_origin(self): ''' If the Access-Control-Allow-Origin header will change dynamically, the Vary:Origin header should be set. ''' for resp in self.iter_responses('/test_consistent_origin', origin="http://foo.com"): self.assertFalse('Vary' in resp.headers) def test_varying_origin(self): ''' Resources that wish to enable themselves to be shared with multiple Origins but do not respond uniformly with "*" must in practice generate the Access-Control-Allow-Origin header dynamically in response to every request they wish to allow. As a consequence, authors of such resources should send a Vary: Origin HTTP header or provide other appropriate control directives to prevent caching of such responses, which may be inaccurate if re-used across-origins. ''' example_origin = 'http://foo.com' for resp in self.iter_responses('/test_vary', origin=example_origin): self.assertHasACLOrigin(resp) self.assertEqual(resp.headers.get('Vary'), 'Origin') def test_consistent_origin_concat(self): ''' If Flask-Cors adds a Vary header and there is already a Vary header set, the headers should be combined and comma-separated. ''' resp = self.get('/test_existing_vary_headers', origin="http://foo.com") self.assertEqual(set(resp.headers.getlist('Vary')), {'Origin', 'Accept-Encoding'}) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/decorator/test_w3.py000066400000000000000000000050371466443373300222070ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ from ..base_test import FlaskCorsTestCase from flask import Flask from flask_cors import * from flask_cors.core import * class OriginsW3TestCase(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) @self.app.route('/') @cross_origin(origins='*', send_wildcard=False, always_send=False) def allowOrigins(): ''' This sets up flask-cors to echo the request's `Origin` header, only if it is actually set. This behavior is most similar to the actual W3 specification, http://www.w3.org/TR/cors/ but is not the default because it is more common to use the wildcard configuration in order to support CDN caching. ''' return 'Welcome!' @self.app.route('/default-origins') @cross_origin(send_wildcard=False, always_send=False) def noWildcard(): ''' With the default origins configuration, send_wildcard should still be respected. ''' return 'Welcome!' def test_wildcard_origin_header(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' example_origin = 'http://example.com' headers = {'Origin': example_origin} for resp in self.iter_responses('/', headers=headers): self.assertEqual( resp.headers.get(ACL_ORIGIN), example_origin ) def test_wildcard_no_origin_header(self): ''' If there is no Origin header in the request, the Access-Control-Allow-Origin header should not be included. ''' for resp in self.iter_responses('/'): self.assertTrue(ACL_ORIGIN not in resp.headers) def test_wildcard_default_origins(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' example_origin = 'http://example.com' headers = {'Origin': example_origin} for resp in self.iter_responses('/default-origins', headers=headers): self.assertEqual(resp.headers.get(ACL_ORIGIN), example_origin) if __name__ == "__main__": unittest.main() python-flask-cors-5.0.0/tests/extension/000077500000000000000000000000001466443373300202725ustar00rootroot00000000000000python-flask-cors-5.0.0/tests/extension/__init__.py000066400000000000000000000004661466443373300224110ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ Tests particular to flask_cors.CORS ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ python-flask-cors-5.0.0/tests/extension/test_app_extension.py000066400000000000000000000332361466443373300245660ustar00rootroot00000000000000# -*- coding: utf-8 -*- """ test ~~~~ Flask-CORS is a simple extension to Flask allowing you to support cross origin resource sharing (CORS) using a simple decorator. :copyright: (c) 2016 by Cory Dolphin. :license: MIT, see LICENSE for more details. """ import re from ..base_test import FlaskCorsTestCase from flask import Flask, jsonify from flask_cors import * from flask_cors.core import * letters = 'abcdefghijklmnopqrstuvwxyz' # string.letters is not PY3 compatible class AppExtensionRegexp(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, resources={ r'/test_list': {'origins': ["http://foo.com", "http://bar.com"]}, r'/test_string': {'origins': 'http://foo.com'}, r'/test_set': { 'origins': {"http://foo.com", "http://bar.com"} }, r'/test_subdomain_regex': { 'origins': r"http?://\w*\.?example\.com:?\d*/?.*" }, r'/test_regex_list': { 'origins': [r".*.example.com", r".*.otherexample.com"] }, r'/test_regex_mixed_list': { 'origins': ["http://example.com", r".*.otherexample.com"] }, r'/test_send_wildcard_with_origin' : { 'send_wildcard':True }, re.compile(r'/test_compiled_subdomain_\w*'): { 'origins': re.compile(r"http://example\d+.com") }, r'/test_defaults':{} }) @self.app.route('/test_defaults') def wildcard(): return 'Welcome!' @self.app.route('/test_send_wildcard_with_origin') def send_wildcard_with_origin(): return 'Welcome!' @self.app.route('/test_list') def test_list(): return 'Welcome!' @self.app.route('/test_string') def test_string(): return 'Welcome!' @self.app.route('/test_set') def test_set(): return 'Welcome!' def test_defaults_no_origin(self): ''' If there is no Origin header in the request, by default the '*' should be sent ''' for resp in self.iter_responses('/test_defaults'): self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_defaults_with_origin(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' for resp in self.iter_responses('/test_defaults', origin='http://example.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://example.com') def test_send_wildcard_with_origin(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be included. ''' for resp in self.iter_responses('/test_send_wildcard_with_origin', origin='http://example.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), '*') def test_list_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed. ''' resp = self.get('/test_list', origin='http://bar.com') self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://bar.com') def test_string_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_string', origin='http://foo.com') self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') def test_set_serialized(self): ''' If there is an Origin header in the request, the Access-Control-Allow-Origin header should be echoed back. ''' resp = self.get('/test_set', origin='http://bar.com') allowed = resp.headers.get(ACL_ORIGIN) # Order is not guaranteed self.assertEqual(allowed, 'http://bar.com') def test_not_matching_origins(self): for resp in self.iter_responses('/test_list',origin="http://bazz.com"): self.assertFalse(ACL_ORIGIN in resp.headers) def test_subdomain_regex(self): for sub in letters: domain = "http://%s.example.com" % sub for resp in self.iter_responses('/test_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) def test_compiled_subdomain_regex(self): for sub in [1, 100, 200]: domain = "http://example%s.com" % sub for resp in self.iter_responses('/test_compiled_subdomain_regex', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) for resp in self.iter_responses('/test_compiled_subdomain_regex', headers={'origin': "http://examplea.com"}): self.assertEqual(None, resp.headers.get(ACL_ORIGIN)) def test_regex_list(self): for parent in 'example.com', 'otherexample.com': for sub in letters: domain = "http://{}.{}.com".format(sub, parent) for resp in self.iter_responses('/test_regex_list', headers={'origin': domain}): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) def test_regex_mixed_list(self): ''' Tests the corner case occurs when the send_always setting is True and no Origin header in the request, it is not possible to match the regular expression(s) to determine the correct Access-Control-Allow-Origin header to be returned. Instead, the list of origins is serialized, and any strings which seem like regular expressions (e.g. are not a '*' and contain either '*' or '?') will be skipped. Thus, the list of returned Access-Control-Allow-Origin header is guaranteed to be 'null', the origin or "*", as per the w3 http://www.w3.org/TR/cors/#access-control-allow-origin-response-header ''' for sub in letters: domain = "http://%s.otherexample.com" % sub for resp in self.iter_responses('/test_regex_mixed_list', origin=domain): self.assertEqual(domain, resp.headers.get(ACL_ORIGIN)) self.assertEqual("http://example.com", self.get('/test_regex_mixed_list', origin='http://example.com').headers.get(ACL_ORIGIN)) class AppExtensionList(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, resources=[r'/test_exposed', r'/test_other_exposed'], origins=['http://foo.com', 'http://bar.com']) @self.app.route('/test_unexposed') def unexposed(): return 'Not exposed over CORS!' @self.app.route('/test_exposed') def exposed1(): return 'Welcome!' @self.app.route('/test_other_exposed') def exposed2(): return 'Welcome!' def test_exposed(self): for resp in self.iter_responses('/test_exposed', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN),'http://foo.com') def test_other_exposed(self): for resp in self.iter_responses('/test_other_exposed', origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://bar.com') def test_unexposed(self): for resp in self.iter_responses('/test_unexposed', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) class AppExtensionString(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, resources=r'/api/*', allow_headers='Content-Type', expose_headers='X-Total-Count', origins='http://bar.com') @self.app.route('/api/v1/foo') def exposed1(): return jsonify(success=True) @self.app.route('/api/v1/bar') def exposed2(): return jsonify(success=True) @self.app.route('/api/v1/special') @cross_origin(origins='http://foo.com') def overridden(): return jsonify(special=True) @self.app.route('/') def index(): return 'Welcome' @self.app.route('/foo.txt') def foo_txt(): return 'Welcome' def test_exposed(self): for path in '/api/v1/foo', '/api/v1/bar': for resp in self.iter_responses(path, origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://bar.com') self.assertEqual(resp.headers.get(ACL_EXPOSE_HEADERS), 'X-Total-Count') for resp in self.iter_responses(path, origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) def test_unexposed(self): for resp in self.iter_responses('/', origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) def test_override(self): for resp in self.iter_responses('/api/v1/special', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.headers.get(ACL_ORIGIN), 'http://foo.com') self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) for resp in self.iter_responses('/api/v1/special', origin='http://bar.com'): self.assertEqual(resp.status_code, 200) self.assertFalse(ACL_ORIGIN in resp.headers) self.assertFalse(ACL_EXPOSE_HEADERS in resp.headers) class AppExtensionError(FlaskCorsTestCase): def test_value_error(self): try: app = Flask(__name__) CORS(app, resources=5) self.assertTrue(False, "Should've raised a value error") except ValueError: pass class AppExtensionDefault(FlaskCorsTestCase): def test_default(self): ''' By default match all. ''' self.app = Flask(__name__) CORS(self.app) @self.app.route('/') def index(): return 'Welcome' for resp in self.iter_responses('/', origin='http://foo.com'): self.assertEqual(resp.status_code, 200) self.assertTrue(ACL_ORIGIN in resp.headers) class AppExtensionExampleApp(FlaskCorsTestCase): def setUp(self): self.app = Flask(__name__) CORS(self.app, resources={ r'/api/*': {'origins': ['http://blah.com', 'http://foo.bar']} }) @self.app.route('/') def index(): return '' @self.app.route('/api/foo') def test_wildcard(): return '' @self.app.route('/api/') def test_exact_match(): return '' def test_index(self): ''' If regex does not match, do not set CORS ''' for resp in self.iter_responses('/', origin='http://foo.bar'): self.assertFalse(ACL_ORIGIN in resp.headers) def test_wildcard(self): ''' Match anything matching the path /api/* with an origin of 'http://blah.com' or 'http://foo.bar' ''' for origin in ['http://foo.bar', 'http://blah.com']: for resp in self.iter_responses('/api/foo', origin=origin): self.assertTrue(ACL_ORIGIN in resp.headers) self.assertEqual(origin, resp.headers.get(ACL_ORIGIN)) def test_exact_match(self): ''' Match anything matching the path /api/* with an origin of 'http://blah.com' or 'http://foo.bar' ''' for origin in ['http://foo.bar', 'http://blah.com']: for resp in self.iter_responses('/api/', origin=origin): self.assertTrue(ACL_ORIGIN in resp.headers) self.assertEqual(origin, resp.headers.get(ACL_ORIGIN)) class AppExtensionCompiledRegexp(FlaskCorsTestCase): def test_compiled_regex(self): ''' Ensure we do not error if the user specifies an bad regular expression. ''' import re self.app = Flask(__name__) CORS(self.app, resources=re.compile('/api/.*')) @self.app.route('/') def index(): return 'Welcome' @self.app.route('/api/v1') def example(): return 'Welcome' for resp in self.iter_responses('/'): self.assertFalse(ACL_ORIGIN in resp.headers) for resp in self.iter_responses('/api/v1', origin='http://foo.com'): self.assertTrue(ACL_ORIGIN in resp.headers) class AppExtensionBadRegexp(FlaskCorsTestCase): def test_value_error(self): ''' Ensure we do not error if the user specifies an bad regular expression. ''' self.app = Flask(__name__) CORS(self.app, resources="}") @self.app.route('/') def index(): return 'Welcome' for resp in self.iter_responses('/'): self.assertEqual(resp.status_code, 200) if __name__ == "__main__": unittest.main()