mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-12-09 00:56:30 -05:00
more user admin actions
This commit is contained in:
@@ -7,15 +7,23 @@
|
||||
use base::{bail_t, format_err_t};
|
||||
use http::{Method, Request, StatusCode};
|
||||
|
||||
use crate::json::{self, UserSubset};
|
||||
use crate::json::{self, PutUsersResponse, UserSubset};
|
||||
|
||||
use super::{
|
||||
bad_req, csrf_matches, extract_json_body, internal_server_err, plain_response, serve_json,
|
||||
Caller, ResponseResult, Service,
|
||||
bad_req, csrf_matches, extract_json_body, plain_response, serve_json, Caller, ResponseResult,
|
||||
Service,
|
||||
};
|
||||
|
||||
impl Service {
|
||||
pub(super) async fn users(&self, req: Request<hyper::Body>, caller: Caller) -> ResponseResult {
|
||||
match *req.method() {
|
||||
Method::GET | Method::HEAD => self.get_users(req, caller).await,
|
||||
Method::PUT => self.put_users(req, caller).await,
|
||||
_ => Err(plain_response(StatusCode::METHOD_NOT_ALLOWED, "POST expected").into()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_users(&self, req: Request<hyper::Body>, caller: Caller) -> ResponseResult {
|
||||
if !caller.permissions.admin_users {
|
||||
bail_t!(Unauthenticated, "must have admin_users permission");
|
||||
}
|
||||
@@ -26,7 +34,31 @@ impl Service {
|
||||
.iter()
|
||||
.map(|(&id, user)| (id, user.username.clone()))
|
||||
.collect();
|
||||
serve_json(&req, &json::UsersResponse { users })
|
||||
serve_json(&req, &json::GetUsersResponse { users })
|
||||
}
|
||||
|
||||
async fn put_users(&self, mut req: Request<hyper::Body>, caller: Caller) -> ResponseResult {
|
||||
if !caller.permissions.admin_users {
|
||||
bail_t!(Unauthenticated, "must have admin_users permission");
|
||||
}
|
||||
let r = extract_json_body(&mut req).await?;
|
||||
let r: json::UserSubset = serde_json::from_slice(&r).map_err(|e| bad_req(e.to_string()))?;
|
||||
let username = r
|
||||
.username
|
||||
.ok_or_else(|| format_err_t!(InvalidArgument, "username must be specified"))?;
|
||||
let mut change = db::UserChange::add_user(username.to_owned());
|
||||
if let Some(Some(pwd)) = r.password {
|
||||
change.set_password(pwd.to_owned());
|
||||
}
|
||||
if let Some(preferences) = r.preferences {
|
||||
change.config.preferences = preferences;
|
||||
}
|
||||
if let Some(ref permissions) = r.permissions {
|
||||
change.permissions = permissions.into();
|
||||
}
|
||||
let mut l = self.db.lock();
|
||||
let user = l.apply_user_change(change)?;
|
||||
serve_json(&req, &PutUsersResponse { id: user.id })
|
||||
}
|
||||
|
||||
pub(super) async fn user(
|
||||
@@ -35,26 +67,23 @@ impl Service {
|
||||
caller: Caller,
|
||||
id: i32,
|
||||
) -> ResponseResult {
|
||||
if caller.user.as_ref().map(|u| u.id) != Some(id) && !caller.permissions.admin_users {
|
||||
bail_t!(
|
||||
Unauthenticated,
|
||||
"must be authenticated as supplied user or have admin_users permission"
|
||||
);
|
||||
}
|
||||
match *req.method() {
|
||||
Method::GET | Method::HEAD => self.get_user(req, id).await,
|
||||
Method::GET | Method::HEAD => self.get_user(req, caller, id).await,
|
||||
Method::DELETE => self.delete_user(caller, id).await,
|
||||
Method::POST => self.post_user(req, caller, id).await,
|
||||
_ => Err(plain_response(StatusCode::METHOD_NOT_ALLOWED, "POST expected").into()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_user(&self, req: Request<hyper::Body>, id: i32) -> ResponseResult {
|
||||
async fn get_user(&self, req: Request<hyper::Body>, caller: Caller, id: i32) -> ResponseResult {
|
||||
require_same_or_admin(&caller, id)?;
|
||||
let db = self.db.lock();
|
||||
let user = db
|
||||
.users_by_id()
|
||||
.get(&id)
|
||||
.ok_or_else(|| format_err_t!(NotFound, "can't find requested user"))?;
|
||||
let out = UserSubset {
|
||||
username: Some(&user.username),
|
||||
preferences: Some(user.config.preferences.clone()),
|
||||
password: Some(if user.has_password() {
|
||||
Some("(censored)")
|
||||
@@ -66,12 +95,22 @@ impl Service {
|
||||
serve_json(&req, &out)
|
||||
}
|
||||
|
||||
async fn delete_user(&self, caller: Caller, id: i32) -> ResponseResult {
|
||||
if !caller.permissions.admin_users {
|
||||
bail_t!(Unauthenticated, "must have admin_users permission");
|
||||
}
|
||||
let mut l = self.db.lock();
|
||||
l.delete_user(id)?;
|
||||
Ok(plain_response(StatusCode::NO_CONTENT, &b""[..]))
|
||||
}
|
||||
|
||||
async fn post_user(
|
||||
&self,
|
||||
mut req: Request<hyper::Body>,
|
||||
caller: Caller,
|
||||
id: i32,
|
||||
) -> ResponseResult {
|
||||
require_same_or_admin(&caller, id)?;
|
||||
let r = extract_json_body(&mut req).await?;
|
||||
let r: json::PostUser = serde_json::from_slice(&r).map_err(|e| bad_req(e.to_string()))?;
|
||||
let mut db = self.db.lock();
|
||||
@@ -128,8 +167,18 @@ impl Service {
|
||||
if let Some(ref permissions) = update.permissions {
|
||||
change.permissions = permissions.into();
|
||||
}
|
||||
db.apply_user_change(change).map_err(internal_server_err)?;
|
||||
db.apply_user_change(change)?;
|
||||
}
|
||||
Ok(plain_response(StatusCode::NO_CONTENT, &b""[..]))
|
||||
}
|
||||
}
|
||||
|
||||
fn require_same_or_admin(caller: &Caller, id: i32) -> Result<(), base::Error> {
|
||||
if caller.user.as_ref().map(|u| u.id) != Some(id) && !caller.permissions.admin_users {
|
||||
bail_t!(
|
||||
Unauthenticated,
|
||||
"must be authenticated as supplied user or have admin_users permission"
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user