示例#1
0
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")
示例#2
0
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))
示例#3
0
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}
示例#4
0
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)
示例#5
0
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 &lt;fingerprint&gt;</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")
示例#6
0
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")
示例#7
0
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")