support disabling/enabling users by http api

This commit is contained in:
Scott Lamb 2023-01-31 06:47:25 -08:00
parent a9430464b6
commit 182f6f8a1b
No known key found for this signature in database
3 changed files with 15 additions and 4 deletions

View File

@ -869,7 +869,7 @@ Expects a JSON object:
* `csrf`: a CSRF token, required when using session authentication. * `csrf`: a CSRF token, required when using session authentication.
* `update`: `UserSubset`, sets the provided fields. Field-specific notes: * `update`: `UserSubset`, sets the provided fields. Field-specific notes:
* `username`: requires `adminUsers` permission. * `disabled`: requires `adminUsers` permission.
* `password`: when updating the password, the previous password must * `password`: when updating the password, the previous password must
be supplied as a precondition, unless the caller has `adminUsers` be supplied as a precondition, unless the caller has `adminUsers`
permission. permission.
@ -877,6 +877,7 @@ Expects a JSON object:
user's permissions currently neither adds nor limits permissions of user's permissions currently neither adds nor limits permissions of
existing sessions; it only changes what is available to newly created existing sessions; it only changes what is available to newly created
sessions. sessions.
* `username`: requires `adminUsers` permission.
* `precondition`: `UserSubset`, forces the request to fail with HTTP status * `precondition`: `UserSubset`, forces the request to fail with HTTP status
412 (Precondition failed) if the provided fields don't have the given 412 (Precondition failed) if the provided fields don't have the given
values. values.
@ -899,9 +900,7 @@ Returns HTTP status 204 (No Content) on success.
A JSON object with any of the following parameters: A JSON object with any of the following parameters:
* `username` * `disabled`, boolean indicating if all logins from the user are rejected.
* `preferences`, a JSON object which the server stores without interpreting.
This field is meant for user-level preferences meaningful to the UI.
* `password` * `password`
* on retrieval, a placeholder string to indicate a password is set, * on retrieval, a placeholder string to indicate a password is set,
or null. or null.
@ -910,6 +909,9 @@ A JSON object with any of the following parameters:
* in updates, may be left absent to keep as-is, set to null to disable * in updates, may be left absent to keep as-is, set to null to disable
session creation, or set to a plaintext string. session creation, or set to a plaintext string.
* `permissions`, a `Permissions` as described below. * `permissions`, a `Permissions` as described below.
* `preferences`, a JSON object which the server stores without interpreting.
This field is meant for user-level preferences meaningful to the UI.
* `username`
### Permissions ### Permissions

View File

@ -552,6 +552,8 @@ pub struct UserSubset<'a> {
#[serde(borrow)] #[serde(borrow)]
pub username: Option<&'a str>, pub username: Option<&'a str>,
pub disabled: Option<bool>,
pub preferences: Option<db::json::UserPreferences>, pub preferences: Option<db::json::UserPreferences>,
/// An optional password value. /// An optional password value.
@ -568,6 +570,7 @@ impl<'a> From<&'a db::User> for UserSubset<'a> {
fn from(u: &'a db::User) -> Self { fn from(u: &'a db::User) -> Self {
Self { Self {
username: Some(&u.username), username: Some(&u.username),
disabled: Some(u.config.disabled),
preferences: Some(u.config.preferences.clone()), preferences: Some(u.config.preferences.clone()),
password: Some(u.has_password().then_some("(censored)")), password: Some(u.has_password().then_some("(censored)")),
permissions: Some(u.permissions.clone().into()), permissions: Some(u.permissions.clone().into()),

View File

@ -143,6 +143,9 @@ impl Service {
} }
require_csrf_if_session(&caller, r.csrf)?; require_csrf_if_session(&caller, r.csrf)?;
if let Some(mut precondition) = r.precondition { if let Some(mut precondition) = r.precondition {
if matches!(precondition.disabled.take(), Some(d) if d != user.config.disabled) {
bail_t!(FailedPrecondition, "disabled mismatch");
}
if matches!(precondition.username.take(), Some(n) if n != user.username) { if matches!(precondition.username.take(), Some(n) if n != user.username) {
bail_t!(FailedPrecondition, "username mismatch"); bail_t!(FailedPrecondition, "username mismatch");
} }
@ -187,6 +190,9 @@ impl Service {
if update != Default::default() && !caller.permissions.admin_users { if update != Default::default() && !caller.permissions.admin_users {
bail_t!(Unauthenticated, "must have admin_users permission"); bail_t!(Unauthenticated, "must have admin_users permission");
} }
if let Some(d) = update.disabled.take() {
change.config.disabled = d;
}
if let Some(n) = update.username.take() { if let Some(n) = update.username.take() {
change.username = n.to_string(); change.username = n.to_string();
} }