]> git.lizzy.rs Git - cheatdb.git/blob - app/blueprints/github/__init__.py
6bf63a78c0c8abfaa18efb97daab083287ce6c17
[cheatdb.git] / app / blueprints / github / __init__.py
1 # Content DB
2 # Copyright (C) 2018  rubenwardy
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17 from flask import Blueprint
18
19 bp = Blueprint("github", __name__)
20
21 from flask import redirect, url_for, request, flash, abort
22 from flask_user import current_user
23 from sqlalchemy import func
24 from flask_github import GitHub
25 from app import github, csrf
26 from app.models import db, User, APIToken, Package
27 from app.utils import loginUser
28 from app.blueprints.api.support import error, handleCreateRelease
29 import hmac
30
31 @bp.route("/github/start/")
32 def start():
33         return github.authorize("")
34
35 @bp.route("/github/callback/")
36 @github.authorized_handler
37 def callback(oauth_token):
38         next_url = request.args.get("next")
39         if oauth_token is None:
40                 flash("Authorization failed [err=gh-oauth-login-failed]", "danger")
41                 return redirect(url_for("user.login"))
42
43         import requests
44
45         # Get Github username
46         url = "https://api.github.com/user"
47         r = requests.get(url, headers={"Authorization": "token " + oauth_token})
48         username = r.json()["login"]
49
50         # Get user by github username
51         userByGithub = User.query.filter(func.lower(User.github_username) == func.lower(username)).first()
52
53         # If logged in, connect
54         if current_user and current_user.is_authenticated:
55                 if userByGithub is None:
56                         current_user.github_username = username
57                         db.session.commit()
58                         flash("Linked github to account", "success")
59                         return redirect(url_for("homepage.home"))
60                 else:
61                         flash("Github account is already associated with another user", "danger")
62                         return redirect(url_for("homepage.home"))
63
64         # If not logged in, log in
65         else:
66                 if userByGithub is None:
67                         flash("Unable to find an account for that Github user", "danger")
68                         return redirect(url_for("users.claim"))
69                 elif loginUser(userByGithub):
70                         if not current_user.hasPassword():
71                                 return redirect(next_url or url_for("users.set_password", optional=True))
72                         else:
73                                 return redirect(next_url or url_for("homepage.home"))
74                 else:
75                         flash("Authorization failed [err=gh-login-failed]", "danger")
76                         return redirect(url_for("user.login"))
77
78
79 @bp.route("/github/webhook/", methods=["POST"])
80 @csrf.exempt
81 def webhook():
82         json = request.json
83
84         # Get package
85         github_url = "github.com/" + json["repository"]["full_name"]
86         package = Package.query.filter(Package.repo.like("%{}%".format(github_url))).first()
87         if package is None:
88                 return error(400, "Unknown package")
89
90         # Get all tokens for package
91         possible_tokens = APIToken.query.filter_by(package=package).all()
92         actual_token = None
93
94         #
95         # Check signature
96         #
97
98         header_signature = request.headers.get('X-Hub-Signature')
99         if header_signature is None:
100                 return error(403, "Expected payload signature")
101
102         sha_name, signature = header_signature.split('=')
103         if sha_name != 'sha1':
104                 return error(403, "Expected SHA1 payload signature")
105
106         for token in possible_tokens:
107                 mac = hmac.new(token.access_token.encode("utf-8"), msg=request.data, digestmod='sha1')
108
109                 if hmac.compare_digest(str(mac.hexdigest()), signature):
110                         actual_token = token
111                         break
112
113         if actual_token is None:
114                 return error(403, "Invalid authentication")
115
116         #
117         # Check event
118         #
119
120         event = request.headers.get("X-GitHub-Event")
121         if event == "push":
122                 title = json["head_commit"]["message"].partition("\n")[0]
123                 ref = json["after"]
124         else:
125                 return error(400, "Unknown event, expected 'push'")
126
127         #
128         # Perform release
129         #
130
131         return handleCreateRelease(actual_token, package, title, ref)