aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/access.md9
-rw-r--r--docs/example.toml9
-rw-r--r--src/access/registry.rs35
-rw-r--r--src/config/mod.rs4
-rw-r--r--src/main.rs22
5 files changed, 37 insertions, 42 deletions
diff --git a/docs/access.md b/docs/access.md
index 7a85c94..519113c 100644
--- a/docs/access.md
+++ b/docs/access.md
@@ -34,9 +34,12 @@ password hashes: every member reads the document).
A swarm gets a registry from its start if its genesis node is given its first users,
`--bootstrap-users`: it records them, with the swarm ID, in its first change, each with
-everything (`[]` and `["_continuum"]`), and the other users of its `--htpasswd` with
-`[]`. Without it, the swarm has no registry, and is as swarms were before: users all
-alike (An existing swarm, below), until someone gives it one.
+everything (`[]` and `["_continuum"]`). Other users, of its `--htpasswd` or any node's,
+may do nothing until added (the genesis node warns of those of its `--htpasswd`).
+Without it, the swarm has no registry, and is as swarms were before: users all alike
+(An existing swarm, below), until someone gives it one. `--protocol-writers` counts in
+both: without a registry, as who may write protocol data; with one, to set it right
+should nobody be left who may manage users (Keeping the swarm manageable, below).
## At the HTTP API
diff --git a/docs/example.toml b/docs/example.toml
index e48bd2c..b98752d 100644
--- a/docs/example.toml
+++ b/docs/example.toml
@@ -83,13 +83,14 @@ htpasswd = "/etc/continuum/htpasswd"
# (remake records, members, forget records), e.g. to repair a stuck remake, in a swarm
# without a user registry. Such writes act on the whole swarm and are logged. Epoch
# metadata and the swarm ID are never writable. With a registry its capabilities decide
-# instead (docs/access.md).
+# instead (docs/access.md), but for a registry nobody may manage users of any more: then
+# these may do everything on this node, to set it right.
# protocol-writers = ["alice"]
# With genesis: give the new swarm a user registry (docs/access.md), these its first
-# users, each may do everything, protocol data and managing users included; htpasswd's
-# other users may read and write the swarm's data. Unset, the swarm has no registry: its
-# users are all alike, and only those in protocol-writers write protocol data.
+# users, each may do everything, protocol data and managing users included; other users
+# may do nothing until added. Unset, the swarm has no registry: its users are all alike,
+# and only those in protocol-writers write protocol data.
# bootstrap-users = ["alice"]
# How log lines look: text (timestamped), journal (for systemd's journal: no timestamp or
diff --git a/src/access/registry.rs b/src/access/registry.rs
index 094e62c..3006b31 100644
--- a/src/access/registry.rs
+++ b/src/access/registry.rs
@@ -146,24 +146,12 @@ pub(crate) fn capability(by: &str, since: u64, path: &[String], rights: &[Right]
/// included), not only read and write the swarm's data.
pub(crate) type FirstUsers = Vec<(String, bool)>;
-/// The first users of a new swarm, from the genesis node's options: those of
-/// `bootstrap_users` may do everything, and the other users of its htpasswd file
-/// (`htpasswd`) read and write the swarm's data. None without `bootstrap_users`: then
-/// the swarm has no registry, and its users are all alike, as before there were
-/// registries (`access.md`).
-pub(crate) fn first_users(bootstrap_users: &[String], htpasswd: &[String]) -> FirstUsers {
- if bootstrap_users.is_empty() {
- return Vec::new();
- }
- let mut users: BTreeMap<String, bool> = htpasswd
- .iter()
- .filter(|name| valid_name(name))
- .map(|name| (name.clone(), false))
- .collect();
- for name in bootstrap_users {
- users.insert(name.clone(), true);
- }
- users.into_iter().collect()
+/// 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`).
+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()
}
/// The capabilities of a first user (`first_users`): the swarm's data, and for one who
@@ -261,16 +249,11 @@ mod tests {
fn the_first_users() {
let names = |list: &[&str]| list.iter().map(|n| (*n).to_owned()).collect::<Vec<_>>();
assert_eq!(
- first_users(&names(&["alice"]), &names(&["alice", "bob"])),
- vec![("alice".to_owned(), true), ("bob".to_owned(), false)]
- );
- // One the proxy names, not in htpasswd.
- assert_eq!(
- first_users(&names(&["carol"]), &names(&["bob"])),
- vec![("bob".to_owned(), false), ("carol".to_owned(), true)]
+ first_users(&names(&["bob", "alice", "bob"])),
+ vec![("alice".to_owned(), true), ("bob".to_owned(), true)]
);
// Without bootstrap users, no registry.
- assert!(first_users(&[], &names(&["alice"])).is_empty());
+ assert!(first_users(&[]).is_empty());
let admin = Caps::new(
first_caps(true)
.into_iter()
diff --git a/src/config/mod.rs b/src/config/mod.rs
index 320d2a8..5306c5e 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -121,14 +121,14 @@ pub(crate) const OPTIONS: &[OptionSpec] = &[
Kind::List,
"USER,[USER...]",
None,
- "HTTP API users (from htpasswd or user-header) who may write protocol data under /store/_continuum (remake records, members, forget records), e.g. to repair a stuck remake, in a swarm without a user registry. Such writes act on the whole swarm and are logged. Epoch metadata and the swarm ID are never writable. With a registry its capabilities decide instead (docs/access.md).",
+ "HTTP API users (from htpasswd or user-header) who may write protocol data under /store/_continuum (remake records, members, forget records), e.g. to repair a stuck remake, in a swarm without a user registry. Such writes act on the whole swarm and are logged. Epoch metadata and the swarm ID are never writable. With a registry its capabilities decide instead (docs/access.md), but for a registry nobody may manage users of any more: then these may do everything on this node, to set it right.",
),
option(
"bootstrap-users",
Kind::List,
"USER,[USER...]",
None,
- "With genesis: give the new swarm a user registry (docs/access.md), these its first users, each may do everything, protocol data and managing users included; htpasswd's other users may read and write the swarm's data. Unset, the swarm has no registry: its users are all alike, and only those in protocol-writers write protocol data.",
+ "With genesis: give the new swarm a user registry (docs/access.md), these its first users, each may do everything, protocol data and managing users included; other users may do nothing until added. Unset, the swarm has no registry: its users are all alike, and only those in protocol-writers write protocol data.",
),
option(
"log-format",
diff --git a/src/main.rs b/src/main.rs
index 2b8c376..fb3c0ad 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -97,16 +97,24 @@ async fn main() -> Result<()> {
// A broken htpasswd will abort startup.
let auth_state =
AuthState::from_settings(&args)?.context("htpasswd or user-header is required")?;
- // A new swarm's first users, if it is to have a user registry.
- let first_users = if args.genesis && !args.bootstrap_users.is_empty() {
- let htpasswd = match args.htpasswd.as_deref() {
- Some(path) => api::auth::htpasswd_names(path)?,
- None => Vec::new(),
- };
- access::registry::first_users(&args.bootstrap_users, &htpasswd)
+ // A new swarm's first users, if it is to have a user registry; the users of the
+ // htpasswd file that aren't among them may do nothing until they are added.
+ let first_users = if args.genesis {
+ access::registry::first_users(&args.bootstrap_users)
} else {
Vec::new()
};
+ if !first_users.is_empty()
+ && let Some(path) = args.htpasswd.as_deref()
+ {
+ let missing: Vec<String> = api::auth::htpasswd_names(path)?
+ .into_iter()
+ .filter(|name| !args.bootstrap_users.contains(name))
+ .collect();
+ if !missing.is_empty() {
+ warn!(users = ?missing, "Users of htpasswd not among bootstrap-users: the new swarm's registry doesn't have them, so they may do nothing until added (docs/access.md)");
+ }
+ }
info!(
bind_addr = ?args.bind_addr.iter().map(ToString::to_string).collect::<Vec<_>>(),