fix and improve all login and password-related workflows
This commit is contained in:
parent
10049f0bc6
commit
7fdf88eaa8
|
@ -5,9 +5,12 @@ authors = ["Andrew Reid <glyph@mycelial.technology>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-std = "1.10.0"
|
||||||
chrono = "0.4.19"
|
chrono = "0.4.19"
|
||||||
dirs = "4.0"
|
dirs = "4.0"
|
||||||
fslock="0.1.6"
|
fslock="0.1.6"
|
||||||
|
#golgi = { git = "https://git.coopcloud.tech/golgi-ssb/golgi" }
|
||||||
|
golgi = { path = "../../../playground/rust/golgi" }
|
||||||
jsonrpc-client-core = "0.5"
|
jsonrpc-client-core = "0.5"
|
||||||
jsonrpc-client-http = "0.5"
|
jsonrpc-client-http = "0.5"
|
||||||
jsonrpc-core = "8.0.1"
|
jsonrpc-core = "8.0.1"
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
|
||||||
|
|
||||||
|
- permissions for /var/lib/peachcloud
|
||||||
|
- everything fails if we don't have write permissions
|
||||||
|
- can't write config
|
||||||
|
|
||||||
|
- configure admin (add new admin)
|
||||||
|
- isn't persisted to config.yml
|
||||||
|
- on second try, it is persisted
|
||||||
|
|
||||||
|
- password reset via ssb pm is now working
|
||||||
|
- it sends a temporary password but says nothing about username
|
||||||
|
- what is the default username?
|
||||||
|
- why do we even have a username?
|
||||||
|
|
||||||
|
- login with temporary password fails
|
||||||
|
- "Invalid password: Password error: hash value in YAML configuration file is empty."
|
||||||
|
|
||||||
|
- things are generally working now :)
|
||||||
|
|
||||||
|
- for all form inputs:
|
||||||
|
- use a proper label (not just a placeholder)
|
||||||
|
- login
|
|
@ -1,12 +1,14 @@
|
||||||
//! Interfaces for writing and reading PeachCloud configurations, stored in yaml.
|
//! Interfaces for writing and reading PeachCloud configurations, stored in yaml.
|
||||||
//!
|
//!
|
||||||
//! Different PeachCloud microservices import peach-lib, so that they can share this interface.
|
//! Different PeachCloud microservices import peach-lib, so that they can share
|
||||||
|
//! this interface.
|
||||||
//!
|
//!
|
||||||
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
|
//! The configuration file is located at: "/var/lib/peachcloud/config.yml"
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use fslock::LockFile;
|
use fslock::LockFile;
|
||||||
|
use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::error::PeachError;
|
use crate::error::PeachError;
|
||||||
|
@ -72,6 +74,7 @@ pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
||||||
let peach_config_exists = std::path::Path::new(YAML_PATH).exists();
|
let peach_config_exists = std::path::Path::new(YAML_PATH).exists();
|
||||||
|
|
||||||
let peach_config: PeachConfig = if !peach_config_exists {
|
let peach_config: PeachConfig = if !peach_config_exists {
|
||||||
|
debug!("Loading peach config: {} does not exist", YAML_PATH);
|
||||||
PeachConfig {
|
PeachConfig {
|
||||||
external_domain: "".to_string(),
|
external_domain: "".to_string(),
|
||||||
dyn_domain: "".to_string(),
|
dyn_domain: "".to_string(),
|
||||||
|
@ -87,6 +90,7 @@ pub fn load_peach_config() -> Result<PeachConfig, PeachError> {
|
||||||
}
|
}
|
||||||
// otherwise we load peach config from disk
|
// otherwise we load peach config from disk
|
||||||
else {
|
else {
|
||||||
|
debug!("Loading peach config: {} exists", YAML_PATH);
|
||||||
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
|
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
|
||||||
source,
|
source,
|
||||||
path: YAML_PATH.to_string(),
|
path: YAML_PATH.to_string(),
|
||||||
|
@ -177,6 +181,7 @@ pub fn set_admin_password_hash(password_hash: &str) -> Result<PeachConfig, Peach
|
||||||
|
|
||||||
pub fn get_admin_password_hash() -> Result<String, PeachError> {
|
pub fn get_admin_password_hash() -> Result<String, PeachError> {
|
||||||
let peach_config = load_peach_config()?;
|
let peach_config = load_peach_config()?;
|
||||||
|
debug!("Admin password hash: {}", peach_config.admin_password_hash);
|
||||||
if !peach_config.admin_password_hash.is_empty() {
|
if !peach_config.admin_password_hash.is_empty() {
|
||||||
Ok(peach_config.admin_password_hash)
|
Ok(peach_config.admin_password_hash)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -61,11 +61,8 @@ pub enum PeachError {
|
||||||
/// Represents a failure to parse or compile a regular expression.
|
/// Represents a failure to parse or compile a regular expression.
|
||||||
Regex(regex::Error),
|
Regex(regex::Error),
|
||||||
|
|
||||||
/// Represents a failure to successfully execute an sbot command.
|
/// Represents a failure to successfully execute an sbot command (via golgi).
|
||||||
SbotCli {
|
Sbot(String),
|
||||||
/// The `stderr` output from the sbot command.
|
|
||||||
msg: String,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Represents a failure to serialize or deserialize JSON.
|
/// Represents a failure to serialize or deserialize JSON.
|
||||||
SerdeJson(serde_json::error::Error),
|
SerdeJson(serde_json::error::Error),
|
||||||
|
@ -117,7 +114,7 @@ impl std::error::Error for PeachError {
|
||||||
PeachError::PasswordNotSet => None,
|
PeachError::PasswordNotSet => None,
|
||||||
PeachError::Read { ref source, .. } => Some(source),
|
PeachError::Read { ref source, .. } => Some(source),
|
||||||
PeachError::Regex(_) => None,
|
PeachError::Regex(_) => None,
|
||||||
PeachError::SbotCli { .. } => None,
|
PeachError::Sbot(_) => None,
|
||||||
PeachError::SerdeJson(_) => None,
|
PeachError::SerdeJson(_) => None,
|
||||||
PeachError::SerdeYaml(_) => None,
|
PeachError::SerdeYaml(_) => None,
|
||||||
PeachError::SsbAdminIdNotFound { .. } => None,
|
PeachError::SsbAdminIdNotFound { .. } => None,
|
||||||
|
@ -153,22 +150,19 @@ impl std::fmt::Display for PeachError {
|
||||||
write!(f, "Date/time parse error: {}", path)
|
write!(f, "Date/time parse error: {}", path)
|
||||||
}
|
}
|
||||||
PeachError::PasswordIncorrect => {
|
PeachError::PasswordIncorrect => {
|
||||||
write!(f, "Password error: user-supplied password is incorrect")
|
write!(f, "password is incorrect")
|
||||||
}
|
}
|
||||||
PeachError::PasswordMismatch => {
|
PeachError::PasswordMismatch => {
|
||||||
write!(f, "Password error: user-supplied passwords do not match")
|
write!(f, "passwords do not match")
|
||||||
}
|
}
|
||||||
PeachError::PasswordNotSet => {
|
PeachError::PasswordNotSet => {
|
||||||
write!(
|
write!(f, "hash value in YAML configuration file is empty")
|
||||||
f,
|
|
||||||
"Password error: hash value in YAML configuration file is empty"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
PeachError::Read { ref path, .. } => {
|
PeachError::Read { ref path, .. } => {
|
||||||
write!(f, "Read error: {}", path)
|
write!(f, "Read error: {}", path)
|
||||||
}
|
}
|
||||||
PeachError::Regex(ref err) => err.fmt(f),
|
PeachError::Regex(ref err) => err.fmt(f),
|
||||||
PeachError::SbotCli { ref msg } => {
|
PeachError::Sbot(ref msg) => {
|
||||||
write!(f, "Sbot error: {}", msg)
|
write!(f, "Sbot error: {}", msg)
|
||||||
}
|
}
|
||||||
PeachError::SerdeJson(ref err) => err.fmt(f),
|
PeachError::SerdeJson(ref err) => err.fmt(f),
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
|
use async_std::task;
|
||||||
|
use golgi::Sbot;
|
||||||
|
use log::debug;
|
||||||
use nanorand::{Rng, WyRand};
|
use nanorand::{Rng, WyRand};
|
||||||
use sha3::{Digest, Sha3_256};
|
use sha3::{Digest, Sha3_256};
|
||||||
|
|
||||||
use crate::{config_manager, error::PeachError};
|
use crate::{config_manager, error::PeachError, sbot::SbotConfig};
|
||||||
|
|
||||||
/// Returns Ok(()) if the supplied password is correct,
|
/// Returns Ok(()) if the supplied password is correct,
|
||||||
/// and returns Err if the supplied password is incorrect.
|
/// and returns Err if the supplied password is incorrect.
|
||||||
|
@ -102,8 +105,37 @@ using this link: http://peach.local/reset_password",
|
||||||
// finally send the message to the admins
|
// finally send the message to the admins
|
||||||
let peach_config = config_manager::load_peach_config()?;
|
let peach_config = config_manager::load_peach_config()?;
|
||||||
for ssb_admin_id in peach_config.ssb_admin_ids {
|
for ssb_admin_id in peach_config.ssb_admin_ids {
|
||||||
// TODO: replace with golgi
|
// use golgi to send a private message on scuttlebutt
|
||||||
//sbot_client::private_message(&msg, &ssb_admin_id)?;
|
match task::block_on(publish_private_msg(&msg, &ssb_admin_id)) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => return Err(PeachError::Sbot(e)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn publish_private_msg(msg: &str, recipient: &str) -> Result<(), String> {
|
||||||
|
// retrieve latest go-sbot configuration parameters
|
||||||
|
let sbot_config = SbotConfig::read().ok();
|
||||||
|
|
||||||
|
let msg = msg.to_string();
|
||||||
|
let recipient = vec![recipient.to_string()];
|
||||||
|
|
||||||
|
// initialise sbot connection with ip:port and shscap from config file
|
||||||
|
let mut sbot_client = match sbot_config {
|
||||||
|
// TODO: panics if we pass `Some(conf.shscap)` as second arg
|
||||||
|
Some(conf) => {
|
||||||
|
let ip_port = conf.lis.clone();
|
||||||
|
Sbot::init(Some(ip_port), None)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
}
|
||||||
|
None => Sbot::init(None, None).await.map_err(|e| e.to_string())?,
|
||||||
|
};
|
||||||
|
|
||||||
|
debug!("Publishing a Scuttlebutt private message with temporary password");
|
||||||
|
match sbot_client.publish_private(msg, recipient).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(format!("Failed to publish private message: {}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -31,9 +31,6 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
login,
|
login,
|
||||||
login_post,
|
login_post,
|
||||||
logout,
|
logout,
|
||||||
reboot_cmd,
|
|
||||||
shutdown_cmd,
|
|
||||||
power_menu,
|
|
||||||
settings_menu,
|
settings_menu,
|
||||||
set_theme,
|
set_theme,
|
||||||
],
|
],
|
||||||
|
@ -43,7 +40,6 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
routes![
|
routes![
|
||||||
admin_menu,
|
admin_menu,
|
||||||
configure_admin,
|
configure_admin,
|
||||||
add_admin,
|
|
||||||
add_admin_post,
|
add_admin_post,
|
||||||
delete_admin_post,
|
delete_admin_post,
|
||||||
change_password,
|
change_password,
|
||||||
|
@ -101,6 +97,7 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
/// required to run a complete PeachCloud build.
|
/// required to run a complete PeachCloud build.
|
||||||
pub fn mount_peachcloud_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
pub fn mount_peachcloud_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||||
mount_peachpub_routes(rocket)
|
mount_peachpub_routes(rocket)
|
||||||
|
.mount("/", routes![reboot_cmd, shutdown_cmd, power_menu,])
|
||||||
.mount(
|
.mount(
|
||||||
"/settings/network",
|
"/settings/network",
|
||||||
routes![
|
routes![
|
||||||
|
|
|
@ -6,7 +6,6 @@ use rocket::{
|
||||||
post,
|
post,
|
||||||
request::{self, FlashMessage, FromRequest, Request},
|
request::{self, FlashMessage, FromRequest, Request},
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
serde::Deserialize,
|
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::{tera::Context, Template};
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
|
|
||||||
|
@ -89,17 +88,14 @@ pub fn login(flash: Option<FlashMessage>) -> Template {
|
||||||
Template::render("login", &context.into_json())
|
Template::render("login", &context.into_json())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, FromForm)]
|
||||||
pub struct LoginForm {
|
pub struct LoginForm {
|
||||||
pub username: String,
|
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes in a LoginForm and returns Ok(()) if username and password
|
/// Takes in a LoginForm and returns Ok(()) if the password is correct.
|
||||||
/// are correct to authenticate with peach-web.
|
|
||||||
///
|
///
|
||||||
/// Note: currently there is only one user, and the username should always
|
/// Note: there is currently only one user, therefore we don't need a username.
|
||||||
/// be "admin".
|
|
||||||
pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> {
|
pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> {
|
||||||
password_utils::verify_password(&login_form.password)
|
password_utils::verify_password(&login_form.password)
|
||||||
}
|
}
|
||||||
|
@ -117,13 +113,14 @@ pub fn login_post(login_form: Form<LoginForm>, cookies: &CookieJar<'_>) -> Templ
|
||||||
|
|
||||||
TemplateOrRedirect::Redirect(Redirect::to("/"))
|
TemplateOrRedirect::Redirect(Redirect::to("/"))
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(e) => {
|
||||||
|
let err_msg = format!("Invalid password: {}", e);
|
||||||
// if unsuccessful login, render /login page again
|
// if unsuccessful login, render /login page again
|
||||||
let mut context = Context::new();
|
let mut context = Context::new();
|
||||||
context.insert("back", &Some("/".to_string()));
|
context.insert("back", &Some("/".to_string()));
|
||||||
context.insert("title", &Some("Login".to_string()));
|
context.insert("title", &Some("Login".to_string()));
|
||||||
context.insert("flash_name", &("error".to_string()));
|
context.insert("flash_name", &("error".to_string()));
|
||||||
context.insert("flash_msg", &("Invalid password".to_string()));
|
context.insert("flash_msg", &(err_msg));
|
||||||
|
|
||||||
TemplateOrRedirect::Template(Template::render("login", &context.into_json()))
|
TemplateOrRedirect::Template(Template::render("login", &context.into_json()))
|
||||||
}
|
}
|
||||||
|
@ -142,7 +139,7 @@ pub fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /reset_password
|
// HELPERS AND ROUTES FOR /reset_password
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, FromForm)]
|
||||||
pub struct ResetPasswordForm {
|
pub struct ResetPasswordForm {
|
||||||
pub temporary_password: String,
|
pub temporary_password: String,
|
||||||
pub new_password1: String,
|
pub new_password1: String,
|
||||||
|
@ -198,7 +195,7 @@ pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Temp
|
||||||
let (flash_name, flash_msg) = match save_reset_password_form(reset_password_form.into_inner()) {
|
let (flash_name, flash_msg) = match save_reset_password_form(reset_password_form.into_inner()) {
|
||||||
Ok(_) => (
|
Ok(_) => (
|
||||||
"success".to_string(),
|
"success".to_string(),
|
||||||
"New password is now saved. Return home to login".to_string(),
|
"New password has been saved. Return home to login".to_string(),
|
||||||
),
|
),
|
||||||
Err(err) => (
|
Err(err) => (
|
||||||
"error".to_string(),
|
"error".to_string(),
|
||||||
|
@ -266,9 +263,9 @@ pub fn send_password_reset_post() -> Template {
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/change_password
|
// HELPERS AND ROUTES FOR /settings/change_password
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, FromForm)]
|
||||||
pub struct PasswordForm {
|
pub struct PasswordForm {
|
||||||
pub old_password: String,
|
pub current_password: String,
|
||||||
pub new_password1: String,
|
pub new_password1: String,
|
||||||
pub new_password2: String,
|
pub new_password2: String,
|
||||||
}
|
}
|
||||||
|
@ -277,9 +274,9 @@ pub struct PasswordForm {
|
||||||
pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> {
|
pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> {
|
||||||
info!(
|
info!(
|
||||||
"change password!: {} {} {}",
|
"change password!: {} {} {}",
|
||||||
password_form.old_password, password_form.new_password1, password_form.new_password2
|
password_form.current_password, password_form.new_password1, password_form.new_password2
|
||||||
);
|
);
|
||||||
password_utils::verify_password(&password_form.old_password)?;
|
password_utils::verify_password(&password_form.current_password)?;
|
||||||
// if the previous line did not throw an error, then the old password is correct
|
// if the previous line did not throw an error, then the old password is correct
|
||||||
password_utils::validate_new_passwords(
|
password_utils::validate_new_passwords(
|
||||||
&password_form.new_password1,
|
&password_form.new_password1,
|
||||||
|
@ -321,7 +318,7 @@ pub fn change_password_post(password_form: Form<PasswordForm>, _auth: Authentica
|
||||||
let (flash_name, flash_msg) = match save_password_form(password_form.into_inner()) {
|
let (flash_name, flash_msg) = match save_password_form(password_form.into_inner()) {
|
||||||
Ok(_) => (
|
Ok(_) => (
|
||||||
"success".to_string(),
|
"success".to_string(),
|
||||||
"New password is now saved".to_string(),
|
"New password has been saved".to_string(),
|
||||||
),
|
),
|
||||||
Err(err) => (
|
Err(err) => (
|
||||||
"error".to_string(),
|
"error".to_string(),
|
||||||
|
|
|
@ -3,7 +3,6 @@ use rocket::{
|
||||||
get, post,
|
get, post,
|
||||||
request::FlashMessage,
|
request::FlashMessage,
|
||||||
response::{Flash, Redirect},
|
response::{Flash, Redirect},
|
||||||
serde::Deserialize,
|
|
||||||
uri,
|
uri,
|
||||||
};
|
};
|
||||||
use rocket_dyn_templates::{tera::Context, Template};
|
use rocket_dyn_templates::{tera::Context, Template};
|
||||||
|
@ -77,50 +76,31 @@ pub fn configure_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Tem
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/admin/add
|
// HELPERS AND ROUTES FOR /settings/admin/add
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, FromForm)]
|
||||||
pub struct AddAdminForm {
|
pub struct AddAdminForm {
|
||||||
pub ssb_id: String,
|
pub ssb_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
||||||
let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?;
|
let _result = config_manager::add_ssb_admin_id(&admin_form.ssb_id)?;
|
||||||
|
|
||||||
// if the previous line didn't throw an error then it was a success
|
// if the previous line didn't throw an error then it was a success
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/add")]
|
|
||||||
pub fn add_admin(flash: Option<FlashMessage>, _auth: Authenticated) -> Template {
|
|
||||||
// retrieve current ui theme
|
|
||||||
let theme = utils::get_theme();
|
|
||||||
|
|
||||||
let mut context = Context::new();
|
|
||||||
context.insert("theme", &theme);
|
|
||||||
context.insert("back", &Some("/settings/admin/configure".to_string()));
|
|
||||||
context.insert("title", &Some("Add Admin".to_string()));
|
|
||||||
|
|
||||||
// check to see if there is a flash message to display
|
|
||||||
if let Some(flash) = flash {
|
|
||||||
context.insert("flash_name", &Some(flash.kind().to_string()));
|
|
||||||
context.insert("flash_msg", &Some(flash.message().to_string()));
|
|
||||||
};
|
|
||||||
|
|
||||||
// template_dir is set in Rocket.toml
|
|
||||||
Template::render("settings/admin/add_admin", &context.into_json())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[post("/add", data = "<add_admin_form>")]
|
#[post("/add", data = "<add_admin_form>")]
|
||||||
pub fn add_admin_post(add_admin_form: Form<AddAdminForm>, _auth: Authenticated) -> Flash<Redirect> {
|
pub fn add_admin_post(add_admin_form: Form<AddAdminForm>, _auth: Authenticated) -> Flash<Redirect> {
|
||||||
let result = save_add_admin_form(add_admin_form.into_inner());
|
let result = save_add_admin_form(add_admin_form.into_inner());
|
||||||
let url = uri!("/settings/admin/configure");
|
let url = uri!("/settings/admin/configure");
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "Successfully added new admin"),
|
Ok(_) => Flash::success(Redirect::to(url), "Added SSB administrator"),
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to add new admin"),
|
Err(e) => Flash::error(Redirect::to(url), format!("Failed to add new admin: {}", e)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// HELPERS AND ROUTES FOR /settings/admin/delete
|
// HELPERS AND ROUTES FOR /settings/admin/delete
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, FromForm)]
|
#[derive(Debug, FromForm)]
|
||||||
pub struct DeleteAdminForm {
|
pub struct DeleteAdminForm {
|
||||||
pub ssb_id: String,
|
pub ssb_id: String,
|
||||||
}
|
}
|
||||||
|
@ -131,9 +111,12 @@ pub fn delete_admin_post(
|
||||||
_auth: Authenticated,
|
_auth: Authenticated,
|
||||||
) -> Flash<Redirect> {
|
) -> Flash<Redirect> {
|
||||||
let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id);
|
let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id);
|
||||||
let url = uri!(configure_admin);
|
let url = uri!("/settings/admin", configure_admin);
|
||||||
match result {
|
match result {
|
||||||
Ok(_) => Flash::success(Redirect::to(url), "Successfully removed admin id"),
|
Ok(_) => Flash::success(Redirect::to(url), "Removed SSB administrator"),
|
||||||
Err(_) => Flash::error(Redirect::to(url), "Failed to remove admin id"),
|
Err(e) => Flash::error(
|
||||||
|
Redirect::to(url),
|
||||||
|
format!("Failed to remove admin id: {}", e),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,22 +2,19 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- LOGIN FORM -->
|
<!-- LOGIN FORM -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="card-container">
|
<form id="login_form" class="center" action="/login" method="post">
|
||||||
<form id="login_form" class="center" action="/login" method="post">
|
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||||
<!-- input for username -->
|
|
||||||
<input id="username" name="username" class="center input" type="text" placeholder="Username" title="Username for authentication" autofocus/>
|
|
||||||
<!-- input for password -->
|
<!-- input for password -->
|
||||||
<input id="password" name="password" class="center input" type="password" placeholder="Password" title="Password for given username"/>
|
<label for="password" class="center label-small font-gray" style="width: 80%;">PASSWORD</label>
|
||||||
<div id="buttonDiv">
|
<input id="password" name="password" class="center input" type="password" title="Password for given username"/>
|
||||||
<input id="loginUser" class="button button-primary center" title="Login" type="submit" value="Login">
|
<!-- login (form submission) button -->
|
||||||
|
<input id="loginUser" class="button button-primary center" title="Login" type="submit" value="Login">
|
||||||
|
<div class="center-text" style="margin-top: 1rem;">
|
||||||
|
<a href="/settings/admin/forgot_password" class="label-small link font-gray">Forgot Password?</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
{% include "snippets/flash_message" %}
|
{% include "snippets/flash_message" %}
|
||||||
<div class="center-text" style="margin-top: 25px;">
|
</form>
|
||||||
<a href="/settings/admin/forgot_password" class="label-small link font-gray">Forgot Password?</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
{%- extends "nav" -%}
|
|
||||||
{%- block card %}
|
|
||||||
<!-- ADD ADMIN FORM -->
|
|
||||||
<div class="card center">
|
|
||||||
<div class="card-container">
|
|
||||||
<form id="addAdminForm" action="/settings/admin/add" method="post">
|
|
||||||
<input id="ssb_id" name="ssb_id" class="center input" type="text" placeholder="SSB ID" title="SSB ID of Admin" value=""/>
|
|
||||||
<div id="buttonDiv">
|
|
||||||
<input id="addAdmin" class="button button-primary center" title="Add" type="submit" value="Add">
|
|
||||||
<a class="button button-secondary center" href="/settings/admin/configure" title="Cancel">Cancel</a>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- FLASH MESSAGE -->
|
|
||||||
{% include "snippets/flash_message" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{%- endblock card -%}
|
|
|
@ -3,13 +3,17 @@
|
||||||
<!-- CHANGE PASSWORD FORM -->
|
<!-- CHANGE PASSWORD FORM -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<form id="changePassword" class="center" action="/settings/admin/change_password" method="post">
|
<form id="changePassword" class="center" action="/settings/admin/change_password" method="post">
|
||||||
<!-- input for current password -->
|
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||||
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
<!-- input for current password -->
|
||||||
<!-- input for new password -->
|
<label for="currentPassword" class="center label-small font-gray" style="width: 80%;">CURRENT PASSWORD</label>
|
||||||
<input id="newPassword" class="center input" name="new_password1" type="password" placeholder="New password" title="New password">
|
<input id="currentPassword" class="center input" name="current_password" type="password" title="Current password" autofocus>
|
||||||
<!-- input for duplicate new password -->
|
<!-- input for new password -->
|
||||||
<input id="newPasswordDuplicate" class="center input" name="new_password2" type="password" placeholder="Re-enter new password" title="New password duplicate">
|
<label for="newPassword" class="center label-small font-gray" style="width: 80%;">NEW PASSWORD</label>
|
||||||
<div id="buttonDiv">
|
<input id="newPassword" class="center input" name="new_password1" type="password" title="New password">
|
||||||
|
<!-- input for duplicate new password -->
|
||||||
|
<label for="newPasswordDuplicate" class="center label-small font-gray" style="width: 80%;">RE-ENTER NEW PASSWORD</label>
|
||||||
|
<input id="newPasswordDuplicate" class="center input" name="new_password2" type="password" title="New password duplicate">
|
||||||
|
<!-- save (form submission) button -->
|
||||||
<input id="savePassword" class="button button-primary center" title="Add" type="submit" value="Save">
|
<input id="savePassword" class="button button-primary center" title="Add" type="submit" value="Save">
|
||||||
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,25 +2,31 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- CONFIGURE ADMIN PAGE -->
|
<!-- CONFIGURE ADMIN PAGE -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="text-container">
|
<div class="capsule capsule-profile center-text font-normal border-info" style="font-family: var(--sans-serif); font-size: var(--font-size-6); margin-bottom: 1.5rem;">Administrators are identified and added by their Scuttlebutt public keys. These accounts will be sent private messages on Scuttlebutt when a password reset is requested.</div>
|
||||||
<h4 class="font-normal">Current Admins</h4>
|
{% if not ssb_admin_ids %}
|
||||||
{% if not ssb_admin_ids %}
|
<div class="card-text">
|
||||||
<div class="card-text">
|
There are no currently configured admins.
|
||||||
There are no currently configured admins.
|
</div>
|
||||||
</div>
|
{% else %}
|
||||||
{% else %}
|
{% for admin in ssb_admin_ids %}
|
||||||
{% for admin in ssb_admin_ids %}
|
<form class="center" action="/settings/admin/delete" method="post">
|
||||||
<div>
|
<div class="center" style="display: flex; justify-content: space-between;">
|
||||||
<form action="/settings/admin/delete" method="post">
|
<input type="hidden" name="ssb_id" value="{{ admin }}"/>
|
||||||
<input type="hidden" name="ssb_id" value="{{admin}}"/>
|
<p class="label-small label-ellipsis font-gray" style="user-select: all;">{{ admin }}</p>
|
||||||
<input type="submit" value="X" title="Delete"/> <span>{{ admin }}</span>
|
<input style="width: 30%;" type="submit" class="button button-warning" value="Delete" title="Delete SSB administrator"/>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
</form>
|
||||||
{% endif %}
|
{% endfor %}
|
||||||
<a class="button button-primary center full-width" style="margin-top: 25px;" href="/settings/admin/add" title="Add Admin">Add Admin</a>
|
{% endif %}
|
||||||
</div>
|
<form id="addAdmin" class="center" style="margin-top: 2rem;" action="/settings/admin/add" method="post">
|
||||||
<!-- FLASH MESSAGE -->
|
<div class="center" style="display: flex; flex-direction: column; margin-bottom: 2rem;" title="Public key (ID) of a desired administrator">
|
||||||
{% include "snippets/flash_message" %}
|
<label for="publicKey" class="label-small font-gray">PUBLIC KEY</label>
|
||||||
|
<input type="text" id="publicKey" name="ssb_id" placeholder="@xYz...=.ed25519" autofocus>
|
||||||
|
</div>
|
||||||
|
<!-- BUTTONS -->
|
||||||
|
<input class="button button-primary center" type="submit" title="Add SSB administrator" value="Add Admin">
|
||||||
|
<!-- FLASH MESSAGE -->
|
||||||
|
{% include "snippets/flash_message" %}
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- PASSWORD RESET REQUEST CARD -->
|
<!-- PASSWORD RESET REQUEST CARD -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="capsule capsule-container info-border">
|
<div class="capsule capsule-container border-info">
|
||||||
<p class="card-text">Click the button below to send a new temporary password which can be used to change your device password.
|
<p class="card-text">Click the 'Send Password Reset' button to send a new temporary password which can be used to change your device password.</p>
|
||||||
</br></br>
|
<p class="card-text" style="margin-top: 1rem;">The temporary password will be sent in an SSB private message to the admin of this device.</p>
|
||||||
The temporary password will be sent in an SSB private message to the admin of this device.</p>
|
<p class="card-text" style="margin-top: 1rem;">Once you have the temporary password, click the 'Set New Password' button to reach the password reset page.</p>
|
||||||
</div>
|
</div>
|
||||||
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
<form id="sendPasswordReset" action="/settings/admin/send_password_reset" method="post">
|
||||||
<div id="buttonDiv">
|
<div id="buttonDiv">
|
||||||
<input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Password Reset" title="Send Password Reset Link"/>
|
<input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Password Reset" title="Send password reset link"/>
|
||||||
|
<a href="/settings/admin/reset_password" class="button button-primary center" title="Set a new password using the temporary password">Set New Password</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<!-- FLASH MESSAGE -->
|
<!-- FLASH MESSAGE -->
|
||||||
|
|
|
@ -2,41 +2,22 @@
|
||||||
{%- block card %}
|
{%- block card %}
|
||||||
<!-- RESET PASSWORD PAGE -->
|
<!-- RESET PASSWORD PAGE -->
|
||||||
<div class="card center">
|
<div class="card center">
|
||||||
<div class="form-container">
|
<form id="changePassword" class="center" action="/settings/admin/reset_password" method="post">
|
||||||
<form id="changePassword" action="/reset_password" method="post">
|
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||||
<div class="input-wrapper">
|
<!-- input for temporary password -->
|
||||||
<!-- input for temporary password -->
|
<label class="center label-small font-gray" style="width: 80%;" for="temporary_password">TEMPORARY PASSWORD</label>
|
||||||
<label id="temporary_password" class="label-small input-label font-near-black">
|
<input id="temporary_password" class="center input" name="temporary_password" type="password" title="temporary password" value="">
|
||||||
<label class="label-small input-label font-gray" for="temporary_password" style="padding-top: 0.25rem;">Temporary Password</label>
|
<!-- input for new password1 -->
|
||||||
<input id="temporary_password" class="form-input" style="margin-bottom: 0;"
|
<label class="center label-small font-gray" style="width: 80%;" for="new_password1">NEW PASSWORD</label>
|
||||||
name="temporary_password" type="password" title="temporary password" value="">
|
<input id="new_password1" class="center input" name="new_password1" title="new_password1" type="password" value="">
|
||||||
</label>
|
<!-- input for new password2 -->
|
||||||
</div>
|
<label class="center label-small font-gray" style="width: 80%;" for="new_password2">RE-ENTER NEW PASSWORD</label>
|
||||||
|
<input id="new_password2" class="center input" name="new_password2" title="new_password2" type="password" value="">
|
||||||
<div class="input-wrapper">
|
<!-- save (form submission) button -->
|
||||||
<!-- input for new password1 -->
|
<input id="changePasswordButton" class="button button-primary center" title="Add" type="submit" value="Save">
|
||||||
<label id="new_password1" class="label-small input-label font-near-black">
|
</div>
|
||||||
<label class="label-small input-label font-gray" for="new_password1" style="padding-top: 0.25rem;">Enter New Password</label>
|
</form>
|
||||||
<input id="new_password1" class="form-input" style="margin-bottom: 0;"
|
<!-- FLASH MESSAGE -->
|
||||||
name="new_password1" title="new_password1" type="password" value="">
|
{% include "snippets/flash_message" %}
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-wrapper">
|
|
||||||
<!-- input for new password2 -->
|
|
||||||
<label id="new_password2" class="label-small input-label font-near-black">
|
|
||||||
<label class="label-small input-label font-gray" for="new_password2" style="padding-top: 0.25rem;">Re-Enter New Password</label>
|
|
||||||
<input id="new_password2" class="form-input" style="margin-bottom: 0;"
|
|
||||||
name="new_password2" title="new_password2" type="password" value="">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="buttonDiv">
|
|
||||||
<input id="changePasswordButton" class="button button-primary center" title="Add" type="submit" value="Save">
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- FLASH MESSAGE -->
|
|
||||||
{% include "snippets/flash_message" %}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{%- endblock card -%}
|
{%- endblock card -%}
|
||||||
|
|
Loading…
Reference in New Issue