24
24
)
25
25
26
26
27
+ def check_preflight_request (request : flask .Request ) -> bool :
28
+ """Check if the request is a preflight request
29
+
30
+ A preflight request is a CORS request that checks if the API endpoint is
31
+ allowed to be accessed outside of the domain. This function checks if the
32
+ request is a preflight request by checking if the request method is OPTIONS
33
+ and if the request headers contain the `Origin` header.
34
+
35
+ Args:
36
+ request (flask.Request): The request object
37
+
38
+ Returns:
39
+ bool: True if the request is a preflight request, False otherwise
40
+ """
41
+
42
+ if "Origin" not in request .headers :
43
+ return False
44
+
45
+ if "Access-Control-Request-Method" not in request .headers :
46
+ return False
47
+
48
+ if request .headers ["Access-Control-Request-Method" ] != "GET" :
49
+ return False
50
+
51
+ if "Access-Control-Request-Headers" not in request .headers :
52
+ return False
53
+
54
+ if (
55
+ "x-cursus-api-token"
56
+ != request .headers ["Access-Control-Request-Headers" ].lower ()
57
+ ):
58
+ return False
59
+
60
+ return True
61
+
62
+
27
63
api_bp : Blueprint = Blueprint (
28
64
name = "api" , import_name = __name__ , url_prefix = "/api/v1/"
29
65
)
@@ -61,23 +97,27 @@ def swagger():
61
97
def before_request ():
62
98
"""Process actions all requests that are made to the API endpoints"""
63
99
64
- # Missing API token
65
- if "X-CURSUS-API-TOKEN" not in request .headers :
66
- raise CursusException .BadRequestError (
67
- "API endpoints require an authorized API token"
100
+ if request .method != "OPTIONS" and request .method != "GET" :
101
+ raise CursusException .MethodNotAllowedError (
102
+ "Only GET requests are allowed"
68
103
)
69
104
105
+ # Prelight request to check if the API endpoint is allowed to be accessed
106
+ # outside of the domain
70
107
if request .method == "OPTIONS" :
71
- response = flask .make_response ("" , 200 )
108
+ if check_preflight_request (request ):
109
+ # Returning a non-None value from a before_request handler will
110
+ # cause Flask to skip the normal request handling and continue to
111
+ # the after request handler.
112
+ return flask .make_response ()
72
113
73
- response .headers .add ("Content-Type" , "application/json" )
74
- response .headers .add ("Access-Control-Allow-Origin" , "*" )
75
- response .headers .add ("Access-Control-Allow-Methods" , "GET, OPTIONS" )
76
- response .headers .add (
77
- "Access-Control-Allow-Headers" , "origin, x-cursus-api-token"
78
- )
114
+ raise CursusException .BadRequestError ("Invalid preflight request" )
79
115
80
- return response
116
+ # Missing API token
117
+ if "X-CURSUS-API-TOKEN" not in request .headers :
118
+ raise CursusException .BadRequestError (
119
+ "API endpoints require an authorized API token"
120
+ )
81
121
82
122
token = request .headers ["X-CURSUS-API-TOKEN" ]
83
123
@@ -126,8 +166,6 @@ def before_request():
126
166
def after_request (response : flask .Response ):
127
167
"""Perform actions after a request has been processed"""
128
168
129
- response .headers .add ("Access-Control-Allow-Origin" , "*" )
130
-
131
169
# A response that made it to the endpoint handler either succeeded (200) or
132
170
# failed (404) to retrieve the requested resource. In both cases, we want
133
171
# to increment the request count for the API token.
@@ -136,6 +174,18 @@ def after_request(response: flask.Response):
136
174
if response .status_code != 200 and response .status_code != 404 :
137
175
return response
138
176
177
+ response .headers .add ("Access-Control-Allow-Origin" , "*" )
178
+ response .headers .add (
179
+ "Access-Control-Allow-Headers" ,
180
+ "X-CURSUS-API-TOKEN, Content-Type, Accept, Origin" ,
181
+ )
182
+ response .headers .add ("Access-Control-Allow-Methods" , "GET, OPTIONS" )
183
+ response .headers .add ("Access-Control-Allow-Credentials" , "true" )
184
+ response .headers .add ("Access-Control-Max-Age" , "86400" )
185
+
186
+ if request .method == "OPTIONS" :
187
+ return response
188
+
139
189
token = request .headers ["X-CURSUS-API-TOKEN" ]
140
190
cache_item = cache .get (token )
141
191
0 commit comments