Skip to content

Commit ebd2e55

Browse files
fix: fix cors problem on requesting API endpoints
1 parent d1acf74 commit ebd2e55

File tree

2 files changed

+64
-19
lines changed

2 files changed

+64
-19
lines changed

cursus/apis/__init__.py

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,42 @@
2424
)
2525

2626

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+
2763
api_bp: Blueprint = Blueprint(
2864
name="api", import_name=__name__, url_prefix="/api/v1/"
2965
)
@@ -61,23 +97,27 @@ def swagger():
6197
def before_request():
6298
"""Process actions all requests that are made to the API endpoints"""
6399

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"
68103
)
69104

105+
# Prelight request to check if the API endpoint is allowed to be accessed
106+
# outside of the domain
70107
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()
72113

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")
79115

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+
)
81121

82122
token = request.headers["X-CURSUS-API-TOKEN"]
83123

@@ -126,8 +166,6 @@ def before_request():
126166
def after_request(response: flask.Response):
127167
"""Perform actions after a request has been processed"""
128168

129-
response.headers.add("Access-Control-Allow-Origin", "*")
130-
131169
# A response that made it to the endpoint handler either succeeded (200) or
132170
# failed (404) to retrieve the requested resource. In both cases, we want
133171
# to increment the request count for the API token.
@@ -136,6 +174,18 @@ def after_request(response: flask.Response):
136174
if response.status_code != 200 and response.status_code != 404:
137175
return response
138176

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+
139189
token = request.headers["X-CURSUS-API-TOKEN"]
140190
cache_item = cache.get(token)
141191

cursus/apis/university.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,6 @@ def university_index():
2121
def university_find():
2222
"""University find endpoint with query string"""
2323

24-
if flask.request.method != "GET" or flask.request.method != "OPTIONS":
25-
raise CursusException.MethodNotAllowedError(
26-
"This endpoint only accepts GET requests"
27-
)
28-
2924
# Get request query string
3025
query_string = flask.request.query_string.decode("utf-8")
3126

0 commit comments

Comments
 (0)