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 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 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 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 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 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 secrets_POST(): valid = Validation(request) name = valid.optional("name") secret_type = valid.require("secret_type", friendly_name="Secret Type") valid.expect(not name or 3 < len(name) < 512, "Name must be between 3 and 512 characters", field="name") if secret_type is not None: try: secret_type = SecretType(secret_type) except: valid.error("{} is not a valid secret type".format(secret_type), field="secret_type") if secret_type == SecretType.plaintext_file: _secret = valid.optional("secret") secret_file = valid.optional("file-file", max_file_size=16384) for f in ["secret", "file-file"]: valid.expect(bool(_secret) ^ bool(secret_file), "Either secret text or file have to be provided", field=f) if _secret: _secret = _secret.replace('\r\n', '\n') if not _secret.endswith('\n'): _secret += '\n' else: _secret = secret_file else: _secret = valid.require("secret", friendly_name="Secret") if isinstance(_secret, str): _secret = _secret.encode() if not valid.ok: return render_template("secrets.html", **valid.kwargs) secret = Secret(current_user, secret_type) if secret_type == SecretType.pgp_key: try: key, _ = pgpy.PGPKey.from_blob(_secret) if key.is_protected: valid.error("PGP key cannot be passphrase protected.", field="secret") except Exception as ex: valid.error("Invalid PGP key.", field="secret") elif secret_type == SecretType.plaintext_file: file_path = valid.require("file-path", friendly_name="Path") file_mode = valid.require("file-mode", friendly_name="Mode") if not valid.ok: return render_template("secrets.html", **valid.kwargs) try: file_mode = int(file_mode, 8) except: valid.error("Must be specified in octal", field="file-mode") if not valid.ok: return render_template("secrets.html", **valid.kwargs) secret.path = file_path secret.mode = file_mode if not valid.ok: return render_template("secrets.html", **valid.kwargs) secret.name = name secret.secret = _secret db.session.add(secret) db.session.commit() session["message"] = "Successfully added secret {}.".format(secret.uuid) return redirect("/secrets")