pub struct Operator {
pub login_limiter: Limiter,
pub step_up_limiter: Limiter,
pub passkey_limiter: Limiter,
pub ceremonies: Ceremonies,
/* private fields */
}Fields§
§login_limiter: LimiterWrong passwords per client address at login (kernel spec §3.2).
step_up_limiter: LimiterWrong passwords per client address at step-up, a budget of its own (3b decision 10): a login flood from a shared address does not stop a signed-in owner stepping up, nor step-up guesses lock out login.
passkey_limiter: LimiterPasskey logins begun per client address (plan 3c decision 8): a budget of its own, untouched by wrong passwords.
ceremonies: CeremoniesThe passkey ceremonies begun and not yet finished (plan 3c decision 5).
Implementations§
Source§impl Operator
impl Operator
Sourcepub fn open(path: &Path) -> Result<Self>
pub fn open(path: &Path) -> Result<Self>
Open the kernel’s tables in hennery.db (shared with the host
registry and the sessions store).
pub fn open_in_memory() -> Result<Self>
Sourcepub fn owner_id(&self) -> &str
pub fn owner_id(&self) -> &str
The owner’s id. The owner exists from the first start, set up or
not (is_set_up).
Sourcepub fn is_set_up(&self) -> Result<bool>
pub fn is_set_up(&self) -> Result<bool>
Whether setup has run: the owner has a password and a public_url.
pub fn public_url(&self) -> Option<PublicUrl>
Sourcepub fn issue_setup_token(&self, now: i64) -> Result<Option<String>>
pub fn issue_setup_token(&self, now: i64) -> Result<Option<String>>
A fresh setup token (64 hex characters), valid for
SETUP_TOKEN_TTL_SECS, replacing any earlier one. None once there
is an owner.
Sourcepub fn announce_setup(
&self,
dir: &Path,
base_url: &str,
now: i64,
) -> Result<Option<SetupLink>>
pub fn announce_setup( &self, dir: &Path, base_url: &str, now: i64, ) -> Result<Option<SetupLink>>
Before setup: issue a fresh setup token and write its link,
<base_url>/setup#<token>, to dir/setup-url (kernel spec §3.1).
The file is created 0600 under a temporary name and renamed into
place, so an existing setup-url (even a symlink) is replaced, never
written through. Once set up: remove a stale setup-url and return
None.
Sourcepub fn setup_link(
&self,
dir: &Path,
base_url: &str,
now: i64,
) -> Result<Option<SetupLink>>
pub fn setup_link( &self, dir: &Path, base_url: &str, now: i64, ) -> Result<Option<SetupLink>>
The setup link for the admin socket’s setup-url (kernel spec
§4.2): the one last announced while its token is live, else a fresh
one, announced as announce_setup does (the old token dies). None
once set up.
Sourcepub fn set_up(
&self,
token: &str,
password: &str,
public_url: &str,
now: i64,
) -> Result<SetupOutcome>
pub fn set_up( &self, token: &str, password: &str, public_url: &str, now: i64, ) -> Result<SetupOutcome>
Set the owner up with password and store public_url (kernel spec
§3.1), if token is the live setup token, keeping the default hat’s
name. See set_up_naming_hat.
Sourcepub fn set_up_naming_hat(
&self,
token: &str,
password: &str,
public_url: &str,
hat_name: Option<&str>,
now: i64,
) -> Result<SetupOutcome>
pub fn set_up_naming_hat( &self, token: &str, password: &str, public_url: &str, hat_name: Option<&str>, now: i64, ) -> Result<SetupOutcome>
Set the owner up with password and store public_url (kernel spec
§3.1), if token is the live setup token, and name the default hat
hat_name if one is given (plan 5a decision 1: the hat exists from
the first start). The token is used up only once the owner is
committed. Hashes the password: call it on a blocking thread. It
takes no check_password slot: only the token’s holder gets as far
as the hash, and the setup lock serialises it.
Sourcepub fn verify_password(&self, password: &str) -> Result<Option<String>>
pub fn verify_password(&self, password: &str) -> Result<Option<String>>
Whether password is the owner’s: the PHC string it verified
against, or None. Before setup it is checked against a dummy hash,
so the answer takes as long either way. A session opened on the
strength of it passes the PHC to open_session, so a reset landing
in between wins (3b-ii decision 11). Blocking: prefer
check_password.
Sourcepub async fn check_password(
self: &Arc<Self>,
password: String,
) -> Result<Option<String>>
pub async fn check_password( self: &Arc<Self>, password: String, ) -> Result<Option<String>>
verify_password on a blocking thread, at most
MAX_CONCURRENT_HASHES at once; others wait their turn. The slot
moves into the blocking closure: a caller that goes away (a client
disconnect, a timeout) cannot free it while its verify still runs.
Sourcepub fn verifications(&self) -> u64
pub fn verifications(&self) -> u64
How many password checks have run: every login attempt that is not rate limited runs exactly one (kernel spec §3.2’s constant-time failure path), which tests pin with this.
Sourcepub async fn reset_password(
self: &Arc<Self>,
password: String,
now: i64,
) -> Result<Reset>
pub async fn reset_password( self: &Arc<Self>, password: String, now: i64, ) -> Result<Reset>
Replace the owner’s password (the admin socket’s reset, kernel spec
§4.2) and end every signed-in session, so their streams end too. The
hash runs on a blocking thread and takes a check_password slot like
any other, so a reset cannot add a third Argon2 run to a login
flood. The limiters are cleared: the operator proved local access.
It fails, changing nothing, unless it replaced exactly one password
(3b-ii’s O9): an owner with none (a passkey-only owner, in 3c) has
nothing to reset. Every passkey is removed and every passkey
ceremony begun before it ends, a finish re-checking under the lock
in any case (plan 3c decision 2, amended by its review’s A1): a
passkey added with a stolen session must not survive the recovery.
With no change-password route, every password change costs the
passkeys.
Sourcepub fn reset_public_url(&self, input: &str) -> Result<Reset>
pub fn reset_public_url(&self, input: &str) -> Result<Reset>
Replace public_url (the admin socket’s recovery when the collector
moved, 3b decision 4): the stored row and the origin every browser
request is checked against, which is cached here. Every signed-in
session ends: they were opened at the old origin, and the new one
signs in afresh. Passkeys are bound to the host name, the RP id
(kernel spec §3.2): when it changes they stop working, and are
removed; a move to another port or scheme keeps them (plan 3c
decision 9). Every passkey ceremony begun before it ends too, a
finish re-checking under the lock in any case.
Sourcepub fn change_public_url(
&self,
input: &str,
contact: Option<Option<&str>>,
caller: Option<(&str, i64)>,
) -> Result<PublicUrlChange>
pub fn change_public_url( &self, input: &str, contact: Option<Option<&str>>, caller: Option<(&str, i64)>, ) -> Result<PublicUrlChange>
What reset_public_url does, the one way public_url changes after
setup: the admin socket’s reset and PATCH /api/settings (plan 4d-B4
decision 1) both call it.
- With
contact, the push contact is set too (Some(None)clears it), in the same transaction: a body that names both changes both or neither. Both are checked before anything is written; either one refused isInvalid, nothing changed. - With
caller, the session that asked and the time it asked: the transaction’s first statement re-checks that session, live and stepped up, as a passkey registration’s write does (plan 3c A2; 4d-B4’s review, A1). One ended or stepped down since the request’s checks (a password reset, a revoke, a slow body) changes nothing.
Source§impl Operator
impl Operator
Sourcepub fn open_session(
&self,
user_agent: &str,
verified: &str,
now: i64,
) -> Result<Option<String>>
pub fn open_session( &self, user_agent: &str, verified: &str, now: i64, ) -> Result<Option<String>>
Open a session for the owner and return its token, the cookie’s
value. Only the token’s hash is stored. The password was just
checked, so the session starts stepped up. verified is the PHC
string that check verified against (verify_password): the session
is opened only while it is still the owner’s, so a login whose check
raced a password reset gets no session (None), like one before
setup, when the owner has no password.
Sourcepub fn authenticate(
&self,
token: &str,
now: i64,
) -> Result<Option<Authenticated>>
pub fn authenticate( &self, token: &str, now: i64, ) -> Result<Option<Authenticated>>
The live session token names, if any. Its expiry slides to
SESSION_TTL_SECS from now, at most every SESSION_SLIDE_SECS.
Sourcepub fn step_up(&self, session_id: &str, now: i64) -> Result<bool>
pub fn step_up(&self, session_id: &str, now: i64) -> Result<bool>
Record a fresh password check on a session (kernel spec §3.4). Whether the session still exists.
Sourcepub fn sessions(&self, now: i64) -> Result<Vec<AuthSession>>
pub fn sessions(&self, now: i64) -> Result<Vec<AuthSession>>
Every live session, most recently used first.
Sourcepub fn revoke_session(&self, session_id: &str, now: i64) -> Result<bool>
pub fn revoke_session(&self, session_id: &str, now: i64) -> Result<bool>
End a live session, and the push subscriptions it made (plan 10a
decision 4): a device signed out, or revoked as lost, gets no more
notifications. Whether there was one: an expired session is not
listed by sessions, so it is not there to revoke either.
Sourcepub fn session_ends(&self) -> Receiver<u64>
pub fn session_ends(&self) -> Receiver<u64>
Changes whenever a session ends (revoke_session, and so logout,
and the resets).
Sourcepub fn session_expires_at(
&self,
session_id: &str,
now: i64,
) -> Result<Option<i64>>
pub fn session_expires_at( &self, session_id: &str, now: i64, ) -> Result<Option<i64>>
When the live session session_id expires, without sliding it;
None once it is gone or expired.
The first of the request’s session cookies (session_tokens) that
authenticates, with its token; None when none does. Like
authenticate, it slides the session it finds.
Source§impl Operator
impl Operator
Sourcepub fn start_passkey_registration(
&self,
session_id: &str,
label: &str,
now: i64,
) -> Result<Start>
pub fn start_passkey_registration( &self, session_id: &str, label: &str, now: i64, ) -> Result<Start>
Begin registering a passkey labelled label for the signed-in
session session_id (decision 7: the route requires step-up). The
owner’s passkeys are excluded, so an authenticator that holds one
says so rather than making a second.
Sourcepub fn finish_passkey_registration(
&self,
session_id: &str,
ceremony_id: &str,
credential: &Value,
now: i64,
) -> Result<Result<PasskeyRecord, Refused>>
pub fn finish_passkey_registration( &self, session_id: &str, ceremony_id: &str, credential: &Value, now: i64, ) -> Result<Result<PasskeyRecord, Refused>>
Finish the registration ceremony_id names, begun by session_id,
with the browser’s credential (RegisterPublicKeyCredential as
JSON), and store the passkey.
Sourcepub fn passkeys(&self) -> Result<Vec<PasskeyRecord>>
pub fn passkeys(&self) -> Result<Vec<PasskeyRecord>>
The owner’s passkeys, oldest first.
Sourcepub fn remove_passkey(&self, id: &str) -> Result<bool>
pub fn remove_passkey(&self, id: &str) -> Result<bool>
Remove the passkey id (the route requires step-up, kernel spec
§3.4). Whether there was one. The password stays, so a login method
always remains (decision 2).
Sourcepub fn start_passkey_login(&self, now: i64) -> Result<Start>
pub fn start_passkey_login(&self, now: i64) -> Result<Start>
Begin signing in with a passkey (decision 8).
Sourcepub fn finish_passkey_login(
&self,
ceremony_id: &str,
credential: &Value,
user_agent: &str,
now: i64,
) -> Result<Result<String, Refused>>
pub fn finish_passkey_login( &self, ceremony_id: &str, credential: &Value, user_agent: &str, now: i64, ) -> Result<Result<String, Refused>>
Finish the login ceremony_id names with the browser’s
credential (PublicKeyCredential as JSON), and open a session:
its token, the cookie’s value. The session starts stepped up, as a
password login’s does. The passkey’s counter moves on and the
session opens in one transaction, and only while the passkey is
still stored: one removed since the ceremony began opens nothing.
Sourcepub fn start_passkey_step_up(&self, session_id: &str, now: i64) -> Result<Start>
pub fn start_passkey_step_up(&self, session_id: &str, now: i64) -> Result<Start>
Begin stepping the session session_id up with a passkey (kernel
spec §3.4, decision 8).
Sourcepub fn finish_passkey_step_up(
&self,
session_id: &str,
ceremony_id: &str,
credential: &Value,
now: i64,
) -> Result<Result<bool, Refused>>
pub fn finish_passkey_step_up( &self, session_id: &str, ceremony_id: &str, credential: &Value, now: i64, ) -> Result<Result<bool, Refused>>
Finish the step-up ceremony_id names, begun by session_id, and
record it on the session, in one transaction with the passkey’s
counter. Ok(false) when the session has ended meanwhile.