diff options
Diffstat (limited to 'src/api/users.rs')
| -rw-r--r-- | src/api/users.rs | 26 |
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(), + ), )); } } |
