def _webhook(record_id): record_id = UUID(record_id) hook = GitHubCommitToBuild._GitHubCommitToBuildRecord.query.filter( GitHubCommitToBuild._GitHubCommitToBuildRecord.id == record_id).one_or_none() if not hook: return "Unknown hook " + str(record_id), 404 valid = Validation(request) commit = valid.require("head_commit") repo = valid.require("repository") ref = valid.require("ref") if not valid.ok: return "Got request, but it has no commits" return submit_github_build( "commits", hook, repo, commit, env={ "GITHUB_DELIVERY": request.headers.get("X-GitHub-Delivery"), "GITHUB_EVENT": request.headers.get("X-GitHub-Event"), "GITHUB_REF": ref, "GITHUB_REPO": repo["full_name"], }, secrets=hook.secrets)
def _webhook(record_id): record_id = UUID(record_id) hook = GitHubPRToBuild._GitHubPRToBuildRecord.query.filter( GitHubPRToBuild._GitHubPRToBuildRecord.id == record_id).first() if not hook: return "Unknown hook " + str(record_id), 404 valid = Validation(request) pr = valid.require("pull_request") action = valid.require("action") if not valid.ok: return "Got request, but it has no commits" if action not in ["opened", "synchronize"]: return "Got update, but there are no new commits" head = pr["head"] base = pr["base"] base_repo = base["repo"] head_repo = head["repo"] auth = GitHubAuthorization.query.filter( GitHubAuthorization.user_id == hook.user_id).first() if not auth: return ("You have not authorized us to access your GitHub account", 401) return submit_build(hook, head_repo, head, base_repo, secrets=False, extras={ "automerge": hook.automerge, "pr": pr["number"] })
def oauth_register_POST(): valid = Validation(request) client_name = valid.require("client-name") redirect_uri = valid.require("redirect-uri") valid.expect(not redirect_uri or valid_url(redirect_uri), "Must be a valid HTTP or HTTPS URI", field="redirect-uri") if not valid.ok: return render_template("oauth-register.html", client_name=client_name, redirect_uri=redirect_uri, valid=valid) client = OAuthClient(current_user, client_name, redirect_uri) secret = client.gen_client_secret() session["client_id"] = client.client_id session["client_secret"] = secret session["client_event"] = "registered" db.session.add(client) audit_log("register oauth client", "Registered OAuth client {}".format(client.client_id)) db.session.commit() return redirect("/oauth/registered")
def _webhook(record_id): record_id = UUID(record_id) hook = GitLabCommitToBuild._GitLabCommitToBuildRecord.query.filter( GitLabCommitToBuild._GitLabCommitToBuildRecord.id == record_id ).one_or_none() if not hook: return "Unknown hook " + str(record_id), 404 auth = GitLabAuthorization.query.filter( GitLabAuthorization.user_id == hook.user_id, GitLabAuthorization.upstream == hook.upstream, ).first() if not auth: return "Invalid authorization for this hook" gitlab = Gitlab(f"https://{hook.upstream}", oauth_token=auth.oauth_token) valid = Validation(request) commit = valid.require("after") ref = valid.require("ref") if not valid.ok: return "Unexpected hook payload" try: project = gitlab.projects.get(hook.repo_id) commit = project.commits.get(commit) except: return "Unable to fetch commit information" urls = submit_gitlab_build("commits", auth, hook, project, commit, env={ "GITLAB_REF": ref, }) if isinstance(urls, str): return urls return "Submitted:\n\n" + "\n".join([f"{n}: {u}" for n, u in urls])
def tracker_labels_POST(owner, name): tracker, access = get_tracker(owner, name) is_owner = current_user.id == tracker.owner_id if not tracker: abort(404) if not is_owner: abort(403) valid = Validation(request) label_name = valid.require("name") label_color = valid.require("color") if not valid.ok: return render_template("tracker-labels.html", tracker=tracker, access=access, is_owner=is_owner, **valid.kwargs), 400 valid.expect(2 < len(label_name) < 50, "Must be between 2 and 50 characters", field="name") valid.expect(color.valid_hex_color_code(label_color), "Invalid hex color code", field="color") if not valid.ok: return render_template("tracker-labels.html", tracker=tracker, access=access, is_owner=is_owner, **valid.kwargs), 400 existing_label = (Label.query.filter( Label.tracker_id == tracker.id).filter( Label.name == label_name)).first() valid.expect(not existing_label, "A label with this name already exists", field="name") if not valid.ok: return render_template("tracker-labels.html", tracker=tracker, access=access, is_owner=is_owner, **valid.kwargs), 400 # Determine a foreground color to use label_color_rgb = color.color_from_hex(label_color) text_color_rgb = color.get_text_color(label_color_rgb) text_color = color.color_to_hex(text_color_rgb) label = Label() label.tracker_id = tracker.id label.name = label_name label.color = label_color label.text_color = text_color db.session.add(label) db.session.commit() return redirect(url_for(".tracker_labels_GET", owner=owner, name=name))
def oauth_token_POST(token): valid = Validation(request) client_id = valid.require("client_id") client_secret = valid.require("client_secret") revocation_url = valid.require("revocation_url") if not valid.ok: return valid.response client = (OAuthClient.query.filter( OAuthClient.client_id == client_id)).one_or_none() if not client: return {"errors": [{"reason": "404 not found"}]}, 404 client_secret_hash = hashlib.sha512(client_secret.encode()).hexdigest() valid.expect(client_secret_hash == client.client_secret_hash, "Invalid client secret") if not valid.ok: return valid.response h = hashlib.sha512(token.encode()).hexdigest() oauth_token = (OAuthToken.query.filter( OAuthToken.token_hash == h)).one_or_none() valid.expect( oauth_token is not None and oauth_token.expires > datetime.utcnow(), "Invalid or expired OAuth token") if not valid.ok: return valid.response rev = RevocationUrl.query\ .filter(RevocationUrl.token_id == oauth_token.id)\ .filter(RevocationUrl.client_id == client.id).first() if not rev: rev = RevocationUrl(client, oauth_token, revocation_url) db.session.add(rev) else: rev.url = revocation_url db.session.commit() if oauth_token._scopes == "*": return {"expires": oauth_token.expires, "scopes": "*"} scopes = [ str(s) for s in oauth_token.scopes if (s.client_id and s.client_id == client.client_id) or s == OAuthScope("profile:read") ] valid.expect(any(scopes), "Invalid or expired OAuth token") if not valid.ok: return valid.response scopes = ",".join(scopes) # TODO: Celery task to notify of revocation return { "expires": oauth_token.expires, "scopes": ",".join(str(s) for s in oauth_token.scopes) }
def oauth_exchange_POST(): valid = Validation(request) client_id = valid.require('client_id') client_secret = valid.require('client_secret') exchange = valid.require('exchange') if not valid.ok: return valid.response client = (OAuthClient.query.filter( OAuthClient.client_id == client_id)).one_or_none() valid.expect(client, 'Unknown client ID') if not valid.ok: return valid.response client_secret_hash = hashlib.sha512(client_secret.encode()).hexdigest() valid.expect(client_secret_hash == client.client_secret_hash, 'Invalid client secret') if not valid.ok: return valid.response stash = redis.get(exchange) valid.expect(stash, 'Exchange token expired') if not valid.ok: return valid.response stash = json.loads(stash.decode()) redis.delete(exchange) user = stash.get('user_id') scopes = stash.get('scopes') user = User.query.filter(User.id == user).first() valid.expect( user, "Unknown user ID stored for " "this exchange token (this isn't supposed to happen") if not valid.ok: return valid.response previous = (OAuthToken.query.filter(OAuthToken.user_id == user.id).filter( OAuthToken.client_id == client.id)).one_or_none() if not previous: oauth_token = OAuthToken(user, client) else: oauth_token = previous previous.expires = datetime.utcnow() + timedelta(days=365) oauth_token.scopes = [OAuthScope(s) for s in scopes.split(",")] token = oauth_token.gen_token() if not client.preauthorized: audit_log("oauth token issued", "issued oauth token {} to client {}".format( oauth_token.token_partial, client.client_id), user=user) if not previous: db.session.add(oauth_token) db.session.commit() return {"token": token, "expires": oauth_token.expires}
def jobs_POST(): valid = Validation(request) _manifest = valid.require("manifest", cls=str) max_len = Job.manifest.prop.columns[0].type.length valid.expect(not _manifest or len(_manifest) < max_len, "Manifest must be less than {} bytes".format(max_len), field="manifest") note = valid.optional("note", cls=str) read = valid.optional("access:read", ["*"], list) write = valid.optional("access:write", [current_token.user.username], list) secrets = valid.optional("secrets", cls=bool, default=True) tags = valid.optional("tags", [], list) valid.expect( all(re.match(r"^[A-Za-z0-9_.-]+$", tag) for tag in tags), "Invalid tag name, tags must use lowercase alphanumeric characters, underscores, dashes, or dots", field="tags") triggers = valid.optional("triggers", list(), list) execute = valid.optional("execute", True, bool) if not valid.ok: return valid.response try: manifest = Manifest(yaml.safe_load(_manifest)) except Exception as ex: valid.error(str(ex)) return valid.response # TODO: access controls job = Job(current_token.user, _manifest) job.note = note if tags: job.tags = "/".join(tags) job.secrets = secrets db.session.add(job) db.session.flush() for task in manifest.tasks: t = Task(job, task.name) db.session.add(t) db.session.flush() # assigns IDs for ordering purposes for index, trigger in enumerate(triggers): _valid = Validation(trigger) action = _valid.require("action", TriggerType) condition = _valid.require("condition", TriggerCondition) if not _valid.ok: _valid.copy(valid, "triggers[{}]".format(index)) return valid.response # TODO: Validate details based on trigger type t = Trigger(job) t.trigger_type = action t.condition = condition t.details = json.dumps(trigger) db.session.add(t) if execute: queue_build(job, manifest) # commits the session else: db.session.commit() return {"id": job.id}
def billing_initial_POST(): valid = Validation(request) amount = valid.require("amount") amount = int(amount) plan = valid.require("plan") valid.expect(not amount or amount > 0, "Expected amount >0") if not valid.ok: return "Invalid form submission", 400 current_user.payment_cents = amount db.session.commit() if current_user.stripe_customer: return redirect(url_for("billing.billing_GET")) return redirect(url_for("billing.new_payment_GET"))
def _webhook(record_id): record_id = UUID(record_id) hook = GitHubCommitToBuild._GitHubCommitToBuildRecord.query.filter( GitHubCommitToBuild._GitHubCommitToBuildRecord.id == record_id ).one_or_none() if not hook: return "Unknown hook " + str(record_id), 404 valid = Validation(request) commit = valid.require("head_commit") repo = valid.require("repository") if not valid.ok: return "Got request, but it has no commits" return submit_build(hook, repo, commit, secrets=hook.secrets)
def ticket_edit_POST(owner, name, ticket_id): tracker, _ = get_tracker(owner, name) if not tracker: abort(404) ticket, access = get_ticket(tracker, ticket_id) if not ticket: abort(404) if not TicketAccess.edit in access: abort(401) valid = Validation(request) title = valid.require("title", friendly_name="Title") desc = valid.optional("description") valid.expect(not title or 3 <= len(title) <= 2048, "Title must be between 3 and 2048 characters.", field="title") valid.expect(not desc or len(desc) < 16384, "Description must be no more than 16384 characters.", field="description") if not valid.ok: return render_template("edit_ticket.html", tracker=tracker, ticket=ticket, **valid.kwargs) ticket.title = title ticket.description = desc db.session.commit() return redirect(ticket_url(ticket))
def _configure_POST(github): valid = Validation(request) repo = valid.require("repo") if not valid.ok: return "quit yo hackin bullshit" repo = github.get_repo(repo) if not repo: return "quit yo hackin bullshit" task = Task() task.name = "{}::github_pr_to_build".format(repo.full_name) task.user_id = current_user.id task._taskdef = "github_pr_to_build" db.session.add(task) db.session.flush() record = GitHubPRToBuild._GitHubPRToBuildRecord() record.id = uuid4() record.user_id = current_user.id record.task_id = task.id record.github_webhook_id = -1 record.repo = repo.full_name db.session.add(record) db.session.flush() hook = repo.create_hook("web", { "url": _root + url_for("github_pr_to_build._webhook", record_id=record.id), "content_type": "json", }, ["pull_request"], active=True) record.github_webhook_id = hook.id db.session.commit() return redirect(url_for("html.edit_task", task_id=task.id))
def forgot_POST(): valid = Validation(request) email = valid.require("email", friendly_name="Email") if not valid.ok: return render_template("forgot.html", **valid.kwargs) user = User.query.filter(User.email == email).first() valid.expect(user, "No account found with this email address.") if not valid.ok: return render_template("forgot.html", **valid.kwargs) factors = (UserAuthFactor.query.filter( UserAuthFactor.user_id == user.id)).all() valid.expect( not any(f for f in factors if f.factor_type in [FactorType.totp, FactorType.u2f]), "This account has two-factor authentication enabled, contact support.") if not valid.ok: return render_template("forgot.html", **valid.kwargs) rh = user.gen_reset_hash() db.session.commit() send_email( 'reset_pw', user.email, 'Reset your password on {}'.format(site_name), headers={ "From": f"{cfg('sr.ht', 'owner-name')} <*****@*****.**>", "To": "{} <{}>".format(user.username, user.email), "Reply-To": f"{cfg('sr.ht', 'owner-name')} <{cfg('sr.ht', 'owner-email')}>", }, user=user) audit_log("password reset requested", user=user) return render_template("forgot.html", done=True)
def submit_POST(): valid = Validation(request) _manifest = valid.require("manifest", friendly_name="Manifest") max_len = Job.manifest.prop.columns[0].type.length note = valid.optional("note", default="Submitted on the web") valid.expect(not _manifest or len(_manifest) < max_len, "Manifest must be less than {} bytes".format(max_len), field="manifest") if not valid.ok: return render_template("submit.html", **valid.kwargs) try: manifest = Manifest(yaml.safe_load(_manifest)) except Exception as ex: valid.error(str(ex), field="manifest") return render_template("submit.html", **valid.kwargs) job = Job(current_user, _manifest) job.note = note db.session.add(job) db.session.flush() for task in manifest.tasks: t = Task(job, task.name) db.session.add(t) db.session.flush() # assigns IDs for ordering purposes queue_build(job, manifest) # commits the session return redirect("/~" + current_user.username + "/job/" + str(job.id))
def ssh_keys_POST(): user = User.query.get(current_user.id) valid = Validation(request) ssh_key = valid.require("ssh-key") if valid.ok: try: parsed_key = ssh.SSHKey(ssh_key) valid.expect(parsed_key.bits, "This is not a valid SSH key", "ssh-key") except: valid.error("This is not a valid SSH key", "ssh-key") if valid.ok: fingerprint = parsed_key.hash_md5()[4:] valid.expect(SSHKey.query\ .filter(SSHKey.fingerprint == fingerprint) \ .count() == 0, "We already have this SSH key on file.", "ssh-key") if not valid.ok: return render_template("keys.html", current_user=user, ssh_key=ssh_key, valid=valid) key = SSHKey(user, ssh_key, fingerprint, parsed_key.comment) db.session.add(key) audit_log("ssh key added", 'Added SSH key {}'.format(fingerprint)) db.session.commit() return redirect("/keys")
def _webhook(record_id): record_id = UUID(record_id) hook = GitHubPRToBuild._GitHubPRToBuildRecord.query.filter( GitHubPRToBuild._GitHubPRToBuildRecord.id == record_id).first() if not hook: return "Unknown hook " + str(record_id), 404 valid = Validation(request) pr = valid.require("pull_request") action = valid.require("action") if not valid.ok: return "Got request, but it has no commits" if action not in ["opened", "synchronize"]: return "Got update, but there are no new commits" head = pr["head"] base = pr["base"] base_repo = base["repo"] head_repo = head["repo"] auth = GitHubAuthorization.query.filter( GitHubAuthorization.user_id == hook.user_id).first() if not auth: return ("You have not authorized us to access your GitHub account", 401) secrets = hook.secrets if not base_repo["private"]: secrets = False return submit_github_build( "pulls", hook, head_repo, head, base_repo, secrets=secrets, extras={ "automerge": hook.automerge, "pr": pr["number"] }, env={ "GITHUB_DELIVERY": request.headers.get("X-GitHub-Delivery"), "GITHUB_EVENT": request.headers.get("X-GitHub-Event"), "GITHUB_PR_NUMBER": str(pr["number"]), "GITHUB_PR_TITLE": pr["title"], "GITHUB_BASE_REPO": base_repo["full_name"], "GITHUB_HEAD_REPO": head_repo["full_name"], })
def new_payment_POST(): valid = Validation(request) term = valid.require("term") token = valid.require("stripe-token") if not valid.ok: return "Invalid form submission", 400 if not current_user.stripe_customer: new_customer = True try: customer = stripe.Customer.create( description="~" + current_user.username, email=current_user.email, card=token) current_user.stripe_customer = customer.id current_user.payment_due = datetime.utcnow() + timedelta(seconds=-1) except stripe.error.CardError as e: details = e.json_body["error"]["message"] return render_template("new-payment.html", amount=current_user.payment_cents, error=details) else: new_customer = False try: customer = stripe.Customer.retrieve(current_user.stripe_customer) source = customer.sources.create(source=token) customer.default_source = source.stripe_id customer.save() except stripe.error.CardError as e: details = e.json_body["error"]["message"] return render_template("new-payment.html", amount=current_user.payment_cents, error=details) audit_log("billing", "New payment method handed") current_user.payment_interval = PaymentInterval(term) success, details = charge_user(current_user) if not success: return render_template("new-payment.html", amount=current_user.payment_cents, error=details) db.session.commit() if new_customer: return redirect(onboarding_redirect) session["message"] = "Your payment method was updated." return redirect(url_for("billing.billing_GET"))
def security_totp_enable_POST(): valid = Validation(request) secret = valid.require("secret") code = valid.require("code") if not valid.ok: return render_template("totp-enable.html", qrcode=totp_get_qrcode(secret), otpauth_uri=otpauth_uri(secret), secret=secret, valid=valid), 400 code = code.replace(" ", "") try: code = int(code) except: valid.error( "This TOTP code is invalid (expected a number)", field="code") if not valid.ok: return render_template("totp-enable.html", qrcode=totp_get_qrcode(secret), otpauth_uri=otpauth_uri(secret), secret=secret, valid=valid), 400 valid.expect(totp(secret, code), "The code you entered is incorrect.", field="code") if not valid.ok: return render_template("totp-enable.html", qrcode=totp_get_qrcode(secret), otpauth_uri=otpauth_uri(secret), secret=secret, valid=valid), 400 factor = UserAuthFactor(current_user, FactorType.totp) factor.secret = secret.encode('utf-8') db.session.add(factor) audit_log("enabled two factor auth", 'Enabled TOTP') db.session.commit() metrics.meta_totp_enabled.inc() return redirect("/security")
def totp_challenge_POST(): user_id = session.get('authorized_user') factors = session.get('extra_factors') return_to = session.get('return_to') or '/' if not user_id or not factors: return redirect("/login") valid = Validation(request) code = valid.require('code') if not valid.ok: return render_template("totp-challenge.html", return_to=return_to, valid=valid) code = code.replace(" ", "") try: code = int(code) except: valid.error("This TOTP code is invalid (expected a number)", field="code") if not valid.ok: return render_template("totp-challenge.html", return_to=return_to, valid=valid) factor = UserAuthFactor.query.get(factors[0]) secret = factor.secret.decode('utf-8') valid.expect(totp(secret, code), 'The code you entered is incorrect.', field='code') if not valid.ok: return render_template("totp-challenge.html", valid=valid, return_to=return_to) factors = factors[1:] if len(factors) != 0: return get_challenge(factors[0]) del session['authorized_user'] del session['extra_factors'] del session['return_to'] user = User.query.get(user_id) login_user(user, remember=True) audit_log("logged in") db.session.commit() metrics.meta_logins_success.inc() return redirect(return_to)
def create_POST(): valid = Validation(request) name = valid.require("tracker_name", friendly_name="Name") desc = valid.optional("tracker_desc") if not valid.ok: return render_template("tracker-create.html", **valid.kwargs), 400 valid.expect(2 < len(name) < 256, "Must be between 2 and 256 characters", field="tracker_name") valid.expect(not valid.ok or name[0] in string.ascii_lowercase, "Must begin with a lowercase letter", field="tracker_name") valid.expect(not valid.ok or name_re.match(name), "Only lowercase alphanumeric characters or -.", field="tracker_name") valid.expect(not desc or len(desc) < 4096, "Must be less than 4096 characters", field="tracker_desc") if not valid.ok: return render_template("tracker-create.html", **valid.kwargs), 400 tracker = (Tracker.query.filter( Tracker.owner_id == current_user.id).filter( Tracker.name == name)).first() valid.expect(not tracker, "A tracker by this name already exists", field="tracker_name") if not valid.ok: return render_template("tracker-create.html", **valid.kwargs), 400 tracker = Tracker() tracker.owner_id = current_user.id tracker.name = name tracker.description = desc db.session.add(tracker) db.session.flush() sub = TicketSubscription() sub.tracker_id = tracker.id sub.user_id = current_user.id db.session.add(sub) db.session.commit() if "create-configure" in valid: return redirect( url_for(".settings_details_GET", owner=current_user.username, name=name)) return redirect(tracker_url(tracker))
def delegate_scopes_POST(): valid = Validation(request) desc = valid.require("description", cls=str) name = valid.require("name", cls=str) writable = valid.require("writable", cls=bool) valid.expect(not name or re.match(r"^[a-z_]+$", name), "Lowercase characters and underscores only", field="name") if not valid.ok: return valid.response scope = (DelegatedScope.query\ .filter(DelegatedScope.client_id == g.oauth_client.id) .filter(DelegatedScope.name == name)).one_or_none() valid.expect(scope is None, "A scope with this name already exists.", field="name") if not valid.ok: return valid.response scope = DelegatedScope(g.oauth_client, name, desc) scope.write = writable db.session.add(scope) db.session.commit() return scope.to_dict()
def ticket_add_label(owner, name, ticket_id): tracker, _ = get_tracker(owner, name) if not tracker: abort(404) ticket, access = get_ticket(tracker, ticket_id) if not ticket: abort(404) if not TicketAccess.edit in access: abort(401) valid = Validation(request) label_id = valid.require("label_id", friendly_name="A label") if not valid.ok: ctx = get_ticket_context(ticket, tracker, access) return render_template("ticket.html", **ctx, **valid.kwargs) valid.expect(re.match(r"^\d+$", label_id), "Label ID must be numeric", field="label_id") if not valid.ok: ctx = get_ticket_context(ticket, tracker, access) return render_template("ticket.html", **ctx, **valid.kwargs) label_id = int(request.form.get('label_id')) label = Label.query.filter(Label.id == label_id).first() if not label: abort(404) ticket_label = (TicketLabel.query.filter( TicketLabel.label_id == label.id).filter( TicketLabel.ticket_id == ticket.id)).first() if not ticket_label: ticket_label = TicketLabel() ticket_label.ticket_id = ticket.id ticket_label.label_id = label.id ticket_label.user_id = current_user.id event = Event() event.event_type = EventType.label_added event.user_id = current_user.id event.ticket_id = ticket.id event.label_id = label.id db.session.add(ticket_label) db.session.add(event) db.session.commit() return redirect(ticket_url(ticket))
def pgp_keys_POST(): user = User.query.get(current_user.id) valid = Validation(request) pgp_key = valid.require("pgp-key") valid.expect( not pgp_key or len(pgp_key) < 32768, Markup("Maximum encoded key length is 32768 bytes. " "Try <br /><code>gpg --armor --export-options export-minimal " "--export <fingerprint></code><br /> to export a " "smaller key."), field="pgp-key") if valid.ok: try: key = pgpy.PGPKey() key.parse(pgp_key.replace('\r', '').encode('utf-8')) except: valid.error("This is not a valid PGP key", field="pgp-key") valid.expect(any(key.userids), "This key has no user IDs", field="pgp-key") try: prepare_email("test", user.email, "test", encrypt_key=pgp_key) except: valid.error( "We were unable to encrypt a test message with this key", field="pgp-key") if valid.ok: valid.expect(PGPKey.query\ .filter(PGPKey.user_id == user.id) \ .filter(PGPKey.key_id == key.fingerprint)\ .count() == 0, "This is a duplicate key", field="pgp-key") if not valid.ok: return render_template("keys.html", current_user=user, pgp_key=pgp_key, valid=valid) pgp = PGPKey(user, pgp_key, key.fingerprint, key.userids[0].email) db.session.add(pgp) audit_log("pgp key added", 'Added PGP key {}'.format(key.fingerprint)) db.session.commit() return redirect("/keys")
def _webhook(record_id): record_id = UUID(record_id) hook = GitLabMRToBuild._GitLabMRToBuildRecord.query.filter( GitLabMRToBuild._GitLabMRToBuildRecord.id == record_id).one_or_none() if not hook: return "Unknown hook " + str(record_id), 404 auth = GitLabAuthorization.query.filter( GitLabAuthorization.user_id == hook.user_id, GitLabAuthorization.upstream == hook.upstream, ).first() if not auth: return "Invalid authorization for this hook" gitlab = Gitlab(f"https://{hook.upstream}", oauth_token=auth.oauth_token) valid = Validation(request) object_attrs = valid.require("object_attributes") if not valid.ok: return "Unexpected hook payload" source = object_attrs["source"] last_commit = object_attrs["last_commit"] project = gitlab.projects.get(hook.repo_id) source = gitlab.projects.get(source["id"]) commit = project.commits.get(last_commit["id"]) merge_req = project.mergerequests.get(object_attrs["iid"]) urls = submit_gitlab_build( "mrs", auth, hook, project, commit, source, { "GITLAB_MR_NUMBER": object_attrs["iid"], "GITLAB_MR_TITLE": object_attrs["title"], "GITLAB_BASE_REPO": project.attributes["name_with_namespace"], "GITLAB_HEAD_REPO": source.attributes["name_with_namespace"], }) if isinstance(urls, str): return urls summary = "\n\nbuilds.sr.ht jobs:\n\n" + ("\n".join( [f"[{n}]({url}): :clock1: running" for n, u in urls])) merge_req.description += summary merge_req.save() return
def settings_access_POST(owner_name, repo_name): owner, repo = check_access(owner_name, repo_name, UserAccess.manage) if isinstance(repo, BaseRedirectMixin): repo = repo.new_repo valid = Validation(request) user = valid.require("user", friendly_name="User") mode = valid.optional("access", cls=AccessMode, default=AccessMode.ro) if not valid.ok: return render_template("settings_access.html", owner=owner, repo=repo, **valid.kwargs) # TODO: Group access if user[0] == "~": user = user[1:] User = current_app.User user = User.query.filter(User.username == user).first() valid.expect( user, "I don't know this user. Have they logged into this service before?", field="user") valid.expect( not user or user.id != current_user.id, "You can't adjust your own access controls. You always have full read/write access.", field="user") if not valid.ok: return render_template("settings_access.html", owner=owner, repo=repo, **valid.kwargs) Access = current_app.Access grant = (Access.query.filter(Access.repo_id == repo.id, Access.user_id == user.id)).first() if not grant: grant = Access() grant.repo_id = repo.id grant.user_id = user.id db.session.add(grant) grant.mode = mode db.session.commit() return redirect("/{}/{}/settings/access".format(owner.canonical_name, repo.name))
def secret_delete_POST(): valid = Validation(request) uuid = valid.require("uuid") if not uuid: abort(404) secret = Secret.query.filter(Secret.uuid == uuid).first() if not secret: abort(404) if secret.user_id != current_user.id: abort(401) name = secret.name db.session.delete(secret) db.session.commit() session["message"] = "Successfully removed secret {}{}.".format( uuid, " ({})".format(name) if name else "") return redirect("/secrets")
def reset_POST(token): user = User.query.filter(User.reset_hash == token).first() if not user: abort(404) if user.reset_expiry < datetime.utcnow(): abort(404) valid = Validation(request) password = valid.require("password", friendly_name="Password") if not valid.ok: return render_template("reset.html", valid=valid) valid.expect(8 <= len(password) <= 512, "Password must be between 8 and 512 characters.", "password") if not valid.ok: return render_template("reset.html", valid=valid) user.password = bcrypt.hashpw(password.encode('utf-8'), salt=bcrypt.gensalt()).decode('utf-8') audit_log("password reset", user=user) db.session.commit() login_user(user, remember=True) metrics.meta_pw_resets.inc() return redirect("/")
def privacy_POST(): valid = Validation(request) key_id = valid.require("pgp-key") key_id = key_id if key_id != "null" else None key = None if key_id: key = PGPKey.query.get(int(key_id)) valid.expect(key.user_id == current_user.id, "Invalid PGP key") if not valid.ok: return redirect("/privacy") user = User.query.get(current_user.id) user.pgp_key = key audit_log("changed pgp key", "Set default PGP key to {}".format(key.key_id if key else None)) db.session.commit() return redirect("/privacy")
def create_list_POST(): valid = Validation(request) list_name = valid.require("list_name", friendly_name="Name") list_desc = valid.optional("list_desc") if not valid.ok: return render_template("create.html", **valid.kwargs) valid.expect(re.match(r'^[a-z._-][a-z0-9._-]*$', list_name), "Name must match [a-z._-][a-z0-9._-]*", field="list_name") existing = (List.query.filter(List.owner_id == current_user.id).filter( List.name.ilike(list_name)).first()) valid.expect(not existing, "This name is already in use.", field="list_name") valid.expect(not list_desc or 16 < len(list_desc) < 2048, "Description must be between 16 and 2048 characters.", field="list_desc") if not valid.ok: return render_template("create.html", **valid.kwargs) ml = List() ml.owner_id = current_user.id ml.name = list_name ml.description = list_desc db.session.add(ml) db.session.commit() # Auto-subscribe the owner sub = Subscription() sub.user_id = current_user.id sub.list_id = ml.id sub.confirmed = True db.session.add(sub) db.session.commit() return redirect( url_for("archives.archive", owner_name=current_user.canonical_name, list_name=ml.name))
def configure_repo_POST(gitlab, upstream): valid = Validation(request) repo_id = valid.require("repo_id") if not valid.ok: return "quit yo hackin bullshit" project = gitlab.projects.get(int(repo_id)) if not project: return "quit yo hackin bullshit" task = Task() task.name = "{}::gitlab_mr_to_build".format( project.attributes["name_with_namespace"]) task.user_id = current_user.id task._taskdef = "gitlab_mr_to_build" db.session.add(task) db.session.flush() record = GitLabMRToBuild._GitLabMRToBuildRecord() record.id = uuid4() record.user_id = current_user.id record.task_id = task.id record.gitlab_webhook_id = -1 record.repo_name = project.attributes['name_with_namespace'] record.repo_id = project.id record.web_url = project.attributes['web_url'] record.upstream = upstream db.session.add(record) db.session.flush() hook = project.hooks.create({ "url": _root + url_for("gitlab_mr_to_build._webhook", record_id=record.id), "merge_requests_events": 1, }) record.gitlab_webhook_id = hook.id db.session.commit() return redirect(url_for("html.edit_task", task_id=task.id))