"moonfire-nvr config" support for users (for #26)
This commit is contained in:
parent
f9d4b5bb8a
commit
8a5056b253
|
@ -47,6 +47,7 @@ use std::str::FromStr;
|
|||
|
||||
mod cameras;
|
||||
mod dirs;
|
||||
mod users;
|
||||
|
||||
static USAGE: &'static str = r#"
|
||||
Interactive configuration editor.
|
||||
|
@ -137,8 +138,9 @@ pub fn run() -> Result<(), Error> {
|
|||
let db = db.clone();
|
||||
move |siv, item| item(&db, siv)
|
||||
})
|
||||
.item("Directories and retention".to_string(), dirs::top_dialog)
|
||||
.item("Cameras and streams".to_string(), cameras::top_dialog)
|
||||
.item("Directories and retention".to_string(), dirs::top_dialog)
|
||||
.item("Users".to_string(), users::top_dialog)
|
||||
)
|
||||
.button("Quit", |siv| siv.quit())
|
||||
.title("Main menu"));
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
// This file is part of Moonfire NVR, a security camera digital video recorder.
|
||||
// Copyright (C) 2017 Scott Lamb <slamb@slamb.org>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// In addition, as a special exception, the copyright holders give
|
||||
// permission to link the code of portions of this program with the
|
||||
// OpenSSL library under certain conditions as described in each
|
||||
// individual source file, and distribute linked combinations including
|
||||
// the two.
|
||||
//
|
||||
// You must obey the GNU General Public License in all respects for all
|
||||
// of the code used other than OpenSSL. If you modify file(s) with this
|
||||
// exception, you may extend this exception to your version of the
|
||||
// file(s), but you are not obligated to do so. If you do not wish to do
|
||||
// so, delete this exception statement from your version. If you delete
|
||||
// this exception statement from all source files in the program, then
|
||||
// also delete it here.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate cursive;
|
||||
|
||||
use self::cursive::Cursive;
|
||||
use self::cursive::traits::{Boxable, Identifiable};
|
||||
use self::cursive::views;
|
||||
use db;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Builds a `UserChange` from an active `edit_user_dialog`.
|
||||
fn get_change(siv: &mut Cursive, db: &db::LockedDatabase, id: Option<i32>,
|
||||
pw: PasswordChange) -> db::UserChange {
|
||||
let mut change = match id {
|
||||
Some(id) => db.users_by_id().get(&id).unwrap().change(),
|
||||
None => db::UserChange::add_user(String::new()),
|
||||
};
|
||||
change.username.clear();
|
||||
change.username += siv.find_id::<views::EditView>("username").unwrap().get_content().as_str();
|
||||
match pw {
|
||||
PasswordChange::Leave => {},
|
||||
PasswordChange::Set => {
|
||||
let pwd = siv.find_id::<views::EditView>("new_pw").unwrap().get_content();
|
||||
change.set_password(pwd.as_str().into());
|
||||
},
|
||||
PasswordChange::Clear => change.clear_password(),
|
||||
};
|
||||
change
|
||||
}
|
||||
|
||||
fn press_edit(siv: &mut Cursive, db: &Arc<db::Database>, id: Option<i32>, pw: PasswordChange) {
|
||||
let result = {
|
||||
let mut l = db.lock();
|
||||
let c = get_change(siv, &l, id, pw);
|
||||
l.apply_user_change(c).map(|_| ())
|
||||
};
|
||||
if let Err(e) = result {
|
||||
siv.add_layer(views::Dialog::text(format!("Unable to apply change: {}", e))
|
||||
.title("Error")
|
||||
.dismiss_button("Abort"));
|
||||
} else {
|
||||
siv.pop_layer(); // get rid of the add/edit user dialog.
|
||||
|
||||
// Recreate the "Edit users" dialog from scratch; it's easier than adding the new entry.
|
||||
siv.pop_layer();
|
||||
top_dialog(db, siv);
|
||||
}
|
||||
}
|
||||
|
||||
fn press_delete(siv: &mut Cursive, db: &Arc<db::Database>, id: i32, name: String) {
|
||||
siv.add_layer(views::Dialog::text(format!("Delete user {}?", name))
|
||||
.button("Delete", {
|
||||
let db = db.clone();
|
||||
move |s| actually_delete(s, &db, id)
|
||||
})
|
||||
.title("Delete user").dismiss_button("Cancel"));
|
||||
}
|
||||
|
||||
fn actually_delete(siv: &mut Cursive, db: &Arc<db::Database>, id: i32) {
|
||||
siv.pop_layer(); // get rid of the add/edit user dialog.
|
||||
let result = {
|
||||
let mut l = db.lock();
|
||||
l.delete_user(id)
|
||||
};
|
||||
if let Err(e) = result {
|
||||
siv.add_layer(views::Dialog::text(format!("Unable to delete user: {}", e))
|
||||
.title("Error")
|
||||
.dismiss_button("Abort"));
|
||||
} else {
|
||||
// Recreate the "Edit users" dialog from scratch; it's easier than adding the new entry.
|
||||
siv.pop_layer();
|
||||
top_dialog(db, siv);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum PasswordChange {
|
||||
Leave,
|
||||
Clear,
|
||||
Set,
|
||||
}
|
||||
|
||||
fn select_set(siv: &mut Cursive) {
|
||||
siv.find_id::<views::RadioButton<PasswordChange>>("pw_set").unwrap().select();
|
||||
}
|
||||
|
||||
/// Adds or updates a user.
|
||||
/// (The former if `item` is None; the latter otherwise.)
|
||||
fn edit_user_dialog(db: &Arc<db::Database>, siv: &mut Cursive, item: Option<i32>) {
|
||||
let username;
|
||||
let id_str;
|
||||
let has_password;
|
||||
let mut pw_group = views::RadioGroup::new();
|
||||
{
|
||||
let l = db.lock();
|
||||
let u = item.map(|id| l.users_by_id().get(&id).unwrap());
|
||||
username = u.map(|u| u.username.clone()).unwrap_or(String::new());
|
||||
id_str = item.map(|id| id.to_string()).unwrap_or("<new>".to_string());
|
||||
has_password = u.map(|u| u.has_password()).unwrap_or(false);
|
||||
}
|
||||
let top_list = views::ListView::new()
|
||||
.child("id", views::TextView::new(id_str))
|
||||
.child("username", views::EditView::new()
|
||||
.content(username.clone())
|
||||
.with_id("username"));
|
||||
let mut layout = views::LinearLayout::vertical()
|
||||
.child(top_list)
|
||||
.child(views::DummyView)
|
||||
.child(views::TextView::new("password"));
|
||||
|
||||
if has_password {
|
||||
layout.add_child(pw_group.button(PasswordChange::Leave, "Leave set"));
|
||||
layout.add_child(pw_group.button(PasswordChange::Clear, "Clear"));
|
||||
layout.add_child(views::LinearLayout::horizontal()
|
||||
.child(pw_group.button(PasswordChange::Set, "Set to:")
|
||||
.with_id("pw_set"))
|
||||
.child(views::DummyView)
|
||||
.child(views::EditView::new()
|
||||
.on_edit(|siv, _, _| select_set(siv))
|
||||
.with_id("new_pw")
|
||||
.full_width()));
|
||||
} else {
|
||||
layout.add_child(pw_group.button(PasswordChange::Leave, "Leave unset"));
|
||||
layout.add_child(views::LinearLayout::horizontal()
|
||||
.child(pw_group.button(PasswordChange::Set, "Reset to:")
|
||||
.with_id("pw_set"))
|
||||
.child(views::DummyView)
|
||||
.child(views::EditView::new()
|
||||
.on_edit(|siv, _, _| select_set(siv))
|
||||
.with_id("new_pw")
|
||||
.full_width()));
|
||||
}
|
||||
|
||||
let dialog = views::Dialog::around(layout);
|
||||
let dialog = if let Some(id) = item {
|
||||
dialog.title("Edit user")
|
||||
.button("Edit", {
|
||||
let db = db.clone();
|
||||
move |s| press_edit(s, &db, item, *pw_group.selection())
|
||||
})
|
||||
.button("Delete", {
|
||||
let db = db.clone();
|
||||
move |s| press_delete(s, &db, id, username.clone())
|
||||
})
|
||||
} else {
|
||||
dialog.title("Add user")
|
||||
.button("Add", {
|
||||
let db = db.clone();
|
||||
move |s| press_edit(s, &db, item, *pw_group.selection())
|
||||
})
|
||||
};
|
||||
siv.add_layer(dialog.dismiss_button("Cancel"));
|
||||
}
|
||||
|
||||
pub fn top_dialog(db: &Arc<db::Database>, siv: &mut Cursive) {
|
||||
siv.add_layer(views::Dialog::around(
|
||||
views::SelectView::new()
|
||||
.on_submit({
|
||||
let db = db.clone();
|
||||
move |siv, &item| edit_user_dialog(&db, siv, item)
|
||||
})
|
||||
.item("<new user>".to_string(), None)
|
||||
.with_all(db.lock()
|
||||
.users_by_id()
|
||||
.iter()
|
||||
.map(|(&id, user)| (format!("{}: {}", id, user.username), Some(id))))
|
||||
.full_width())
|
||||
.dismiss_button("Done")
|
||||
.title("Edit cameras"));
|
||||
}
|
Loading…
Reference in New Issue