]> git.gir.st - subscriptionfeed.git/blob - app/__init__.py
better pagination
[subscriptionfeed.git] / app / __init__.py
1 import base64
2 import secrets
3 import importlib
4 from flask import Flask
5
6 from .common.common import *
7 from .common.user import init_login
8
9 app = Flask(__name__)
10 app.secret_key = base64.b64decode(cf['frontend'].get('secret_key','')) or \
11 secrets.token_bytes(16) # development fallback; CSRF/cookies won't persist.
12 init_login(app)
13
14 for name in cf['frontend']['modules'].split(','):
15 blueprint = importlib.import_module('.'+name, __name__)
16 app.register_blueprint(blueprint.frontend)
17
18 # TODO: move this somewhere else
19 @app.template_global()
20 def querystring_page(fields):
21 tmp = dict(request.args)
22 for field,what in fields.items():
23 if type(what) is tuple:
24 (plusminus, default) = what
25 tmp[field] = int(tmp.get(field, str(default))) + plusminus
26 elif type(what) is type(None):
27 if field in tmp: del tmp[field]
28 else:
29 tmp[field] = what
30 from werkzeug.urls import url_encode
31 return url_encode(tmp)
32
33 # TODO: should this go somewhere else?
34 # This error handler logs requests to external apis, and POST data. this makes debugging of api responses easier, as the request can be reconstructed and replayed.
35 from flask import g, request
36 from werkzeug.exceptions import InternalServerError
37 @app.errorhandler(InternalServerError)
38 def log_errors(e):
39 if request.method == "POST":
40 app.logger.error(request.data)
41 if 'api_requests' in g:
42 app.logger.error(g.api_requests)
43 return e
44
45 # TODO: build a proper flask extension
46 # Magic CSRF protection: This modifies outgoing HTML responses and injects a csrf token into all forms.
47 # All post requests are then checked if they contain the valid token.
48 # TODO:
49 # - don't use regex for injecting
50 # - inject a http header into all responses (that could be used by apis)
51 # - allow csrf token to be passed in http header, json, ...
52 # - a decorator on routes to opt out of verification or output munging
53 # https://stackoverflow.com/questions/19574694/flask-hit-decorator-before-before-request-signal-fires
54 # - allow specifying hmac message contents (currently request.remote_addr)
55 import re
56 import hmac
57 import hashlib
58 from flask import request
59 @app.after_request
60 def add_csrf_protection(response):
61 if response.mimetype == "text/html":
62 token = hmac.new(app.secret_key, request.remote_addr.encode('ascii'), hashlib.sha256).hexdigest() # TODO: will fail behind reverse proxy (remote_addr always localhost)
63 response.set_data( re.sub(
64 rb'''(<[Ff][Oo][Rr][Mm](\s+[a-zA-Z0-9-]+(=(\w*|'[^']*'|"[^"]*"))?)*>)''', # match form tags with any number of attributes and any type of quotes
65 rb'\1<input type="hidden" name="csrf" value="'+token.encode('ascii')+rb'">', # hackily append a hidden input with our csrf protection value
66 response.get_data()))
67 return response
68 @app.before_request
69 def verify_csrf_protection():
70 token = hmac.new(app.secret_key, request.remote_addr.encode('ascii'), hashlib.sha256).hexdigest() # TODO: will fail behind reverse proxy (remote_addr always localhost)
71 if request.method == "POST" and request.form.get('csrf') != token:
72 return "CSRF validation failed!", 400
73 request.form = request.form.copy() # make it mutable
74 request.form.poplist('csrf') # remove our csrf again
Imprint / Impressum