aboutsummaryrefslogtreecommitdiffstats
path: root/src/access/registry.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:09 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:09 +0100
commitde12c862b3f4e69290c8e93b3b4520024ba458f6 (patch)
tree0f56381fa62948a7cb11b03fed6075fd85e7e649 /src/access/registry.rs
parent3a9d9da74f55395ead20f3915ed04639e37b2f03 (diff)
access: the users endpoints
/admin/users: list, add (one or several), remove, disable, grant and revoke capabilities; each one change, refused if it would lock its writer out. Capabilities as the store's paths name them. Auth plan M1.
Diffstat (limited to 'src/access/registry.rs')
-rw-r--r--src/access/registry.rs54
1 files changed, 20 insertions, 34 deletions
diff --git a/src/access/registry.rs b/src/access/registry.rs
index 3006b31..9eafc82 100644
--- a/src/access/registry.rs
+++ b/src/access/registry.rs
@@ -33,7 +33,7 @@ pub(crate) fn user_path(name: &str) -> Vec<String> {
}
/// Whether the swarm has a registry: until it has, every user a node lets in may do
-/// what all could before (`docs/access.md`, An existing swarm).
+/// everything but write protocol data (`docs/access.md`, A swarm without a registry).
pub(crate) fn exists(doc: &impl ReadDoc) -> bool {
store::is_map(doc, &users_dir())
}
@@ -141,28 +141,23 @@ pub(crate) fn capability(by: &str, since: u64, path: &[String], rights: &[Right]
})
}
-/// The first users of a new swarm, as its genesis node records them (`first_users`):
-/// each name, and whether it may do everything (protocol data and managing users
-/// included), not only read and write the swarm's data.
-pub(crate) type FirstUsers = Vec<(String, bool)>;
+/// The first users of a new swarm, as its genesis node records them (`first_users`),
+/// each with everything (`all_caps`).
+pub(crate) type FirstUsers = Vec<String>;
-/// The first users of a new swarm, from the genesis node's `--bootstrap-users`: each may
-/// do everything. None without: then the swarm has no registry, and its users are all
-/// alike, as before there were registries (`access.md`).
+/// The first users of a new swarm, from the genesis node's `--bootstrap-users`, sorted.
+/// None without: then the swarm has no registry, and its users are all alike
+/// (`access.md`, A swarm without a registry).
pub(crate) fn first_users(bootstrap_users: &[String]) -> FirstUsers {
let users: std::collections::BTreeSet<&String> = bootstrap_users.iter().collect();
- users.into_iter().map(|name| (name.clone(), true)).collect()
+ users.into_iter().cloned().collect()
}
-/// The capabilities of a first user (`first_users`): the swarm's data, and for one who
-/// may do everything protocol data too.
-pub(crate) fn first_caps(admin: bool) -> Vec<(Vec<String>, &'static [Right])> {
+/// Everything, as a first user has it (`first_users`): the swarm's data and protocol
+/// data (managing users included), to read and write.
+pub(crate) fn all_caps() -> Vec<(Vec<String>, &'static [Right])> {
const BOTH: &[Right] = &[Right::Read, Right::Write];
- let mut caps = vec![(Vec::new(), BOTH)];
- if admin {
- caps.push((meta::path(&[]), BOTH));
- }
- caps
+ vec![(Vec::new(), BOTH), (meta::path(&[]), BOTH)]
}
/// A random ID for a capability (8 bytes, hex): concurrent grants don't collide.
@@ -250,37 +245,28 @@ mod tests {
let names = |list: &[&str]| list.iter().map(|n| (*n).to_owned()).collect::<Vec<_>>();
assert_eq!(
first_users(&names(&["bob", "alice", "bob"])),
- vec![("alice".to_owned(), true), ("bob".to_owned(), true)]
+ names(&["alice", "bob"])
);
// Without bootstrap users, no registry.
assert!(first_users(&[]).is_empty());
- let admin = Caps::new(
- first_caps(true)
- .into_iter()
- .map(|(path, rights)| {
- serde_json::from_value(capability("x", 0, &path, rights)).unwrap()
- })
- .collect(),
- );
- assert!(admin.allows(Right::Write, &path("_continuum/users/x")));
- assert!(admin.allows(Right::Write, &path("accounts")));
- let user = Caps::new(
- first_caps(false)
+ let all = Caps::new(
+ all_caps()
.into_iter()
.map(|(path, rights)| {
serde_json::from_value(capability("x", 0, &path, rights)).unwrap()
})
.collect(),
);
- assert!(user.allows(Right::Write, &path("accounts")));
- assert!(!user.allows(Right::Read, &path("_continuum")));
+ assert!(all.allows(Right::Write, &path("_continuum/users/x")));
+ assert!(all.allows(Right::Write, &path("accounts")));
}
#[test]
fn who_may_manage_users() {
let mut doc = Document::new();
- for (name, admin) in [("alice", true), ("bob", false)] {
- let entry = entry("x", 0, &first_caps(admin));
+ const DATA: &[Right] = &[Right::Read, Right::Write];
+ for (name, caps) in [("alice", all_caps()), ("bob", vec![(Vec::new(), DATA)])] {
+ let entry = entry("x", 0, &caps);
change(&mut doc, |tx| {
store::write_path(tx, &user_path(name), &entry).unwrap()
});