login_required
def login_required(mut db: State) -> Bool orca.authWho 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.
login_requireddef login_required(mut db: State) -> Bool add_userdef 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_userdef remove_user(mut db: State, name: String) -> Bool Removes the user and their sessions; False if there was none.
usersdef users(mut db: State) -> List[String] too_many_failuresdef 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_faileddef login_failed(mut db: State, name: String, address: String) open_sessiondef 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_userdef session_user(mut db: State, token: String) -> String The user whose session token is, or empty.
close_sessiondef close_session(mut db: State, token: String) auditdef 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).
comptime SESSION_COOKIE = "orca_session"comptime FAILURES_PER_NAME = 5comptime FAILURES_PER_ADDRESS = 20comptime FAILURE_WINDOW_MINUTES = 15