add temporary password reset routes
This commit is contained in:
parent
4a94f14dc5
commit
deaedc4428
|
@ -86,7 +86,7 @@ pub fn send_password_reset() -> Result<(), PeachError> {
|
||||||
"Your new temporary password is: {}
|
"Your new temporary password is: {}
|
||||||
|
|
||||||
If you are on the same WiFi network as your PeachCloud device you can reset your password \
|
If you are on the same WiFi network as your PeachCloud device you can reset your password \
|
||||||
using this link: http://peach.local/reset_password",
|
using this link: http://peach.local/auth/reset",
|
||||||
temporary_password
|
temporary_password
|
||||||
);
|
);
|
||||||
// if there is an external domain, then include remote link in message
|
// if there is an external domain, then include remote link in message
|
||||||
|
@ -95,7 +95,7 @@ using this link: http://peach.local/reset_password",
|
||||||
Some(domain) => {
|
Some(domain) => {
|
||||||
format!(
|
format!(
|
||||||
"\n\nOr if you are on a different WiFi network, you can reset your password \
|
"\n\nOr if you are on a different WiFi network, you can reset your password \
|
||||||
using the the following link: {}/reset_password",
|
using the the following link: {}/auth/reset",
|
||||||
domain
|
domain
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use log::{error, info};
|
||||||
use rouille::{router, Request, Response};
|
use rouille::{router, Request, Response};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -6,8 +7,11 @@ use crate::{
|
||||||
SessionData,
|
SessionData,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Receive an incoming request, mount the fileservers for static assets and
|
/// Request handler.
|
||||||
/// define the publically-accessible routes.
|
///
|
||||||
|
/// Mount the fileservers for static assets and define the
|
||||||
|
/// publically-accessible routes (including per-route handlers). Includes
|
||||||
|
/// logging of all incoming requests.
|
||||||
///
|
///
|
||||||
/// If the request is for a private route (ie. a route requiring successful
|
/// If the request is for a private route (ie. a route requiring successful
|
||||||
/// authentication to view), check the authentication status of the user
|
/// authentication to view), check the authentication status of the user
|
||||||
|
@ -32,42 +36,68 @@ pub fn handle_route(request: &Request, session_data: &mut Option<SessionData>) -
|
||||||
return rouille::match_assets(&request, &blobstore);
|
return rouille::match_assets(&request, &blobstore);
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle the routes which are always accessible (ie. whether logged-in
|
// get the current time (for logging purposes)
|
||||||
// or not)
|
let now = chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.6f");
|
||||||
router!(request,
|
|
||||||
(GET) (/auth/forgot) => {
|
|
||||||
Response::html(routes::authentication::forgot::build_template())
|
|
||||||
},
|
|
||||||
|
|
||||||
(GET) (/auth/login) => {
|
// define the success logger for incoming requests
|
||||||
Response::html(routes::authentication::login::build_template(request))
|
let log_ok = |req: &Request, _resp: &Response, _elap: std::time::Duration| {
|
||||||
.reset_flash()
|
info!("{} {} {}", now, req.method(), req.raw_url());
|
||||||
},
|
};
|
||||||
|
|
||||||
(POST) (/auth/login) => {
|
// define the error logger for incoming requests
|
||||||
routes::authentication::login::handle_form(request, session_data)
|
let log_err = |req: &Request, _elap: std::time::Duration| {
|
||||||
},
|
error!(
|
||||||
|
"{} Handler panicked: {} {}",
|
||||||
|
now,
|
||||||
|
req.method(),
|
||||||
|
req.raw_url()
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
(GET) (/auth/reset) => {
|
// instantiate request logging
|
||||||
Response::html(routes::authentication::reset::build_template(request))
|
rouille::log_custom(request, log_ok, log_err, || {
|
||||||
.reset_flash()
|
// handle the routes which are always accessible (ie. whether logged-in
|
||||||
},
|
// or not)
|
||||||
|
router!(request,
|
||||||
|
(GET) (/auth/forgot) => {
|
||||||
|
Response::html(routes::authentication::forgot::build_template(request))
|
||||||
|
.reset_flash()
|
||||||
|
},
|
||||||
|
|
||||||
(POST) (/auth/reset) => {
|
(GET) (/auth/login) => {
|
||||||
routes::authentication::reset::handle_form(request)
|
Response::html(routes::authentication::login::build_template(request))
|
||||||
},
|
.reset_flash()
|
||||||
|
},
|
||||||
|
|
||||||
_ => {
|
(POST) (/auth/login) => {
|
||||||
// now that we handled all the routes that are accessible in all
|
routes::authentication::login::handle_form(request, session_data)
|
||||||
// circumstances, we check that the user is logged in before proceeding
|
},
|
||||||
if let Some(_session) = session_data.as_ref() {
|
|
||||||
// logged in:
|
(GET) (/auth/reset) => {
|
||||||
// mount the routes which require authentication to view
|
Response::html(routes::authentication::reset::build_template(request))
|
||||||
private_router::mount_peachpub_routes(request, session_data)
|
.reset_flash()
|
||||||
} else {
|
},
|
||||||
// not logged in:
|
|
||||||
Response::redirect_303("/auth/login")
|
(POST) (/auth/reset) => {
|
||||||
|
routes::authentication::reset::handle_form(request)
|
||||||
|
},
|
||||||
|
|
||||||
|
(POST) (/auth/temporary) => {
|
||||||
|
routes::authentication::temporary::handle_form()
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
// now that we handled all the routes that are accessible in all
|
||||||
|
// circumstances, we check that the user is logged in before proceeding
|
||||||
|
if let Some(_session) = session_data.as_ref() {
|
||||||
|
// logged in:
|
||||||
|
// mount the routes which require authentication to view
|
||||||
|
private_router::mount_peachpub_routes(request, session_data)
|
||||||
|
} else {
|
||||||
|
// not logged in:
|
||||||
|
Response::redirect_303("/auth/login")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
)
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,19 @@
|
||||||
use maud::{html, PreEscaped};
|
use maud::{html, PreEscaped};
|
||||||
|
use rouille::Request;
|
||||||
|
|
||||||
use crate::{templates, utils::theme};
|
use crate::{
|
||||||
|
templates,
|
||||||
|
utils::{flash::FlashRequest, theme},
|
||||||
|
};
|
||||||
|
|
||||||
// ROUTE: /auth/forgot
|
// ROUTE: /auth/forgot
|
||||||
|
|
||||||
/// Forgot password template builder.
|
/// Forgot password template builder.
|
||||||
pub fn build_template() -> PreEscaped<String> {
|
pub fn build_template(request: &Request) -> PreEscaped<String> {
|
||||||
let form_template = html! {
|
// check for flash cookies; will be (None, None) if no flash cookies are found
|
||||||
|
let (flash_name, flash_msg) = request.retrieve_flash();
|
||||||
|
|
||||||
|
let password_reset_template = html! {
|
||||||
(PreEscaped("<!-- PASSWORD RESET REQUEST CARD -->"))
|
(PreEscaped("<!-- PASSWORD RESET REQUEST CARD -->"))
|
||||||
div class="card center" {
|
div class="card center" {
|
||||||
div class="capsule capsule-container border-info" {
|
div class="capsule capsule-container border-info" {
|
||||||
|
@ -20,20 +27,26 @@ pub fn build_template() -> PreEscaped<String> {
|
||||||
"Once you have the temporary password, click the 'Set New Password' button to reach the password reset page."
|
"Once you have the temporary password, click the 'Set New Password' button to reach the password reset page."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
form id="sendPasswordReset" action="/auth/send_password_reset" method="post" {
|
form id="sendPasswordReset" action="/auth/temporary" method="post" {
|
||||||
div id="buttonDiv" {
|
div id="buttonDiv" {
|
||||||
input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Temporary Password" title="Send temporary password to Scuttlebutt admin";
|
input class="button button-primary center" style="margin-top: 1rem;" type="submit" value="Send Temporary Password" title="Send temporary password to Scuttlebutt admin(s)";
|
||||||
a href="/auth/reset_password" class="button button-primary center" title="Set a new password using the temporary password" {
|
a href="/auth/reset_password" class="button button-primary center" title="Set a new password using the temporary password" {
|
||||||
"Set New Password"
|
"Set New Password"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// render flash message if cookies were found in the request
|
||||||
|
@if let (Some(name), Some(msg)) = (flash_name, flash_msg) {
|
||||||
|
(PreEscaped("<!-- FLASH MESSAGE -->"))
|
||||||
|
(templates::flash::build_template(name, msg))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// wrap the nav bars around the settings menu template content
|
// wrap the nav bars around the settings menu template content
|
||||||
// parameters are template, title and back url
|
// parameters are template, title and back url
|
||||||
let body = templates::nav::build_template(form_template, "Send Password Reset", Some("/"));
|
let body =
|
||||||
|
templates::nav::build_template(password_reset_template, "Send Password Reset", Some("/"));
|
||||||
|
|
||||||
// query the current theme so we can pass it into the base template builder
|
// query the current theme so we can pass it into the base template builder
|
||||||
let theme = theme::get_theme();
|
let theme = theme::get_theme();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use log::info;
|
use log::debug;
|
||||||
use maud::{html, PreEscaped};
|
use maud::{html, PreEscaped};
|
||||||
use peach_lib::password_utils;
|
use peach_lib::password_utils;
|
||||||
use rouille::{post_input, try_or_400, Request, Response};
|
use rouille::{post_input, try_or_400, Request, Response};
|
||||||
|
@ -63,17 +63,17 @@ pub fn handle_form(request: &Request, session_data: &mut Option<SessionData>) ->
|
||||||
|
|
||||||
match password_utils::verify_password(&data.password) {
|
match password_utils::verify_password(&data.password) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Successful login attempt");
|
debug!("Successful login attempt");
|
||||||
// if password verification is successful, write to `session_data`
|
// if password verification is successful, write to `session_data`
|
||||||
// to authenticate the user
|
// to authenticate the user
|
||||||
*session_data = Some(SessionData {
|
*session_data = Some(SessionData {
|
||||||
_login: data.password,
|
_login: "success".to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Response::redirect_303("/")
|
Response::redirect_303("/")
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
info!("Unsuccessful login attempt");
|
debug!("Unsuccessful login attempt");
|
||||||
let err_msg = format!("Invalid password: {}", err);
|
let err_msg = format!("Invalid password: {}", err);
|
||||||
let (flash_name, flash_msg) = (
|
let (flash_name, flash_msg) = (
|
||||||
"flash_name=error".to_string(),
|
"flash_name=error".to_string(),
|
||||||
|
|
|
@ -3,3 +3,4 @@ pub mod forgot;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod logout;
|
pub mod logout;
|
||||||
pub mod reset;
|
pub mod reset;
|
||||||
|
pub mod temporary;
|
||||||
|
|
|
@ -100,7 +100,6 @@ pub fn handle_form(request: &Request) -> Response {
|
||||||
&data.new_password2,
|
&data.new_password2,
|
||||||
) {
|
) {
|
||||||
Ok(_) => (
|
Ok(_) => (
|
||||||
// <cookie-name>=<cookie-value>
|
|
||||||
"flash_name=success".to_string(),
|
"flash_name=success".to_string(),
|
||||||
"flash_msg=New password has been saved. Return home to login".to_string(),
|
"flash_msg=New password has been saved. Return home to login".to_string(),
|
||||||
),
|
),
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
use log::debug;
|
||||||
|
use peach_lib::password_utils;
|
||||||
|
use rouille::Response;
|
||||||
|
|
||||||
|
use crate::utils::flash::FlashResponse;
|
||||||
|
|
||||||
|
// ROUTE: /auth/temporary (POST)
|
||||||
|
|
||||||
|
/// Send a temporary password as a Scuttlebutt private message to the admin(s).
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Redirects to the Send Password Reset page a flash message describing the
|
||||||
|
/// outcome of the action (may be successful or unsuccessful).
|
||||||
|
pub fn handle_form() -> Response {
|
||||||
|
// save submitted admin id to file
|
||||||
|
let (flash_name, flash_msg) = match password_utils::send_password_reset() {
|
||||||
|
Ok(_) => {
|
||||||
|
debug!("Sent temporary password to device admin(s)");
|
||||||
|
(
|
||||||
|
"flash_name=success".to_string(),
|
||||||
|
"flash_msg=A temporary password has been sent to the admin(s) of this device"
|
||||||
|
.to_string(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
debug!(
|
||||||
|
"Received an error while trying to send temporary password to device admin(s): {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
(
|
||||||
|
"error".to_string(),
|
||||||
|
format!("Failed to send temporary password: {}", err),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Response::redirect_303("/auth/forgot").add_flash(flash_name, flash_msg)
|
||||||
|
}
|
Loading…
Reference in New Issue