forked from corydolphin/flask-cors
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathflask_cors.py
163 lines (132 loc) · 6.75 KB
/
flask_cors.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# -*- 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) 2014 by Cory Dolphin.
:license: MIT, see LICENSE for more details.
"""
from _version import __version__
import collections
from datetime import timedelta
from functools import update_wrapper
from flask import make_response, request, current_app
from six import string_types
# Common string constants
ACL_ORIGIN = 'Access-Control-Allow-Origin'
ACL_METHODS = 'Access-Control-Allow-Methods'
ACL_HEADERS = 'Access-Control-Allow-Headers'
ACL_CREDENTIALS = 'Access-Control-Allow-Credentials'
ACL_MAX_AGE = 'Access-Control-Max-Age'
def cross_origin(origins=None, methods=None, headers=None,
supports_credentials=False, max_age=None, send_wildcard=True,
always_send=True, automatic_options=True):
'''
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, for example Cross Site Forgery
Request protection.
:param origins: The origin, or list of origins which are to be allowed,
and injected into the returned `Access-Control-Allow-Origin` header
:type origins: list or string
:param methods: The methods to be allowed and injected as the
`Access-Control-Allow-Methods` header returned.
:type methods: list
:param headers: The list of allowed headers to be injected as the
`Access-Control-Allow-Headers` header returned.
:type headers: list or string
:param supports_credentials: Allows users to make authenticated requests.
If true, injects the `Access-Control-Allow-Credentials` header in
responses.
Note: this option cannot be used in conjuction with a '*' origin
: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.
: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 echoing the request's `Origin`
header.
:type send_wildcard: bool
:param always_send: If True, CORS headers are sent even if there is no
`Origin` in the request's headers.
:type always_send: bool
:param automatic_options: If True, CORS headers will be returned for
OPTIONS requests. For use with cross domain POST requests which
preflight OPTIONS requests, you will need to specifically allow
the Content-Type header.
:type automatic_options: bool
'''
_origins = origins
_headers = headers
methods = methods or ['GET', 'HEAD', 'POST', 'OPTIONS', 'PUT']
methods = ', '.join(sorted(x for x in methods)).upper()
if isinstance(max_age, timedelta):
max_age = max_age.total_seconds()
def decorator(f):
def wrapped_function(*args, **kwargs):
# Determine origins when in the context of a request
origins = _origins or current_app.config.get('CORS_ORIGINS', '*')
origins_str = str(origins)
wildcard = origins_str == '*'
if(not isinstance(origins, string_types)
and isinstance(origins, collections.Iterable)):
origins_str = ', '.join(origins)
# Determine headers when in the context of the request
headers = _headers or current_app.config.get('CORS_HEADERS')
if (not isinstance(headers, string_types)
and isinstance(headers, collections.Iterable)):
headers = ', '.join(x for x in headers)
# If the Origin header is not present terminate this set of steps.
# The request is outside the scope of this specification.
request_origin = request.headers.get('Origin', '')
if 'Origin' not in request.headers and not always_send:
return make_response(f(*args, **kwargs))
# If the value of the Origin header is not a case-sensitive match
# for any of the values in list of origins, do not set any
# additional headers and terminate this set of steps.
elif(not wildcard and not always_send and
request_origin not in origins):
return make_response(f(*args, **kwargs))
if automatic_options and request.method == 'OPTIONS':
resp = current_app.make_default_options_response()
else:
resp = make_response(f(*args, **kwargs))
# Add a single Access-Control-Allow-Origin header, with either
# the value of the Origin header or the string "*" as value.
if wildcard:
# If the `origins` param is '*', either send the request's
# origin, or `*`
if send_wildcard:
resp.headers[ACL_ORIGIN] = origins
else:
req_origin = request.headers.get('Origin', '*')
resp.headers[ACL_ORIGIN] = req_origin
# If not 'wildcard', send the string-joined-form of the
# origins header
else:
resp.headers[ACL_ORIGIN] = origins_str
resp.headers[ACL_METHODS] = methods
if max_age:
resp.headers[ACL_MAX_AGE] = str(max_age)
if headers is not None:
resp.headers[ACL_HEADERS] = headers
if supports_credentials:
resp.headers[ACL_CREDENTIALS] = 'true'
return resp
# If True, intercept OPTIONS requests by modifying the view function
# mirroring 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 automatic_options:
f.required_methods = getattr(f, 'required_methods', set())
f.required_methods.add('OPTIONS')
f.provide_automatic_options = False
return update_wrapper(wrapped_function, f)
return decorator