Orca · API

orca.auth

Who may act in orca ui: users with passwords, their sessions, and a record of what each did.

orca user add alice       reads the password from standard input
orca user remove alice    and ends their sessions
orca user list

Anyone who can reach the UI can read it. Its actions (start, backfill, resume, rerun, cancel, rebuild) need a logged-in user once login is on: when ORCA_UI_LOGIN=required, or as soon as any user exists, so a deployment can't be left open by forgetting the variable once it has users.

Passwords are kept as bcrypt hashes made by Postgres's pgcrypto (crypt(password, gen_salt('bf', 12))), so neither orca nor its logs see one after it's checked. Logging in makes a session: 32 random bytes from the system's generator, given to the browser as the cookie orca_session and kept only as their SHA-256 digest, for ORCA_UI_SESSION_HOURS (default 12). Every action a user takes is a row of orca.audit, and so is every failed login, which too_many_failures counts: after 5 for one name, or 20 from one address, in 15 minutes, /login answers 429 without checking a password, so a public UI can't be guessed at.

Functions

login_required

def login_required(mut db: State) -> Bool

add_user

def add_user(mut db: State, name: String, password: String) -> Bool

Adds the user, or sets an existing one's password (ending her sessions); True if the user is new.

remove_user

def remove_user(mut db: State, name: String) -> Bool

Removes the user and their sessions; False if there was none.

users

def users(mut db: State) -> List[String]

too_many_failures

def too_many_failures(mut db: State, name: String, address: String) -> Bool

Whether logins for name, or from address, have failed too often of late to check another: FAILURES_PER_NAME for a name or FAILURES_PER_ADDRESS from an address in FAILURE_WINDOW_MINUTES. Failures are login-failed rows of orca.audit, with the address as their detail.

login_failed

def login_failed(mut db: State, name: String, address: String)

open_session

def open_session(mut db: State, name: String, password: String) -> String

A new session's token if the password is the user's, else empty.

session_user

def session_user(mut db: State, token: String) -> String

The user whose session token is, or empty.

close_session

def close_session(mut db: State, token: String)

audit

def audit(mut db: State, user: String, action: String, run: Int, detail: String)

Records that user did action (the action's path), to run (0 for none), with detail (what the form gave).

Aliases

comptime FAILURES_PER_NAME = 5
comptime FAILURES_PER_ADDRESS = 20
comptime FAILURE_WINDOW_MINUTES = 15