]>
git.gir.st - subscriptionfeed.git/blob - app/__init__.py
4 from flask
import Flask
6 from . common
. common
import *
7 from . common
. user
import init_login
10 app
. secret_key
= base64
. b64decode ( cf
[ 'frontend' ]. get ( 'secret_key' , '' )) or \
11 secrets
. token_bytes ( 16 ) # development fallback; CSRF/cookies won't persist.
14 for name
in cf
[ 'frontend' ][ 'modules' ]. split ( ',' ):
15 blueprint
= importlib
. import_module ( '.' + name
, __name__
)
16 app
. register_blueprint ( blueprint
. frontend
)
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
]
30 from werkzeug
. urls
import url_encode
31 return url_encode ( tmp
)
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
)
39 if request
. method
== "POST" :
40 app
. logger
. error ( request
. data
)
41 if 'api_requests' in g
:
42 app
. logger
. error ( g
. api_requests
)
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.
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)
58 from flask
import 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
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