-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathapp_helpers.py
134 lines (103 loc) · 4.22 KB
/
app_helpers.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
import gzip
from functools import wraps
from io import BytesIO
import flask
from model import Admin
from util import GeometryUtility
from util.flask_util import originating_ip
from util.problem_detail import ProblemDetail
def has_library_factory(app):
"""Create a decorator that extracts a library uuid from request arguments."""
def factory(f):
@wraps(f)
def decorated(*args, **kwargs):
"""A decorator that extracts a library UUID from request
arguments.
"""
if "uuid" in kwargs:
uuid = kwargs.pop("uuid")
else:
uuid = None
library = app.library_registry.registry_controller.library_for_request(uuid)
if isinstance(library, ProblemDetail):
return library.response
else:
return f(*args, **kwargs)
return decorated
return factory
def uses_location_factory(app):
"""Create a decorator that guesses at a location for the client."""
def factory(f):
@wraps(f)
def decorated(*args, **kwargs):
"""A decorator that guesses at a location for the client."""
location = flask.request.args.get("_location")
if location:
location = GeometryUtility.point_from_string(location)
if not location:
ip = originating_ip()
location = GeometryUtility.point_from_ip(ip)
return f(*args, _location=location, **kwargs)
return decorated
return factory
def compressible(f):
"""Decorate a function to make it transparently handle whatever
compression the client has announced it supports.
Currently the only form of compression supported is
representation-level gzip compression requested through the
Accept-Encoding header.
This code was modified from
http://kb.sites.apiit.edu.my/knowledge-base/how-to-gzip-response-in-flask/,
though I don't know if that's the original source; it shows up in
a lot of places.
"""
@wraps(f)
def compressor(*args, **kwargs):
@flask.after_this_request
def compress(response):
if (
response.status_code < 200
or response.status_code >= 300
or "Content-Encoding" in response.headers
):
# Don't encode anything other than a 2xx response
# code. Don't encode a response that's
# already been encoded.
return response
accept_encoding = flask.request.headers.get("Accept-Encoding", "")
if "gzip" not in accept_encoding.lower():
return response
# At this point we know we're going to be changing the
# outgoing response.
# TODO: I understand what direct_passthrough does, but am
# not sure what it has to do with this, and commenting it
# out doesn't change the results or cause tests to
# fail. This is pure copy-and-paste magic.
response.direct_passthrough = False
buffer = BytesIO()
gzipped = gzip.GzipFile(mode="wb", fileobj=buffer)
gzipped.write(response.data)
gzipped.close()
response.data = buffer.getvalue()
response.headers["Content-Encoding"] = "gzip"
# TODO: This is bad if Vary is already set.
response.headers["Vary"] = "Accept-Encoding"
response.headers["Content-Length"] = len(response.data)
return response
return f(*args, **kwargs)
return compressor
def require_admin_authentication(func):
"""Test authentication on the request.
The request session should have previously authenticatated as an admin."""
@wraps(func)
def wrapper(*args, **kwargs):
if "username" in flask.session and flask.session["username"] is not None:
admin = (
flask.current_app._db.query(Admin)
.filter(Admin.username == flask.session["username"])
.first()
)
if admin:
return func(*args, **kwargs)
return flask.Response(response="Unauthorized", status=401)
return wrapper