]> git.gir.st - subscriptionfeed.git/blob - app/webhooks/__init__.py
move anticsrf out of __init__, provide decorator for opting out
[subscriptionfeed.git] / app / webhooks / __init__.py
1 """
2 This is the webhook server that interfaces with Google's WebSub (formerly
3 pubsubhubbub) server. This can be run either as a module on the main frontend,
4 or as its own server, so it can live on a different host than the frontend.
5 """
6
7 import time
8 import sqlite3
9 from flask import Flask, Blueprint, request
10 from urllib.parse import parse_qs, urlparse
11
12 from ..common.common import *
13 from ..common.anticsrf import no_csrf_protection
14
15 frontend = Blueprint('webhooks', __name__)
16
17 def app():
18 app = Flask(__name__)
19 app.register_blueprint(frontend)
20 return app
21
22 @frontend.route('/websub')
23 def index():
24 return '', 204 # No Content
25
26 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["GET"])
27 def websub(timestamp, nonce, subject, sig):
28 hmackey = cf['websub']['hmac_key']
29 mode = request.args.get('hub.mode', '')
30 topic = request.args.get('hub.topic', '')
31 challenge = request.args.get('hub.challenge', '')
32 until = int(request.args.get('hub.lease_seconds', '0'))
33
34 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
35 return '', 400
36
37 if time.time() - timestamp > int(cf['websub']['lease']):
38 return '', 400
39
40 if mode != "subscribe":
41 return '', 200
42 # Note: channels are not purged from the websub dbtable.
43
44 with sqlite3.connect(cf['global']['database']) as conn:
45 c = conn.cursor()
46 c.execute("""
47 INSERT OR REPLACE INTO websub (channel_id, subscribed_until)
48 VALUES (?, datetime(?, 'unixepoch'))
49 """, (subject, time.time()+until))
50 return challenge, 200
51
52 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["POST"])
53 @no_csrf_protection
54 def websub_post(timestamp, nonce, subject, sig):
55 lease = cf['websub']['lease']
56 hmackey = cf['websub']['hmac_key']
57 hub_signature = request.headers.get('X-Hub-Signature').replace("sha1=", "")
58
59 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
60 return '', 400
61 if hub_signature != websub_body_hmac(hmackey, request.data):
62 return '', 400
63
64 with sqlite3.connect(cf['global']['database']) as conn:
65 c = conn.cursor()
66 try:
67 update_channel(conn, request.data, from_webhook=True)
68 except Exception as e:
69 with open('/tmp/websub-subscriptions.err', 'ab') as f:
70 f.write(f"<!-- {time.ctime()} ({int(time.time())}) -->\n".encode('ascii'))
71 f.write(request.data + b"\n")
72 raise e
73 return '', 200
74
75 if __name__ == '__main__':
76 app().run(debug=True)
Imprint / Impressum