aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/users.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/users.rs')
-rw-r--r--src/api/users.rs26
1 files changed, 19 insertions, 7 deletions
diff --git a/src/api/users.rs b/src/api/users.rs
index 1dda33b..d6950af 100644
--- a/src/api/users.rs
+++ b/src/api/users.rs
@@ -24,8 +24,10 @@
//! * `GET /auth/keys`: the caller's keys, its entry, and the envelope holding it under a
//! strict policy (`signed`), to sign the next one against.
//! * `PUT /auth/keys/{id}`: one added or replaced (the line as the body);
-//! `DELETE /auth/keys/{id}` removes one. Under a strict policy `409`: the user signs
-//! its entry with the keys changed, nothing else (`continuum-signed key-add`).
+//! `DELETE /auth/keys/{id}` removes one, the last only of a user with a password on
+//! this node (`--htpasswd`, `password` in `GET /auth/keys`), which logs in by it. Under
+//! a strict policy `409`: the user signs its entry with the keys changed, nothing else
+//! (`continuum-signed key-add`).
//!
//! Looking needs `read` on `_continuum/users`, changing `write` on it (in a swarm
//! without a registry, being in `--protocol-writers`). Every change is one change to
@@ -142,6 +144,7 @@ fn shown(doc: &impl crate::document::ReadDoc, name: &str) -> Option<JsonValue> {
pub(crate) async fn own_keys(
State(state): State<AppState>,
Extension(user): Extension<AuthenticatedUser>,
+ Extension(auth): Extension<super::auth::AuthState>,
) -> Result<Json<JsonValue>, ApiError> {
let replica = state.doc.read().await;
let path = registry::user_path(&user.0);
@@ -157,6 +160,7 @@ pub(crate) async fn own_keys(
"keys": entry.get("keys").cloned().unwrap_or_else(|| json!({})),
"entry": entry,
"signed": signed,
+ "password": auth.has_password(&user.0),
})))
}
@@ -168,24 +172,28 @@ pub(crate) async fn put_own_key(
body: String,
) -> Result<StatusCode, ApiError> {
let key = crate::access::sshsig::PublicKey::parse(body.trim()).map_err(|why| bad(&why))?;
- own_key(&state, &user.0, id, Some(key.line())).await
+ own_key(&state, &user.0, id, Some(key.line()), false).await
}
/// `DELETE /auth/keys/{id}`.
pub(crate) async fn delete_own_key(
State(state): State<AppState>,
Extension(user): Extension<AuthenticatedUser>,
+ Extension(auth): Extension<super::auth::AuthState>,
Path(id): Path<String>,
) -> Result<StatusCode, ApiError> {
- own_key(&state, &user.0, id, None).await
+ let password = auth.has_password(&user.0);
+ own_key(&state, &user.0, id, None, password).await
}
-/// `user`'s key `id` set to `line`, or removed: one change, as `user`.
+/// `user`'s key `id` set to `line`, or removed: one change, as `user`. Its last key only
+/// with `password` (it has one here, and logs in by it).
async fn own_key(
state: &AppState,
user: &str,
id: String,
line: Option<String>,
+ password: bool,
) -> Result<StatusCode, ApiError> {
if !registry::valid_name(&id) {
return Err(bad("not a key's name: 1 to 64 of A-Z a-z 0-9 . _ @ -"));
@@ -216,7 +224,7 @@ async fn own_key(
Some("no entry of yours".to_owned()),
));
}
- if line.is_none() {
+ if line.is_none() && !password {
let mut keys = entry.clone();
keys.push("keys".to_owned());
let others = store::keys_at(&*doc, &keys)?
@@ -227,7 +235,11 @@ async fn own_key(
if others == 0 {
return Err(ApiError(
StatusCode::CONFLICT,
- Some("your last key: add another first".to_owned()),
+ Some(
+ "your last key, and you have no password on this node: add \
+ another first"
+ .to_owned(),
+ ),
));
}
}