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"
|
||||
|
||||
[dependencies]
|
||||
async-std = "1.10.0"
|
||||
chrono = "0.4.19"
|
||||
dirs = "4.0"
|
||||
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-http = "0.5"
|
||||
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.
|
||||
//!
|
||||
//! 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"
|
||||
|
||||
use std::fs;
|
||||
|
||||
use fslock::LockFile;
|
||||
use log::debug;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
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: PeachConfig = if !peach_config_exists {
|
||||
debug!("Loading peach config: {} does not exist", YAML_PATH);
|
||||
PeachConfig {
|
||||
external_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
|
||||
else {
|
||||
debug!("Loading peach config: {} exists", YAML_PATH);
|
||||
let contents = fs::read_to_string(YAML_PATH).map_err(|source| PeachError::Read {
|
||||
source,
|
||||
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> {
|
||||
let peach_config = load_peach_config()?;
|
||||
debug!("Admin password hash: {}", peach_config.admin_password_hash);
|
||||
if !peach_config.admin_password_hash.is_empty() {
|
||||
Ok(peach_config.admin_password_hash)
|
||||
} else {
|
||||
|
|
|
@ -61,11 +61,8 @@ pub enum PeachError {
|
|||
/// Represents a failure to parse or compile a regular expression.
|
||||
Regex(regex::Error),
|
||||
|
||||
/// Represents a failure to successfully execute an sbot command.
|
||||
SbotCli {
|
||||
/// The `stderr` output from the sbot command.
|
||||
msg: String,
|
||||
},
|
||||
/// Represents a failure to successfully execute an sbot command (via golgi).
|
||||
Sbot(String),
|
||||
|
||||
/// Represents a failure to serialize or deserialize JSON.
|
||||
SerdeJson(serde_json::error::Error),
|
||||
|
@ -117,7 +114,7 @@ impl std::error::Error for PeachError {
|
|||
PeachError::PasswordNotSet => None,
|
||||
PeachError::Read { ref source, .. } => Some(source),
|
||||
PeachError::Regex(_) => None,
|
||||
PeachError::SbotCli { .. } => None,
|
||||
PeachError::Sbot(_) => None,
|
||||
PeachError::SerdeJson(_) => None,
|
||||
PeachError::SerdeYaml(_) => None,
|
||||
PeachError::SsbAdminIdNotFound { .. } => None,
|
||||
|
@ -153,22 +150,19 @@ impl std::fmt::Display for PeachError {
|
|||
write!(f, "Date/time parse error: {}", path)
|
||||
}
|
||||
PeachError::PasswordIncorrect => {
|
||||
write!(f, "Password error: user-supplied password is incorrect")
|
||||
write!(f, "password is incorrect")
|
||||
}
|
||||
PeachError::PasswordMismatch => {
|
||||
write!(f, "Password error: user-supplied passwords do not match")
|
||||
write!(f, "passwords do not match")
|
||||
}
|
||||
PeachError::PasswordNotSet => {
|
||||
write!(
|
||||
f,
|
||||
"Password error: hash value in YAML configuration file is empty"
|
||||
)
|
||||
write!(f, "hash value in YAML configuration file is empty")
|
||||
}
|
||||
PeachError::Read { ref path, .. } => {
|
||||
write!(f, "Read error: {}", path)
|
||||
}
|
||||
PeachError::Regex(ref err) => err.fmt(f),
|
||||
PeachError::SbotCli { ref msg } => {
|
||||
PeachError::Sbot(ref msg) => {
|
||||
write!(f, "Sbot error: {}", msg)
|
||||
}
|
||||
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 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,
|
||||
/// 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
|
||||
let peach_config = config_manager::load_peach_config()?;
|
||||
for ssb_admin_id in peach_config.ssb_admin_ids {
|
||||
// TODO: replace with golgi
|
||||
//sbot_client::private_message(&msg, &ssb_admin_id)?;
|
||||
// use golgi to send a private message on scuttlebutt
|
||||
match task::block_on(publish_private_msg(&msg, &ssb_admin_id)) {
|
||||
Ok(_) => (),
|
||||
Err(e) => return Err(PeachError::Sbot(e)),
|
||||
}
|
||||
}
|
||||
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_post,
|
||||
logout,
|
||||
reboot_cmd,
|
||||
shutdown_cmd,
|
||||
power_menu,
|
||||
settings_menu,
|
||||
set_theme,
|
||||
],
|
||||
|
@ -43,7 +40,6 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||
routes![
|
||||
admin_menu,
|
||||
configure_admin,
|
||||
add_admin,
|
||||
add_admin_post,
|
||||
delete_admin_post,
|
||||
change_password,
|
||||
|
@ -101,6 +97,7 @@ pub fn mount_peachpub_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
|||
/// required to run a complete PeachCloud build.
|
||||
pub fn mount_peachcloud_routes(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
mount_peachpub_routes(rocket)
|
||||
.mount("/", routes![reboot_cmd, shutdown_cmd, power_menu,])
|
||||
.mount(
|
||||
"/settings/network",
|
||||
routes![
|
||||
|
|
|
@ -6,7 +6,6 @@ use rocket::{
|
|||
post,
|
||||
request::{self, FlashMessage, FromRequest, Request},
|
||||
response::{Flash, Redirect},
|
||||
serde::Deserialize,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
|
||||
|
@ -89,17 +88,14 @@ pub fn login(flash: Option<FlashMessage>) -> Template {
|
|||
Template::render("login", &context.into_json())
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct LoginForm {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
/// Takes in a LoginForm and returns Ok(()) if username and password
|
||||
/// are correct to authenticate with peach-web.
|
||||
/// Takes in a LoginForm and returns Ok(()) if the password is correct.
|
||||
///
|
||||
/// Note: currently there is only one user, and the username should always
|
||||
/// be "admin".
|
||||
/// Note: there is currently only one user, therefore we don't need a username.
|
||||
pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> {
|
||||
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("/"))
|
||||
}
|
||||
Err(_) => {
|
||||
Err(e) => {
|
||||
let err_msg = format!("Invalid password: {}", e);
|
||||
// if unsuccessful login, render /login page again
|
||||
let mut context = Context::new();
|
||||
context.insert("back", &Some("/".to_string()));
|
||||
context.insert("title", &Some("Login".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()))
|
||||
}
|
||||
|
@ -142,7 +139,7 @@ pub fn logout(cookies: &CookieJar<'_>) -> Flash<Redirect> {
|
|||
|
||||
// HELPERS AND ROUTES FOR /reset_password
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct ResetPasswordForm {
|
||||
pub temporary_password: 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()) {
|
||||
Ok(_) => (
|
||||
"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) => (
|
||||
"error".to_string(),
|
||||
|
@ -266,9 +263,9 @@ pub fn send_password_reset_post() -> Template {
|
|||
|
||||
// HELPERS AND ROUTES FOR /settings/change_password
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct PasswordForm {
|
||||
pub old_password: String,
|
||||
pub current_password: String,
|
||||
pub new_password1: String,
|
||||
pub new_password2: String,
|
||||
}
|
||||
|
@ -277,9 +274,9 @@ pub struct PasswordForm {
|
|||
pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebError> {
|
||||
info!(
|
||||
"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
|
||||
password_utils::validate_new_passwords(
|
||||
&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()) {
|
||||
Ok(_) => (
|
||||
"success".to_string(),
|
||||
"New password is now saved".to_string(),
|
||||
"New password has been saved".to_string(),
|
||||
),
|
||||
Err(err) => (
|
||||
"error".to_string(),
|
||||
|
|
|
@ -3,7 +3,6 @@ use rocket::{
|
|||
get, post,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
serde::Deserialize,
|
||||
uri,
|
||||
};
|
||||
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
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct AddAdminForm {
|
||||
pub ssb_id: String,
|
||||
}
|
||||
|
||||
pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError> {
|
||||
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
|
||||
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>")]
|
||||
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 url = uri!("/settings/admin/configure");
|
||||
match result {
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Successfully added new admin"),
|
||||
Err(_) => Flash::error(Redirect::to(url), "Failed to add new admin"),
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Added SSB administrator"),
|
||||
Err(e) => Flash::error(Redirect::to(url), format!("Failed to add new admin: {}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
// HELPERS AND ROUTES FOR /settings/admin/delete
|
||||
|
||||
#[derive(Debug, Deserialize, FromForm)]
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct DeleteAdminForm {
|
||||
pub ssb_id: String,
|
||||
}
|
||||
|
@ -131,9 +111,12 @@ pub fn delete_admin_post(
|
|||
_auth: Authenticated,
|
||||
) -> Flash<Redirect> {
|
||||
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 {
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Successfully removed admin id"),
|
||||
Err(_) => Flash::error(Redirect::to(url), "Failed to remove admin id"),
|
||||
Ok(_) => Flash::success(Redirect::to(url), "Removed SSB administrator"),
|
||||
Err(e) => Flash::error(
|
||||
Redirect::to(url),
|
||||
format!("Failed to remove admin id: {}", e),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,22 +2,19 @@
|
|||
{%- block card %}
|
||||
<!-- LOGIN FORM -->
|
||||
<div class="card center">
|
||||
<div class="card-container">
|
||||
<form id="login_form" class="center" action="/login" method="post">
|
||||
<!-- input for username -->
|
||||
<input id="username" name="username" class="center input" type="text" placeholder="Username" title="Username for authentication" autofocus/>
|
||||
<form id="login_form" class="center" action="/login" method="post">
|
||||
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||
<!-- input for password -->
|
||||
<input id="password" name="password" class="center input" type="password" placeholder="Password" title="Password for given username"/>
|
||||
<div id="buttonDiv">
|
||||
<input id="loginUser" class="button button-primary center" title="Login" type="submit" value="Login">
|
||||
<label for="password" class="center label-small font-gray" style="width: 80%;">PASSWORD</label>
|
||||
<input id="password" name="password" class="center input" type="password" title="Password for given username"/>
|
||||
<!-- 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>
|
||||
</form>
|
||||
</div>
|
||||
<!-- FLASH MESSAGE -->
|
||||
{% include "snippets/flash_message" %}
|
||||
<div class="center-text" style="margin-top: 25px;">
|
||||
<a href="/settings/admin/forgot_password" class="label-small link font-gray">Forgot Password?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{%- 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 -->
|
||||
<div class="card center">
|
||||
<form id="changePassword" class="center" action="/settings/admin/change_password" method="post">
|
||||
<!-- input for current password -->
|
||||
<input id="currentPassword" class="center input" name="current_password" type="password" placeholder="Current password" title="Current password" autofocus>
|
||||
<!-- input for new password -->
|
||||
<input id="newPassword" class="center input" name="new_password1" type="password" placeholder="New password" title="New password">
|
||||
<!-- input for duplicate new password -->
|
||||
<input id="newPasswordDuplicate" class="center input" name="new_password2" type="password" placeholder="Re-enter new password" title="New password duplicate">
|
||||
<div id="buttonDiv">
|
||||
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||
<!-- input for current password -->
|
||||
<label for="currentPassword" class="center label-small font-gray" style="width: 80%;">CURRENT PASSWORD</label>
|
||||
<input id="currentPassword" class="center input" name="current_password" type="password" title="Current password" autofocus>
|
||||
<!-- input for new password -->
|
||||
<label for="newPassword" class="center label-small font-gray" style="width: 80%;">NEW PASSWORD</label>
|
||||
<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">
|
||||
<a class="button button-secondary center" href="/settings/admin" title="Cancel">Cancel</a>
|
||||
</div>
|
||||
|
|
|
@ -2,25 +2,31 @@
|
|||
{%- block card %}
|
||||
<!-- CONFIGURE ADMIN PAGE -->
|
||||
<div class="card center">
|
||||
<div class="text-container">
|
||||
<h4 class="font-normal">Current Admins</h4>
|
||||
{% if not ssb_admin_ids %}
|
||||
<div class="card-text">
|
||||
There are no currently configured admins.
|
||||
</div>
|
||||
{% else %}
|
||||
{% for admin in ssb_admin_ids %}
|
||||
<div>
|
||||
<form action="/settings/admin/delete" method="post">
|
||||
<input type="hidden" name="ssb_id" value="{{admin}}"/>
|
||||
<input type="submit" value="X" title="Delete"/> <span>{{ admin }}</span>
|
||||
</form>
|
||||
<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>
|
||||
{% if not ssb_admin_ids %}
|
||||
<div class="card-text">
|
||||
There are no currently configured admins.
|
||||
</div>
|
||||
{% else %}
|
||||
{% for admin in ssb_admin_ids %}
|
||||
<form class="center" action="/settings/admin/delete" method="post">
|
||||
<div class="center" style="display: flex; justify-content: space-between;">
|
||||
<input type="hidden" name="ssb_id" value="{{ admin }}"/>
|
||||
<p class="label-small label-ellipsis font-gray" style="user-select: all;">{{ admin }}</p>
|
||||
<input style="width: 30%;" type="submit" class="button button-warning" value="Delete" title="Delete SSB administrator"/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<a class="button button-primary center full-width" style="margin-top: 25px;" href="/settings/admin/add" title="Add Admin">Add Admin</a>
|
||||
</div>
|
||||
<!-- FLASH MESSAGE -->
|
||||
{% include "snippets/flash_message" %}
|
||||
</form>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<form id="addAdmin" class="center" style="margin-top: 2rem;" action="/settings/admin/add" method="post">
|
||||
<div class="center" style="display: flex; flex-direction: column; margin-bottom: 2rem;" title="Public key (ID) of a desired administrator">
|
||||
<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>
|
||||
{%- endblock card -%}
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
{%- block card %}
|
||||
<!-- PASSWORD RESET REQUEST CARD -->
|
||||
<div class="card center">
|
||||
<div class="capsule capsule-container info-border">
|
||||
<p class="card-text">Click the button below to send a new temporary password which can be used to change your device password.
|
||||
</br></br>
|
||||
The temporary password will be sent in an SSB private message to the admin of this device.</p>
|
||||
<div class="capsule capsule-container border-info">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<form id="sendPasswordReset" action="/send_password_reset" method="post">
|
||||
<form id="sendPasswordReset" action="/settings/admin/send_password_reset" method="post">
|
||||
<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>
|
||||
</form>
|
||||
<!-- FLASH MESSAGE -->
|
||||
|
|
|
@ -2,41 +2,22 @@
|
|||
{%- block card %}
|
||||
<!-- RESET PASSWORD PAGE -->
|
||||
<div class="card center">
|
||||
<div class="form-container">
|
||||
<form id="changePassword" action="/reset_password" method="post">
|
||||
<div class="input-wrapper">
|
||||
<!-- input for temporary password -->
|
||||
<label id="temporary_password" class="label-small input-label font-near-black">
|
||||
<label class="label-small input-label font-gray" for="temporary_password" style="padding-top: 0.25rem;">Temporary Password</label>
|
||||
<input id="temporary_password" class="form-input" style="margin-bottom: 0;"
|
||||
name="temporary_password" type="password" title="temporary password" value="">
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="input-wrapper">
|
||||
<!-- input for new password1 -->
|
||||
<label id="new_password1" class="label-small input-label font-near-black">
|
||||
<label class="label-small input-label font-gray" for="new_password1" style="padding-top: 0.25rem;">Enter New Password</label>
|
||||
<input id="new_password1" class="form-input" style="margin-bottom: 0;"
|
||||
name="new_password1" title="new_password1" type="password" value="">
|
||||
</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>
|
||||
<form id="changePassword" class="center" action="/settings/admin/reset_password" method="post">
|
||||
<div style="display: flex; flex-direction: column; margin-bottom: 1rem;">
|
||||
<!-- input for temporary password -->
|
||||
<label class="center label-small font-gray" style="width: 80%;" for="temporary_password">TEMPORARY PASSWORD</label>
|
||||
<input id="temporary_password" class="center input" name="temporary_password" type="password" title="temporary password" value="">
|
||||
<!-- input for new password1 -->
|
||||
<label class="center label-small font-gray" style="width: 80%;" for="new_password1">NEW PASSWORD</label>
|
||||
<input id="new_password1" class="center input" name="new_password1" title="new_password1" type="password" value="">
|
||||
<!-- input for new password2 -->
|
||||
<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="">
|
||||
<!-- save (form submission) button -->
|
||||
<input id="changePasswordButton" class="button button-primary center" title="Add" type="submit" value="Save">
|
||||
</div>
|
||||
</form>
|
||||
<!-- FLASH MESSAGE -->
|
||||
{% include "snippets/flash_message" %}
|
||||
</div>
|
||||
{%- endblock card -%}
|
||||
|
|
Loading…
Reference in New Issue