351 lines
13 KiB
Rust
351 lines
13 KiB
Rust
use log::{debug, info};
|
|
use rocket::request::{FlashMessage};
|
|
use rocket::form::{Form, FromForm};
|
|
use rocket::response::{Flash, Redirect};
|
|
use rocket::{get, post};
|
|
use rocket::serde::json::Json;
|
|
use rocket_dyn_templates::Template;
|
|
use rocket::serde::{Deserialize, Serialize};
|
|
|
|
use peach_lib::password_utils;
|
|
|
|
use crate::error::PeachWebError;
|
|
use crate::utils::build_json_response;
|
|
use rocket::serde::json::Value;
|
|
|
|
// HELPERS AND ROUTES FOR /login
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct LoginContext {
|
|
pub back: Option<String>,
|
|
pub flash_name: Option<String>,
|
|
pub flash_msg: Option<String>,
|
|
pub title: Option<String>,
|
|
}
|
|
|
|
impl LoginContext {
|
|
pub fn build() -> LoginContext {
|
|
LoginContext {
|
|
back: None,
|
|
flash_name: None,
|
|
flash_msg: None,
|
|
title: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[get("/login")]
|
|
pub fn login(flash: Option<FlashMessage>) -> Template {
|
|
let mut context = LoginContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Login".to_string());
|
|
// check to see if there is a flash message to display
|
|
if let Some(flash) = flash {
|
|
// add flash message contents to the context object
|
|
context.flash_name = Some(flash.kind().to_string());
|
|
context.flash_msg = Some(flash.message().to_string());
|
|
};
|
|
Template::render("login", &context)
|
|
}
|
|
|
|
// HELPERS AND ROUTES FOR /logout
|
|
|
|
#[post("/logout")]
|
|
pub fn logout() -> Flash<Redirect> {
|
|
// logout authenticated user
|
|
debug!("Attempting deauthentication of user.");
|
|
/*
|
|
match logout_user() {
|
|
Ok(_) => Flash::success(Redirect::to("/"), "Logout success"),
|
|
Err(_) => Flash::error(
|
|
Redirect::to("/"),
|
|
"Failed to logout",
|
|
),
|
|
}
|
|
*/
|
|
Flash::success(Redirect::to("/"), "Logged out")
|
|
}
|
|
|
|
// HELPERS AND ROUTES FOR /reset_password
|
|
|
|
#[derive(Debug, Deserialize, FromForm)]
|
|
pub struct ResetPasswordForm {
|
|
pub temporary_password: String,
|
|
pub new_password1: String,
|
|
pub new_password2: String,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ResetPasswordContext {
|
|
pub back: Option<String>,
|
|
pub title: Option<String>,
|
|
pub flash_name: Option<String>,
|
|
pub flash_msg: Option<String>,
|
|
}
|
|
|
|
impl ResetPasswordContext {
|
|
pub fn build() -> ResetPasswordContext {
|
|
ResetPasswordContext {
|
|
back: None,
|
|
title: None,
|
|
flash_name: None,
|
|
flash_msg: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct ChangePasswordContext {
|
|
pub back: Option<String>,
|
|
pub title: Option<String>,
|
|
pub flash_name: Option<String>,
|
|
pub flash_msg: Option<String>,
|
|
}
|
|
|
|
impl ChangePasswordContext {
|
|
pub fn build() -> ChangePasswordContext {
|
|
ChangePasswordContext {
|
|
back: None,
|
|
title: None,
|
|
flash_name: None,
|
|
flash_msg: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Verify, validate and save the submitted password. This function is publicly exposed for users who have forgotten their password.
|
|
pub fn save_reset_password_form(password_form: ResetPasswordForm) -> Result<(), PeachWebError> {
|
|
info!(
|
|
"reset password!: {} {} {}",
|
|
password_form.temporary_password, password_form.new_password1, password_form.new_password2
|
|
);
|
|
password_utils::verify_temporary_password(&password_form.temporary_password)?;
|
|
// if the previous line did not throw an error, then the secret_link is correct
|
|
password_utils::validate_new_passwords(
|
|
&password_form.new_password1,
|
|
&password_form.new_password2,
|
|
)?;
|
|
// if the previous line did not throw an error, then the new password is valid
|
|
password_utils::set_new_password(&password_form.new_password1)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Password reset request handler. This route is used by a user who is not logged in
|
|
/// and is specifically for users who have forgotten their password.
|
|
/// All routes under /public/* are excluded from nginx basic auth via the nginx config.
|
|
#[get("/reset_password")]
|
|
pub fn reset_password(flash: Option<FlashMessage>) -> Template {
|
|
let mut context = ResetPasswordContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Reset Password".to_string());
|
|
// check to see if there is a flash message to display
|
|
if let Some(flash) = flash {
|
|
// add flash message contents to the context object
|
|
context.flash_name = Some(flash.kind().to_string());
|
|
context.flash_msg = Some(flash.message().to_string());
|
|
};
|
|
Template::render("password/reset_password", &context)
|
|
}
|
|
|
|
/// Password reset form request handler. This route is used by a user who is not logged in
|
|
/// and is specifically for users who have forgotten their password.
|
|
/// This route is excluded from nginx basic auth via the nginx config.
|
|
#[post("/reset_password", data = "<reset_password_form>")]
|
|
pub fn reset_password_post(reset_password_form: Form<ResetPasswordForm>) -> Template {
|
|
let result = save_reset_password_form(reset_password_form.into_inner());
|
|
match result {
|
|
Ok(_) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Reset Password".to_string());
|
|
context.flash_name = Some("success".to_string());
|
|
let flash_msg = "New password is now saved. Return home to login".to_string();
|
|
context.flash_msg = Some(flash_msg);
|
|
Template::render("password/reset_password", &context)
|
|
}
|
|
Err(err) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
// set back icon link to network route
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Reset Password".to_string());
|
|
context.flash_name = Some("error".to_string());
|
|
context.flash_msg = Some(format!("Failed to reset password: {}", err));
|
|
Template::render("password/reset_password", &context)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// JSON password reset form request handler. This route is used by a user who is not logged in
|
|
/// and is specifically for users who have forgotten their password.
|
|
/// All routes under /public/* are excluded from nginx basic auth via the nginx config.
|
|
#[post("/public/api/v1/reset_password", data = "<reset_password_form>")]
|
|
pub fn reset_password_form_endpoint(
|
|
reset_password_form: Json<ResetPasswordForm>,
|
|
) -> Value {
|
|
let result = save_reset_password_form(reset_password_form.into_inner());
|
|
match result {
|
|
Ok(_) => {
|
|
let status = "success".to_string();
|
|
let msg = "New password is now saved. Return home to login.".to_string();
|
|
build_json_response(status, None, Some(msg))
|
|
}
|
|
Err(err) => {
|
|
let status = "error".to_string();
|
|
let msg = format!("{}", err);
|
|
build_json_response(status, None, Some(msg))
|
|
}
|
|
}
|
|
}
|
|
|
|
// HELPERS AND ROUTES FOR /send_password_reset
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct SendPasswordResetContext {
|
|
pub back: Option<String>,
|
|
pub title: Option<String>,
|
|
pub flash_name: Option<String>,
|
|
pub flash_msg: Option<String>,
|
|
}
|
|
|
|
impl SendPasswordResetContext {
|
|
pub fn build() -> SendPasswordResetContext {
|
|
SendPasswordResetContext {
|
|
back: None,
|
|
title: None,
|
|
flash_name: None,
|
|
flash_msg: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Password reset request handler. This route is used by a user who is not logged in to send a new password reset link.
|
|
#[get("/send_password_reset")]
|
|
pub fn send_password_reset_page(flash: Option<FlashMessage>) -> Template {
|
|
let mut context = SendPasswordResetContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Send Password Reset".to_string());
|
|
// check to see if there is a flash message to display
|
|
if let Some(flash) = flash {
|
|
// add flash message contents to the context object
|
|
context.flash_name = Some(flash.kind().to_string());
|
|
context.flash_msg = Some(flash.message().to_string());
|
|
};
|
|
Template::render("password/send_password_reset", &context)
|
|
}
|
|
|
|
/// Send password reset request handler. This route is used by a user who is not logged in
|
|
/// and is specifically for users who have forgotten their password. A successful request results
|
|
/// in a Scuttlebutt private message being sent to the account of the device admin.
|
|
#[post("/send_password_reset")]
|
|
pub fn send_password_reset_post() -> Template {
|
|
info!("++ send password reset post");
|
|
let result = password_utils::send_password_reset();
|
|
match result {
|
|
Ok(_) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Send Password Reset".to_string());
|
|
context.flash_name = Some("success".to_string());
|
|
let flash_msg =
|
|
"A password reset link has been sent to the admin of this device".to_string();
|
|
context.flash_msg = Some(flash_msg);
|
|
Template::render("password/send_password_reset", &context)
|
|
}
|
|
Err(err) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
context.back = Some("/".to_string());
|
|
context.title = Some("Send Password Reset".to_string());
|
|
context.flash_name = Some("error".to_string());
|
|
context.flash_msg = Some(format!("Failed to send password reset link: {}", err));
|
|
Template::render("password/send_password_reset", &context)
|
|
}
|
|
}
|
|
}
|
|
|
|
// HELPERS AND ROUTES FOR /settings/change_password
|
|
|
|
#[derive(Debug, Deserialize, FromForm)]
|
|
pub struct PasswordForm {
|
|
pub old_password: String,
|
|
pub new_password1: String,
|
|
pub new_password2: String,
|
|
}
|
|
|
|
/// Password save form request handler. This function is for use by a user who is already logged in to change their password.
|
|
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_utils::verify_password(&password_form.old_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,
|
|
&password_form.new_password2,
|
|
)?;
|
|
// if the previous line did not throw an error, then the new password is valid
|
|
password_utils::set_new_password(&password_form.new_password1)?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Change password request handler. This is used by a user who is already logged in.
|
|
#[get("/settings/change_password")]
|
|
pub fn change_password(flash: Option<FlashMessage>) -> Template {
|
|
let mut context = ChangePasswordContext::build();
|
|
// set back icon link to network route
|
|
context.back = Some("/network".to_string());
|
|
context.title = Some("Change Password".to_string());
|
|
// check to see if there is a flash message to display
|
|
if let Some(flash) = flash {
|
|
// add flash message contents to the context object
|
|
context.flash_name = Some(flash.kind().to_string());
|
|
context.flash_msg = Some(flash.message().to_string());
|
|
};
|
|
Template::render("password/change_password", &context)
|
|
}
|
|
|
|
/// Change password form request handler. This route is used by a user who is already logged in.
|
|
#[post("/settings/change_password", data = "<password_form>")]
|
|
pub fn change_password_post(password_form: Form<PasswordForm>) -> Template {
|
|
let result = save_password_form(password_form.into_inner());
|
|
match result {
|
|
Ok(_) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
// set back icon link to network route
|
|
context.back = Some("/network".to_string());
|
|
context.title = Some("Change Password".to_string());
|
|
context.flash_name = Some("success".to_string());
|
|
context.flash_msg = Some("New password is now saved".to_string());
|
|
// template_dir is set in Rocket.toml
|
|
Template::render("password/change_password", &context)
|
|
}
|
|
Err(err) => {
|
|
let mut context = ChangePasswordContext::build();
|
|
// set back icon link to network route
|
|
context.back = Some("/network".to_string());
|
|
context.title = Some("Configure DNS".to_string());
|
|
context.flash_name = Some("error".to_string());
|
|
context.flash_msg = Some(format!("Failed to save new password: {}", err));
|
|
Template::render("password/change_password", &context)
|
|
}
|
|
}
|
|
}
|
|
|
|
/// JSON change password form request handler.
|
|
#[post("/api/v1/settings/change_password", data = "<password_form>")]
|
|
pub fn save_password_form_endpoint(password_form: Json<PasswordForm>) -> Value {
|
|
let result = save_password_form(password_form.into_inner());
|
|
match result {
|
|
Ok(_) => {
|
|
let status = "success".to_string();
|
|
let msg = "Your password was successfully changed".to_string();
|
|
build_json_response(status, None, Some(msg))
|
|
}
|
|
Err(err) => {
|
|
let status = "error".to_string();
|
|
let msg = format!("{}", err);
|
|
build_json_response(status, None, Some(msg))
|
|
}
|
|
}
|
|
}
|