diff options
Diffstat (limited to 'src/access')
| -rw-r--r-- | src/access/registry.rs | 16 | ||||
| -rw-r--r-- | src/access/rules.rs | 4 | ||||
| -rw-r--r-- | src/access/token.rs | 21 |
3 files changed, 39 insertions, 2 deletions
diff --git a/src/access/registry.rs b/src/access/registry.rs index 01e6ba9..e806d0c 100644 --- a/src/access/registry.rs +++ b/src/access/registry.rs @@ -146,6 +146,22 @@ pub(crate) fn logouts_path(name: &str) -> Vec<String> { meta::path(&[meta::LOGOUTS, name]) } +/// Where user `name`'s session `session` is recorded as ended (logged out of): its +/// tokens aren't taken any more (`token::verify`). The value: until when the session +/// would have lasted (Unix ms), after which it is left out of snapshots. Written by the +/// user itself (`DELETE /auth/session`) or by one who may manage users. +pub(crate) fn session_path(name: &str, session: &str) -> Vec<String> { + meta::path(&[meta::SESSIONS, name, session]) +} + +/// Whether user `name`'s session `session` was ended (`session_path`). +pub(crate) fn session_ended(doc: &impl ReadDoc, name: &str, session: &str) -> bool { + store::read_path(doc, &session_path(name, session)) + .ok() + .flatten() + .is_some() +} + /// The users who may manage users: enabled, with `write` on `_continuum/users`. pub(crate) fn managers(doc: &impl ReadDoc) -> Vec<String> { names(doc) diff --git a/src/access/rules.rs b/src/access/rules.rs index d120961..5c839af 100644 --- a/src/access/rules.rs +++ b/src/access/rules.rs @@ -269,7 +269,9 @@ pub(crate) fn judge_reading( (Some(meta::LOCAL), _) => false, // Logging out: a user revokes its own tokens (`POST /auth/logout`); one who // manages users, anyone's (`DELETE /admin/users/{name}/tokens`). - (Some(meta::LOGOUTS), Some(owner)) => { + // Ending a session: a user its own (`DELETE /auth/session`); one who manages + // users, anyone's. + (Some(meta::LOGOUTS | meta::SESSIONS), Some(owner)) => { change.user == Some(owner) || user_may(path) || user_may(&meta::path(&[meta::USERS])) diff --git a/src/access/token.rs b/src/access/token.rs index d33a3fe..e7aff42 100644 --- a/src/access/token.rs +++ b/src/access/token.rs @@ -89,13 +89,16 @@ pub(crate) enum Refused { Disabled, #[error("revoked: issued before its user's tokens were")] Revoked, + #[error("its session was ended (logged out of)")] + Ended, } /// The claims of `token` if this node takes it: well formed, for `swarm`, signed by a /// node that is a member in `doc` and not banned, not expired at `now` (Unix ms), nor issued later /// than a clock off by `SKEW_MS` would; and in a swarm with a registry, for a /// registered user that is enabled, issued after its tokens were last revoked -/// (`registry::User::tokens_not_before`). +/// (`registry::User::tokens_not_before`), of a session not ended +/// (`registry::session_ended`). pub(crate) fn verify( doc: &impl ReadDoc, token: &str, @@ -150,6 +153,9 @@ pub(crate) fn verify( if claims.iat < user.tokens_not_before { return Err(Refused::Revoked); } + if registry::session_ended(doc, &claims.user, &claims.session) { + return Err(Refused::Ended); + } } Ok(claims) } @@ -290,6 +296,19 @@ mod tests { store::write_path(tx, ®istry::logouts_path("alice"), &json!(1_001)).unwrap() }); assert_eq!(verify(&doc, &token, "s", 1_500), Err(Refused::Revoked)); + // One session ended: its tokens refused, another's taken. + let session = claims_of(&token).session; + change(&mut doc, |tx| { + store::write_path(tx, ®istry::logouts_path("alice"), &json!(0)).unwrap(); + store::write_path( + tx, + ®istry::session_path("alice", &session), + &json!(10_000), + ) + .unwrap() + }); + assert_eq!(verify(&doc, &token, "s", 1_500), Err(Refused::Ended)); + assert!(verify(&doc, &issue(&key, &claims(&key)), "s", 1_500).is_ok()); alice["disabled"] = json!(true); change(&mut doc, |tx| { store::write_path(tx, ®istry::logouts_path("alice"), &json!(0)).unwrap(); |
