aboutsummaryrefslogtreecommitdiffstats
path: root/src/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/access')
-rw-r--r--src/access/registry.rs16
-rw-r--r--src/access/rules.rs4
-rw-r--r--src/access/token.rs21
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, &registry::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, &registry::logouts_path("alice"), &json!(0)).unwrap();
+ store::write_path(
+ tx,
+ &registry::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, &registry::logouts_path("alice"), &json!(0)).unwrap();