udata 9.1.2.dev30382__py2.py3-none-any.whl → 9.1.2.dev30472__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- udata/api/__init__.py +1 -35
- udata/app.py +2 -1
- udata/core/reports/api.py +2 -2
- udata/cors.py +99 -0
- udata/routing.py +1 -1
- udata/static/chunks/{11.52e531c19f8de80c00cf.js → 11.f6ed628667c44bbad757.js} +3 -3
- udata/static/chunks/{11.52e531c19f8de80c00cf.js.map → 11.f6ed628667c44bbad757.js.map} +1 -1
- udata/static/chunks/{13.c3343a7f1070061c0e10.js → 13.a535d3f0dc1690137ebf.js} +2 -2
- udata/static/chunks/{13.c3343a7f1070061c0e10.js.map → 13.a535d3f0dc1690137ebf.js.map} +1 -1
- udata/static/chunks/{16.8fa42440ad75ca172e6d.js → 16.e499a69d451e8620b796.js} +2 -2
- udata/static/chunks/{16.8fa42440ad75ca172e6d.js.map → 16.e499a69d451e8620b796.js.map} +1 -1
- udata/static/chunks/{19.9c6c8412729cd6d59cfa.js → 19.7d96d075450c6a7f843e.js} +3 -3
- udata/static/chunks/{19.9c6c8412729cd6d59cfa.js.map → 19.7d96d075450c6a7f843e.js.map} +1 -1
- udata/static/chunks/{5.71d15c2e4f21feee2a9a.js → 5.a1a6164f9161d24acda1.js} +3 -3
- udata/static/chunks/{5.71d15c2e4f21feee2a9a.js.map → 5.a1a6164f9161d24acda1.js.map} +1 -1
- udata/static/chunks/{6.9139dc098b8ea640b890.js → 6.7d18ddefd50912448615.js} +3 -3
- udata/static/chunks/{6.9139dc098b8ea640b890.js.map → 6.7d18ddefd50912448615.js.map} +1 -1
- udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js → 9.47a0060ef619922385ad.js} +2 -2
- udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js.map → 9.47a0060ef619922385ad.js.map} +1 -1
- udata/static/common.js +1 -1
- udata/static/common.js.map +1 -1
- udata/tests/api/__init__.py +3 -0
- udata/tests/api/test_base_api.py +12 -12
- udata/tests/api/test_reports_api.py +7 -3
- udata/tests/plugin.py +3 -0
- udata/tests/test_cors.py +62 -0
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/METADATA +6 -2
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/RECORD +32 -30
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/LICENSE +0 -0
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/WHEEL +0 -0
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/entry_points.txt +0 -0
- {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/top_level.txt +0 -0
udata/api/__init__.py
CHANGED
|
@@ -15,11 +15,10 @@ from flask import (
|
|
|
15
15
|
request,
|
|
16
16
|
url_for,
|
|
17
17
|
)
|
|
18
|
-
from flask_cors import CORS
|
|
19
18
|
from flask_restx import Api, Resource
|
|
20
19
|
from flask_storage import UnauthorizedFileType
|
|
21
20
|
|
|
22
|
-
from udata import entrypoints, tracking
|
|
21
|
+
from udata import cors, entrypoints, tracking
|
|
23
22
|
from udata.app import csrf
|
|
24
23
|
from udata.auth import Permission, PermissionDenied, RoleNeed, current_user, login_user
|
|
25
24
|
from udata.i18n import get_locale
|
|
@@ -37,38 +36,6 @@ apiv2_blueprint = Blueprint("apiv2", __name__, url_prefix="/api/2")
|
|
|
37
36
|
DEFAULT_PAGE_SIZE = 50
|
|
38
37
|
HEADER_API_KEY = "X-API-KEY"
|
|
39
38
|
|
|
40
|
-
# TODO: make upstream flask-restplus automatically handle
|
|
41
|
-
# flask-restplus headers and allow lazy evaluation
|
|
42
|
-
# of headers (ie. callable)
|
|
43
|
-
PREFLIGHT_HEADERS = (
|
|
44
|
-
HEADER_API_KEY,
|
|
45
|
-
"X-Fields",
|
|
46
|
-
"Content-Type",
|
|
47
|
-
"Accept",
|
|
48
|
-
"Accept-Charset",
|
|
49
|
-
"Accept-Language",
|
|
50
|
-
"Authorization",
|
|
51
|
-
"Cache-Control",
|
|
52
|
-
"Content-Encoding",
|
|
53
|
-
"Content-Length",
|
|
54
|
-
"Content-Security-Policy",
|
|
55
|
-
"Content-Type",
|
|
56
|
-
"Cookie",
|
|
57
|
-
"ETag",
|
|
58
|
-
"Host",
|
|
59
|
-
"If-Modified-Since",
|
|
60
|
-
"Keep-Alive",
|
|
61
|
-
"Last-Modified",
|
|
62
|
-
"Origin",
|
|
63
|
-
"Referer",
|
|
64
|
-
"User-Agent",
|
|
65
|
-
"X-Forwarded-For",
|
|
66
|
-
"X-Forwarded-Port",
|
|
67
|
-
"X-Forwarded-Proto",
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
cors = CORS(allow_headers=PREFLIGHT_HEADERS)
|
|
71
|
-
|
|
72
39
|
|
|
73
40
|
class UDataApi(Api):
|
|
74
41
|
def __init__(self, app=None, **kwargs):
|
|
@@ -367,4 +334,3 @@ def init_app(app):
|
|
|
367
334
|
from udata.api.oauth2 import init_app as oauth2_init_app
|
|
368
335
|
|
|
369
336
|
oauth2_init_app(app)
|
|
370
|
-
cors.init_app(app)
|
udata/app.py
CHANGED
|
@@ -13,7 +13,7 @@ from flask_wtf.csrf import CSRFProtect
|
|
|
13
13
|
from speaklater import is_lazy_string
|
|
14
14
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
|
15
15
|
|
|
16
|
-
from udata import entrypoints
|
|
16
|
+
from udata import cors, entrypoints
|
|
17
17
|
|
|
18
18
|
APP_NAME = __name__.split(".")[0]
|
|
19
19
|
ROOT_DIR = abspath(join(dirname(__file__)))
|
|
@@ -214,6 +214,7 @@ def register_extensions(app):
|
|
|
214
214
|
tasks,
|
|
215
215
|
)
|
|
216
216
|
|
|
217
|
+
cors.init_app(app)
|
|
217
218
|
tasks.init_app(app)
|
|
218
219
|
i18n.init_app(app)
|
|
219
220
|
mongo.init_app(app)
|
udata/core/reports/api.py
CHANGED
|
@@ -21,13 +21,13 @@ class ReportsAPI(API):
|
|
|
21
21
|
|
|
22
22
|
return Report.apply_sort_filters_and_pagination(query)
|
|
23
23
|
|
|
24
|
-
@api.secure
|
|
25
24
|
@api.doc("create_report", responses={400: "Validation error"})
|
|
26
25
|
@api.expect(Report.__write_fields__)
|
|
27
26
|
@api.marshal_with(Report.__read_fields__, code=201)
|
|
28
27
|
def post(self):
|
|
29
28
|
report = patch(Report(), request)
|
|
30
|
-
|
|
29
|
+
if current_user.is_authenticated:
|
|
30
|
+
report.by = current_user._get_current_object()
|
|
31
31
|
|
|
32
32
|
try:
|
|
33
33
|
report.save()
|
udata/cors.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from flask import request
|
|
4
|
+
from werkzeug.datastructures import Headers
|
|
5
|
+
|
|
6
|
+
log = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def add_vary(headers: Headers, header: str):
|
|
10
|
+
values = headers.getlist("Vary")
|
|
11
|
+
if header not in values:
|
|
12
|
+
values.append(header)
|
|
13
|
+
headers.set("Vary", ", ".join(values))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def add_actual_request_headers(headers: Headers) -> Headers:
|
|
17
|
+
origin = request.headers.get("Origin", None)
|
|
18
|
+
if origin:
|
|
19
|
+
headers.set("Access-Control-Allow-Origin", origin)
|
|
20
|
+
add_vary(headers, "Origin")
|
|
21
|
+
|
|
22
|
+
headers.set("Access-Control-Allow-Credentials", "true")
|
|
23
|
+
|
|
24
|
+
return headers
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_preflight_request() -> bool:
|
|
28
|
+
return (
|
|
29
|
+
request.method == "OPTIONS"
|
|
30
|
+
and request.headers.get("Access-Control-Request-Method", None) is not None
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def is_allowed_cors_route():
|
|
35
|
+
return (
|
|
36
|
+
request.path.endswith((".js", ".css", ".woff", ".woff2", ".png", ".jpg", ".jpeg", ".svg"))
|
|
37
|
+
or request.path.startswith("/api")
|
|
38
|
+
or request.path.startswith("/oauth")
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def add_preflight_request_headers(headers: Headers) -> Headers:
|
|
43
|
+
origin = request.headers.get("Origin", None)
|
|
44
|
+
if origin:
|
|
45
|
+
headers.set("Access-Control-Allow-Origin", origin)
|
|
46
|
+
add_vary(headers, "Origin")
|
|
47
|
+
|
|
48
|
+
headers.set("Access-Control-Allow-Credentials", "true")
|
|
49
|
+
|
|
50
|
+
# The API allows all methods, so just copy the browser requested methods from the request headers.
|
|
51
|
+
headers.set(
|
|
52
|
+
"Access-Control-Allow-Methods", request.headers.get("Access-Control-Request-Method", "")
|
|
53
|
+
)
|
|
54
|
+
add_vary(headers, "Access-Control-Request-Method")
|
|
55
|
+
|
|
56
|
+
headers.set(
|
|
57
|
+
"Access-Control-Allow-Headers",
|
|
58
|
+
request.headers.get("Access-Control-Request-Headers", ""),
|
|
59
|
+
)
|
|
60
|
+
add_vary(headers, "Access-Control-Request-Headers")
|
|
61
|
+
|
|
62
|
+
return headers
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def init_app(app):
|
|
66
|
+
"""
|
|
67
|
+
The CORS should be enabled before routing to trigger before some redirects.
|
|
68
|
+
|
|
69
|
+
For OPTIONS requests, we do not call the backend API code at all.
|
|
70
|
+
We just return the access-control headers.
|
|
71
|
+
|
|
72
|
+
For other requests, we append the access-control headers to the original
|
|
73
|
+
response.
|
|
74
|
+
|
|
75
|
+
This module is inspired by:
|
|
76
|
+
- the CORS logic https://github.com/fruitcake/php-cors/blob/master/src/CorsService.php
|
|
77
|
+
- the middleware https://github.com/laravel/framework/blob/11.x/src/Illuminate/Http/Middleware/HandleCors.php
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
@app.before_request
|
|
81
|
+
def bypass_code_for_options_requests():
|
|
82
|
+
if not is_allowed_cors_route():
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
if is_preflight_request():
|
|
86
|
+
headers = add_preflight_request_headers(Headers())
|
|
87
|
+
return "", 204, headers
|
|
88
|
+
|
|
89
|
+
@app.after_request
|
|
90
|
+
def add_cors_headers(response):
|
|
91
|
+
if not is_allowed_cors_route():
|
|
92
|
+
return response
|
|
93
|
+
|
|
94
|
+
if request.method == "OPTIONS":
|
|
95
|
+
add_vary(response.headers, "Access-Control-Request-Method")
|
|
96
|
+
|
|
97
|
+
add_actual_request_headers(response.headers)
|
|
98
|
+
|
|
99
|
+
return response
|
udata/routing.py
CHANGED
|
@@ -212,7 +212,7 @@ def lazy_raise_or_redirect():
|
|
|
212
212
|
new_args = request.view_args
|
|
213
213
|
new_args[name] = value.arg
|
|
214
214
|
new_url = url_for(request.endpoint, **new_args)
|
|
215
|
-
return redirect(new_url,
|
|
215
|
+
return redirect(new_url, 308)
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
def init_app(app):
|