aboutsummaryrefslogtreecommitdiffstats
path: root/src/access/registry.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:21 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:21 +0100
commita3e936154e07ba3b95157d26ffeae9364f3d5cef (patch)
tree771a46e192e9cee20833cc61c9e7e6dad239baba /src/access/registry.rs
parent02e06975d02bd8f02d811b4dd1d45ecad2374861 (diff)
access: logouts apart from users' entries
_continuum/logouts/<name> holds when a user's tokens were last revoked, written by the user itself or by one who may manage users, so users' entries can be signed by their managers alone. Auth plan M7.
Diffstat (limited to 'src/access/registry.rs')
-rw-r--r--src/access/registry.rs25
1 files changed, 18 insertions, 7 deletions
diff --git a/src/access/registry.rs b/src/access/registry.rs
index eb7919c..caa263d 100644
--- a/src/access/registry.rs
+++ b/src/access/registry.rs
@@ -67,7 +67,8 @@ pub(crate) struct User {
pub(crate) caps: BTreeMap<String, Capability>,
pub(crate) disabled: bool,
/// Tokens issued before (Unix ms) are no longer taken (`token::verify`): how all
- /// of a user's tokens are revoked at once.
+ /// of a user's tokens are revoked at once, kept apart from its entry
+ /// (`logouts_path`), as the user writes it itself.
pub(crate) tokens_not_before: u64,
/// Its keys (`keys/<id>`, `authorized_keys` lines, `access::sshsig`): what it signs
/// values with (`access::envelope`) and logs in with. One that doesn't read is left
@@ -100,11 +101,7 @@ impl User {
disabled: value
.get("disabled")
.is_some_and(|disabled| disabled != &JsonValue::Bool(false)),
- // A damaged one revokes every token.
- tokens_not_before: match value.get("tokensNotBefore") {
- None => 0,
- Some(at) => at.as_u64().unwrap_or(u64::MAX),
- },
+ tokens_not_before: 0,
keys: value
.get("keys")
.and_then(JsonValue::as_object)
@@ -132,7 +129,21 @@ impl User {
/// The entry of user `name`, if it has one.
pub(crate) fn user(doc: &impl ReadDoc, name: &str) -> Option<User> {
let value = store::read_path(doc, &user_path(name)).ok()??;
- value.is_object().then(|| User::from_json(&value))
+ let mut user = value.is_object().then(|| User::from_json(&value))?;
+ // A damaged one revokes every token.
+ user.tokens_not_before = match store::read_path(doc, &logouts_path(name)) {
+ Ok(None) => 0,
+ Ok(Some(at)) => at.as_u64().unwrap_or(u64::MAX),
+ Err(_) => u64::MAX,
+ };
+ Some(user)
+}
+
+/// Where user `name`'s tokens were last revoked (Unix ms: those issued before aren't
+/// taken any more), written by the user itself (`POST /auth/logout`) or by one who may
+/// manage users.
+pub(crate) fn logouts_path(name: &str) -> Vec<String> {
+ meta::path(&[meta::LOGOUTS, name])
}
/// The users who may manage users: enabled, with `write` on `_continuum/users`.