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.
Files changed (32) hide show
  1. udata/api/__init__.py +1 -35
  2. udata/app.py +2 -1
  3. udata/core/reports/api.py +2 -2
  4. udata/cors.py +99 -0
  5. udata/routing.py +1 -1
  6. udata/static/chunks/{11.52e531c19f8de80c00cf.js → 11.f6ed628667c44bbad757.js} +3 -3
  7. udata/static/chunks/{11.52e531c19f8de80c00cf.js.map → 11.f6ed628667c44bbad757.js.map} +1 -1
  8. udata/static/chunks/{13.c3343a7f1070061c0e10.js → 13.a535d3f0dc1690137ebf.js} +2 -2
  9. udata/static/chunks/{13.c3343a7f1070061c0e10.js.map → 13.a535d3f0dc1690137ebf.js.map} +1 -1
  10. udata/static/chunks/{16.8fa42440ad75ca172e6d.js → 16.e499a69d451e8620b796.js} +2 -2
  11. udata/static/chunks/{16.8fa42440ad75ca172e6d.js.map → 16.e499a69d451e8620b796.js.map} +1 -1
  12. udata/static/chunks/{19.9c6c8412729cd6d59cfa.js → 19.7d96d075450c6a7f843e.js} +3 -3
  13. udata/static/chunks/{19.9c6c8412729cd6d59cfa.js.map → 19.7d96d075450c6a7f843e.js.map} +1 -1
  14. udata/static/chunks/{5.71d15c2e4f21feee2a9a.js → 5.a1a6164f9161d24acda1.js} +3 -3
  15. udata/static/chunks/{5.71d15c2e4f21feee2a9a.js.map → 5.a1a6164f9161d24acda1.js.map} +1 -1
  16. udata/static/chunks/{6.9139dc098b8ea640b890.js → 6.7d18ddefd50912448615.js} +3 -3
  17. udata/static/chunks/{6.9139dc098b8ea640b890.js.map → 6.7d18ddefd50912448615.js.map} +1 -1
  18. udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js → 9.47a0060ef619922385ad.js} +2 -2
  19. udata/static/chunks/{9.ba6c5ed42a4b3d1de13a.js.map → 9.47a0060ef619922385ad.js.map} +1 -1
  20. udata/static/common.js +1 -1
  21. udata/static/common.js.map +1 -1
  22. udata/tests/api/__init__.py +3 -0
  23. udata/tests/api/test_base_api.py +12 -12
  24. udata/tests/api/test_reports_api.py +7 -3
  25. udata/tests/plugin.py +3 -0
  26. udata/tests/test_cors.py +62 -0
  27. {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/METADATA +6 -2
  28. {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/RECORD +32 -30
  29. {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/LICENSE +0 -0
  30. {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/WHEEL +0 -0
  31. {udata-9.1.2.dev30382.dist-info → udata-9.1.2.dev30472.dist-info}/entry_points.txt +0 -0
  32. {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
- report.by = current_user._get_current_object()
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, code=204 if request.method == "OPTIONS" else 308)
215
+ return redirect(new_url, 308)
216
216
 
217
217
 
218
218
  def init_app(app):