1# -*- coding: utf-8 -*- 2""" 3 extension 4 ~~~~ 5 Flask-CORS is a simple extension to Flask allowing you to support cross 6 origin resource sharing (CORS) using a simple decorator. 7 8 :copyright: (c) 2016 by Cory Dolphin. 9 :license: MIT, see LICENSE for more details. 10""" 11from flask import request 12from .core import * 13 14LOG = logging.getLogger(__name__) 15 16class CORS(object): 17 """ 18 Initializes Cross Origin Resource sharing for the application. The 19 arguments are identical to :py:func:`cross_origin`, with the addition of a 20 `resources` parameter. The resources parameter defines a series of regular 21 expressions for resource paths to match and optionally, the associated 22 options to be applied to the particular resource. These options are 23 identical to the arguments to :py:func:`cross_origin`. 24 25 The settings for CORS are determined in the following order 26 27 1. Resource level settings (e.g when passed as a dictionary) 28 2. Keyword argument settings 29 3. App level configuration settings (e.g. CORS_*) 30 4. Default settings 31 32 Note: as it is possible for multiple regular expressions to match a 33 resource path, the regular expressions are first sorted by length, 34 from longest to shortest, in order to attempt to match the most 35 specific regular expression. This allows the definition of a 36 number of specific resource options, with a wildcard fallback 37 for all other resources. 38 39 :param resources: 40 The series of regular expression and (optionally) associated CORS 41 options to be applied to the given resource path. 42 43 If the argument is a dictionary, it's keys must be regular expressions, 44 and the values must be a dictionary of kwargs, identical to the kwargs 45 of this function. 46 47 If the argument is a list, it is expected to be a list of regular 48 expressions, for which the app-wide configured options are applied. 49 50 If the argument is a string, it is expected to be a regular expression 51 for which the app-wide configured options are applied. 52 53 Default : Match all and apply app-level configuration 54 55 :type resources: dict, iterable or string 56 57 :param origins: 58 The origin, or list of origins to allow requests from. 59 The origin(s) may be regular expressions, case-sensitive strings, 60 or else an asterisk 61 62 Default : '*' 63 :type origins: list, string or regex 64 65 :param methods: 66 The method or list of methods which the allowed origins are allowed to 67 access for non-simple requests. 68 69 Default : [GET, HEAD, POST, OPTIONS, PUT, PATCH, DELETE] 70 :type methods: list or string 71 72 :param expose_headers: 73 The header or list which are safe to expose to the API of a CORS API 74 specification. 75 76 Default : None 77 :type expose_headers: list or string 78 79 :param allow_headers: 80 The header or list of header field names which can be used when this 81 resource is accessed by allowed origins. The header(s) may be regular 82 expressions, case-sensitive strings, or else an asterisk. 83 84 Default : '*', allow all headers 85 :type allow_headers: list, string or regex 86 87 :param supports_credentials: 88 Allows users to make authenticated requests. If true, injects the 89 `Access-Control-Allow-Credentials` header in responses. This allows 90 cookies and credentials to be submitted across domains. 91 92 :note: This option cannot be used in conjuction with a '*' origin 93 94 Default : False 95 :type supports_credentials: bool 96 97 :param max_age: 98 The maximum time for which this CORS request maybe cached. This value 99 is set as the `Access-Control-Max-Age` header. 100 101 Default : None 102 :type max_age: timedelta, integer, string or None 103 104 :param send_wildcard: If True, and the origins parameter is `*`, a wildcard 105 `Access-Control-Allow-Origin` header is sent, rather than the 106 request's `Origin` header. 107 108 Default : False 109 :type send_wildcard: bool 110 111 :param vary_header: 112 If True, the header Vary: Origin will be returned as per the W3 113 implementation guidelines. 114 115 Setting this header when the `Access-Control-Allow-Origin` is 116 dynamically generated (e.g. when there is more than one allowed 117 origin, and an Origin than '*' is returned) informs CDNs and other 118 caches that the CORS headers are dynamic, and cannot be cached. 119 120 If False, the Vary header will never be injected or altered. 121 122 Default : True 123 :type vary_header: bool 124 """ 125 126 def __init__(self, app=None, **kwargs): 127 self._options = kwargs 128 if app is not None: 129 self.init_app(app, **kwargs) 130 131 def init_app(self, app, **kwargs): 132 # The resources and options may be specified in the App Config, the CORS constructor 133 # or the kwargs to the call to init_app. 134 options = get_cors_options(app, self._options, kwargs) 135 136 # Flatten our resources into a list of the form 137 # (pattern_or_regexp, dictionary_of_options) 138 resources = parse_resources(options.get('resources')) 139 140 # Compute the options for each resource by combining the options from 141 # the app's configuration, the constructor, the kwargs to init_app, and 142 # finally the options specified in the resources dictionary. 143 resources = [ 144 (pattern, get_cors_options(app, options, opts)) 145 for (pattern, opts) in resources 146 ] 147 148 # Create a human readable form of these resources by converting the compiled 149 # regular expressions into strings. 150 resources_human = {get_regexp_pattern(pattern): opts for (pattern,opts) in resources} 151 LOG.debug("Configuring CORS with resources: %s", resources_human) 152 153 cors_after_request = make_after_request_function(resources) 154 app.after_request(cors_after_request) 155 156 # Wrap exception handlers with cross_origin 157 # These error handlers will still respect the behavior of the route 158 if options.get('intercept_exceptions', True): 159 def _after_request_decorator(f): 160 def wrapped_function(*args, **kwargs): 161 return cors_after_request(app.make_response(f(*args, **kwargs))) 162 return wrapped_function 163 164 if hasattr(app, 'handle_exception'): 165 app.handle_exception = _after_request_decorator( 166 app.handle_exception) 167 app.handle_user_exception = _after_request_decorator( 168 app.handle_user_exception) 169 170def make_after_request_function(resources): 171 def cors_after_request(resp): 172 # If CORS headers are set in a view decorator, pass 173 if resp.headers is not None and resp.headers.get(ACL_ORIGIN): 174 LOG.debug('CORS have been already evaluated, skipping') 175 return resp 176 177 for res_regex, res_options in resources: 178 if try_match(request.path, res_regex): 179 LOG.debug("Request to '%s' matches CORS resource '%s'. Using options: %s", 180 request.path, get_regexp_pattern(res_regex), res_options) 181 set_cors_headers(resp, res_options) 182 break 183 else: 184 LOG.debug('No CORS rule matches') 185 return resp 186 return cors_after_request 187