mirror of
https://github.com/dani-garcia/vaultwarden.git
synced 2025-05-17 00:53:56 +00:00
Redesign of the admin interface.
Main changes: - Splitted up settings and users into two separate pages. - Added verified shield when the e-mail address has been verified. - Added the amount of personal items in the database to the users overview. - Added Organizations and Diagnostics pages. - Shows if DNS resolving works. - Shows if there is a posible time drift. - Shows current versions of server and web-vault. - Optimized logo-gray.png using optipng Items which can be added later: - Amount of cipher items accessible for a user, not only his personal items. - Amount of users per Org - Version update check in the diagnostics overview. - Copy/Pasteable runtime config which has sensitive data changed or removed for support questions either on the forum or github issues. - Option to delete Orgs and all its passwords (when there are no members anymore). - Etc....
This commit is contained in:
parent
4146612a32
commit
3c66deb5cc
12 changed files with 453 additions and 183 deletions
128
src/api/admin.rs
128
src/api/admin.rs
|
@ -23,7 +23,7 @@ pub fn routes() -> Vec<Route> {
|
|||
|
||||
routes![
|
||||
admin_login,
|
||||
get_users,
|
||||
get_users_json,
|
||||
post_admin_login,
|
||||
admin_page,
|
||||
invite_user,
|
||||
|
@ -36,6 +36,9 @@ pub fn routes() -> Vec<Route> {
|
|||
delete_config,
|
||||
backup_db,
|
||||
test_smtp,
|
||||
users_overview,
|
||||
organizations_overview,
|
||||
diagnostics,
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -118,7 +121,9 @@ fn _validate_token(token: &str) -> bool {
|
|||
struct AdminTemplateData {
|
||||
page_content: String,
|
||||
version: Option<&'static str>,
|
||||
users: Vec<Value>,
|
||||
users: Option<Vec<Value>>,
|
||||
organizations: Option<Vec<Value>>,
|
||||
diagnostics: Option<Value>,
|
||||
config: Value,
|
||||
can_backup: bool,
|
||||
logged_in: bool,
|
||||
|
@ -126,15 +131,59 @@ struct AdminTemplateData {
|
|||
}
|
||||
|
||||
impl AdminTemplateData {
|
||||
fn new(users: Vec<Value>) -> Self {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
page_content: String::from("admin/page"),
|
||||
page_content: String::from("admin/settings"),
|
||||
version: VERSION,
|
||||
users,
|
||||
config: CONFIG.prepare_json(),
|
||||
can_backup: *CAN_BACKUP,
|
||||
logged_in: true,
|
||||
urlpath: CONFIG.domain_path(),
|
||||
users: None,
|
||||
organizations: None,
|
||||
diagnostics: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn users(users: Vec<Value>) -> Self {
|
||||
Self {
|
||||
page_content: String::from("admin/users"),
|
||||
version: VERSION,
|
||||
users: Some(users),
|
||||
config: CONFIG.prepare_json(),
|
||||
can_backup: *CAN_BACKUP,
|
||||
logged_in: true,
|
||||
urlpath: CONFIG.domain_path(),
|
||||
organizations: None,
|
||||
diagnostics: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn organizations(organizations: Vec<Value>) -> Self {
|
||||
Self {
|
||||
page_content: String::from("admin/organizations"),
|
||||
version: VERSION,
|
||||
organizations: Some(organizations),
|
||||
config: CONFIG.prepare_json(),
|
||||
can_backup: *CAN_BACKUP,
|
||||
logged_in: true,
|
||||
urlpath: CONFIG.domain_path(),
|
||||
users: None,
|
||||
diagnostics: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn diagnostics(diagnostics: Value) -> Self {
|
||||
Self {
|
||||
page_content: String::from("admin/diagnostics"),
|
||||
version: VERSION,
|
||||
organizations: None,
|
||||
config: CONFIG.prepare_json(),
|
||||
can_backup: *CAN_BACKUP,
|
||||
logged_in: true,
|
||||
urlpath: CONFIG.domain_path(),
|
||||
users: None,
|
||||
diagnostics: Some(diagnostics),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,11 +193,8 @@ impl AdminTemplateData {
|
|||
}
|
||||
|
||||
#[get("/", rank = 1)]
|
||||
fn admin_page(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let users = User::get_all(&conn);
|
||||
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
||||
|
||||
let text = AdminTemplateData::new(users_json).render()?;
|
||||
fn admin_page(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let text = AdminTemplateData::new().render()?;
|
||||
Ok(Html(text))
|
||||
}
|
||||
|
||||
|
@ -195,13 +241,29 @@ fn logout(mut cookies: Cookies) -> Result<Redirect, ()> {
|
|||
}
|
||||
|
||||
#[get("/users")]
|
||||
fn get_users(_token: AdminToken, conn: DbConn) -> JsonResult {
|
||||
fn get_users_json(_token: AdminToken, conn: DbConn) -> JsonResult {
|
||||
let users = User::get_all(&conn);
|
||||
let users_json: Vec<Value> = users.iter().map(|u| u.to_json(&conn)).collect();
|
||||
|
||||
Ok(Json(Value::Array(users_json)))
|
||||
}
|
||||
|
||||
#[get("/users/overview")]
|
||||
fn users_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let users = User::get_all(&conn);
|
||||
let users_json: Vec<Value> = users.iter()
|
||||
.map(|u| {
|
||||
let mut usr = u.to_json(&conn);
|
||||
if let Some(ciphers) = Cipher::count_owned_by_user(&u.uuid, &conn) {
|
||||
usr["cipher_count"] = json!(ciphers);
|
||||
};
|
||||
usr
|
||||
}).collect();
|
||||
|
||||
let text = AdminTemplateData::users(users_json).render()?;
|
||||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[post("/users/<uuid>/delete")]
|
||||
fn delete_user(uuid: String, _token: AdminToken, conn: DbConn) -> EmptyResult {
|
||||
let user = match User::find_by_uuid(&uuid, &conn) {
|
||||
|
@ -242,6 +304,50 @@ fn update_revision_users(_token: AdminToken, conn: DbConn) -> EmptyResult {
|
|||
User::update_all_revisions(&conn)
|
||||
}
|
||||
|
||||
#[get("/organizations/overview")]
|
||||
fn organizations_overview(_token: AdminToken, conn: DbConn) -> ApiResult<Html<String>> {
|
||||
let organizations = Organization::get_all(&conn);
|
||||
let organizations_json: Vec<Value> = organizations.iter().map(|o| o.to_json()).collect();
|
||||
|
||||
let text = AdminTemplateData::organizations(organizations_json).render()?;
|
||||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct WebVaultVersion {
|
||||
version: String,
|
||||
}
|
||||
|
||||
#[get("/diagnostics")]
|
||||
fn diagnostics(_token: AdminToken, _conn: DbConn) -> ApiResult<Html<String>> {
|
||||
use std::net::ToSocketAddrs;
|
||||
use chrono::prelude::*;
|
||||
use crate::util::read_file_string;
|
||||
|
||||
let vault_version_path = format!("{}/{}", CONFIG.web_vault_folder(), "version.json");
|
||||
let vault_version_str = read_file_string(&vault_version_path)?;
|
||||
let web_vault_version: WebVaultVersion = serde_json::from_str(&vault_version_str)?;
|
||||
|
||||
let github_ips = ("github.com", 0).to_socket_addrs().map(|mut i| i.next());
|
||||
let dns_resolved = match github_ips {
|
||||
Ok(Some(a)) => a.ip().to_string() ,
|
||||
_ => "Could not resolve domain name.".to_string(),
|
||||
};
|
||||
|
||||
let dt = Utc::now();
|
||||
let server_time = dt.format("%Y-%m-%d %H:%M:%S").to_string();
|
||||
|
||||
let diagnostics_json = json!({
|
||||
"dns_resolved": dns_resolved,
|
||||
"server_time": server_time,
|
||||
"web_vault_version": web_vault_version.version,
|
||||
});
|
||||
|
||||
let text = AdminTemplateData::diagnostics(diagnostics_json).render()?;
|
||||
Ok(Html(text))
|
||||
}
|
||||
|
||||
#[post("/config", data = "<data>")]
|
||||
fn post_config(data: Json<ConfigBuilder>, _token: AdminToken) -> EmptyResult {
|
||||
let data: ConfigBuilder = data.into_inner();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue