gitHTTP.py
Python script, ASCII text executable
1import uuid 2 3from app import app, gitCommand, User, Repo, Commit, RepoAccess, getPermissionLevel, getVisibility, db, bcrypt 4import os 5import shutil 6import config 7import flask 8import git 9import subprocess 10from flask_httpauth import HTTPBasicAuth 11import zlib 12import re 13import datetime 14 15auth = HTTPBasicAuth(realm=config.AUTH_REALM) 16 17 18authRequired = flask.Response("Unauthorized Access", 401, {"WWW-Authenticate": 'Basic realm="Login Required"'}) 19 20 21@auth.verify_password 22def verifyPassword(username, password): 23user = User.query.filter_by(username=username).first() 24 25if user and bcrypt.check_password_hash(user.passwordHashed, password): 26flask.g.user = username 27return True 28 29return False 30 31 32@app.route("/<username>/<repository>/git-upload-pack", methods=["POST"]) 33@app.route("/git/<username>/<repository>/git-upload-pack", methods=["POST"]) 34@auth.login_required(optional=True) 35def gitUploadPack(username, repository): 36if auth.current_user() is None and not getVisibility(username, repository): 37return authRequired 38if not (getVisibility(username, repository) or getPermissionLevel(flask.g.user, username, repository) is not None): 39flask.abort(403) 40 41serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository, ".git") 42text = gitCommand(serverRepoLocation, flask.request.data, "upload-pack", "--stateless-rpc", ".") 43 44return flask.Response(text, content_type="application/x-git-upload-pack-result") 45 46 47@app.route("/<username>/<repository>/git-receive-pack", methods=["POST"]) 48@app.route("/git/<username>/<repository>/git-receive-pack", methods=["POST"]) 49@auth.login_required 50def gitReceivePack(username, repository): 51if not getPermissionLevel(flask.g.user, username, repository): 52flask.abort(403) 53 54serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository, ".git") 55text = gitCommand(serverRepoLocation, flask.request.data, "receive-pack", "--stateless-rpc", ".") 56 57sha = flask.request.data.split(b" ", 2)[1].decode() 58info = gitCommand(serverRepoLocation, None, "show", "-s", "--format='%H%n%at%n%cn <%ce>%n%B'", sha).decode() 59 60if re.match("^[0-9a-fA-F]{40}$", info[:40]): 61print(info.split("\n", 3)) 62sha, time, identity, body = info.split("\n", 3) 63login = flask.g.user 64 65if not Commit.query.filter_by(identifier=f"/{username}/{repository}/{sha}").first(): 66user = User.query.filter_by(username=login).first() 67repo = Repo.query.filter_by(route=f"/{username}/{repository}").first() 68 69commit = Commit(sha, user, repo, time, body, identity) 70 71db.session.add(commit) 72db.session.commit() 73 74return flask.Response(text, content_type="application/x-git-receive-pack-result") 75 76 77@app.route("/<username>/<repository>/info/refs", methods=["GET", "POST"]) 78@app.route("/git/<username>/<repository>/info/refs", methods=["GET", "POST"]) 79@auth.login_required(optional=True) 80def gitInfoRefs(username, repository): 81serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository, ".git") 82 83repo = git.Repo(serverRepoLocation) 84repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first() 85if not repoData.defaultBranch: 86if repo.heads: 87repoData.defaultBranch = repo.heads[0].name 88repo.git.checkout("-f", repoData.defaultBranch) 89 90if auth.current_user() is None and (not getVisibility(username, repository) or flask.request.args.get("service") == "git-receive-pack"): 91return authRequired 92try: 93if not (getVisibility(username, repository) or getPermissionLevel(flask.g.user, username, repository) is not None): 94flask.abort(403) 95except AttributeError: 96return authRequired 97 98service = flask.request.args.get("service") 99 100if service.startswith("git"): 101service = service[4:] 102else: 103flask.abort(403) 104 105if service == "receive-pack": 106try: 107if not getPermissionLevel(flask.g.user, username, repository): 108flask.abort(403) 109except AttributeError: 110return authRequired 111 112serviceLine = f"# service=git-{service}\n" 113serviceLine = (f"{len(serviceLine) + 4:04x}" + serviceLine).encode() 114 115if service == "upload-pack": 116text = serviceLine + b"0000" + gitCommand(serverRepoLocation, None, "upload-pack", "--stateless-rpc", "--advertise-refs", "--http-backend-info-refs", ".") 117elif service == "receive-pack": 118refs = gitCommand(serverRepoLocation, None, "receive-pack", "--http-backend-info-refs", ".") 119text = serviceLine + b"0000" + refs 120else: 121flask.abort(403) 122 123response = flask.Response(text, content_type=f"application/x-git-{service}-advertisement") 124response.headers["Cache-Control"] = "no-cache" 125 126return response 127 128