You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
7.4 KiB
191 lines
7.4 KiB
2 years ago
|
# -*- 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.
|
||
|
"""
|
||
|
from flask import request
|
||
|
from .core import *
|
||
|
try:
|
||
|
from urllib.parse import unquote_plus
|
||
|
except ImportError:
|
||
|
from urllib import unquote_plus
|
||
|
|
||
|
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
|
||
|
|
||
|
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
|
||
|
"""
|
||
|
|
||
|
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 '%s' 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
|