From 212c2e1545e5163a63a977a5c94d7c676c85710e Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 15:00:27 +0100 Subject: [PATCH 1/7] Add request guard for password authentication --- Cargo.lock | 223 +++++++++++++++++- peach-web/Cargo.toml | 2 +- peach-web/src/main.rs | 5 +- peach-web/src/routes/authentication.rs | 110 ++++++++- .../src/routes/{helpers.rs => catchers.rs} | 9 + peach-web/src/routes/mod.rs | 2 +- peach-web/src/routes/ping.rs | 4 +- peach-web/src/utils.rs | 12 + peach-web/templates/login.html.tera | 8 +- 9 files changed, 349 insertions(+), 26 deletions(-) rename peach-web/src/routes/{helpers.rs => catchers.rs} (88%) diff --git a/Cargo.lock b/Cargo.lock index 611a250..9465229 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,60 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug 0.3.0", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -159,6 +213,12 @@ dependencies = [ "byteorder", ] +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "binascii" version = "0.1.4" @@ -192,7 +252,16 @@ dependencies = [ "block-padding", "byte-tools", "byteorder", - "generic-array", + "generic-array 0.12.4", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array 0.14.4", ] [[package]] @@ -322,6 +391,15 @@ dependencies = [ "phf_codegen", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "clap" version = "2.33.3" @@ -384,7 +462,13 @@ version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f1c7727e460397e56abc4bddc1d49e07a1ad78fc98eb2e1c8f032a58a2f80d" dependencies = [ + "aes-gcm", + "base64 0.13.0", + "hkdf", "percent-encoding 2.1.0", + "rand 0.8.4", + "sha2", + "subtle", "time 0.2.27", "version_check 0.9.3", ] @@ -405,6 +489,21 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + [[package]] name = "crossbeam-channel" version = "0.3.9" @@ -482,6 +581,25 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + [[package]] name = "ctrlc" version = "3.2.1" @@ -550,7 +668,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array", + "generic-array 0.12.4", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", ] [[package]] @@ -941,6 +1068,16 @@ dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check 0.9.3", +] + [[package]] name = "get_if_addrs" version = "0.5.3" @@ -985,6 +1122,16 @@ dependencies = [ "wasi 0.10.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug 0.3.0", + "polyval", +] + [[package]] name = "gimli" version = "0.26.1" @@ -1094,6 +1241,26 @@ dependencies = [ "libc", ] +[[package]] +name = "hkdf" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" +dependencies = [ + "digest 0.9.0", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "http" version = "0.1.21" @@ -2222,6 +2389,12 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "openssl" version = "0.10.38" @@ -2673,6 +2846,17 @@ version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12295df4f294471248581bc09bef3c38a5e46f1e36d6a37353621a0c6c357e1f" +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.15" @@ -3445,10 +3629,10 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df" dependencies = [ - "block-buffer", - "digest", + "block-buffer 0.7.3", + "digest 0.8.1", "fake-simd", - "opaque-debug", + "opaque-debug 0.2.3", ] [[package]] @@ -3457,6 +3641,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -3745,6 +3942,12 @@ dependencies = [ "syn 1.0.81", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "0.15.44" @@ -4537,6 +4740,16 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + [[package]] name = "url" version = "1.7.2" diff --git a/peach-web/Cargo.toml b/peach-web/Cargo.toml index 979a9ec..c94e96c 100644 --- a/peach-web/Cargo.toml +++ b/peach-web/Cargo.toml @@ -40,7 +40,7 @@ log = "0.4" nest = "1.0.0" peach-lib = { path = "../peach-lib" } percent-encoding = "2.1.0" -rocket = { version = "0.5.0-rc.1", features = ["json"] } +rocket = { version = "0.5.0-rc.1", features = ["json", "secrets"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" snafu = "0.6" diff --git a/peach-web/src/main.rs b/peach-web/src/main.rs index cdfcaf5..edd17bd 100644 --- a/peach-web/src/main.rs +++ b/peach-web/src/main.rs @@ -38,7 +38,7 @@ use rocket_dyn_templates::Template; use crate::routes::authentication::*; use crate::routes::device::*; -use crate::routes::helpers::*; +use crate::routes::catchers::*; use crate::routes::index::*; use crate::routes::ping::*; use crate::routes::scuttlebutt::*; @@ -66,6 +66,7 @@ fn init_rocket() -> Rocket { help, // WEB ROUTE index, // WEB ROUTE login, // WEB ROUTE + login_post, // WEB ROUTE logout, // WEB ROUTE messages, // WEB ROUTE network_home, // WEB ROUTE @@ -121,7 +122,7 @@ fn init_rocket() -> Rocket { ], ) .mount("/", FileServer::from("static")) - .register("/", catchers![not_found, internal_error]) + .register("/", catchers![not_found, internal_error, forbidden]) .attach(Template::fairing()) } diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index a82bcba..ef780d5 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -8,10 +8,60 @@ use rocket_dyn_templates::Template; use rocket::serde::{Deserialize, Serialize}; use peach_lib::password_utils; +use peach_lib::error::PeachError; use crate::error::PeachWebError; -use crate::utils::build_json_response; +use crate::utils::{build_json_response, TemplateOrRedirect}; use rocket::serde::json::Value; +use rocket::request::{self, FromRequest, Request}; +use rocket::http::{Cookie, CookieJar, Status}; +use rocket::outcome::IntoOutcome; +use websocket::futures::future::Ok; + + +// HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES + +pub const AUTH_COOKIE_KEY: &str = "peachweb_auth"; +pub const ADMIN_USERNAME: &str = "admin"; + +pub struct Authenticated { + is_authenticated: bool, +} + +#[derive(Debug)] +pub enum LoginError { + InvalidData, + UsernameDoesNotExist, + WrongPassword +} + +/// Request guard which returns an Authenticated struct with is_authenticated=true +/// iff the user has a cookie which proves they are authenticated with peach-web. +/// +/// Note that cookies.get_private uses encryption, which means that this private cookie +/// cannot be inspected, tampered with, or manufactured by clients. +#[rocket::async_trait] +impl<'r> FromRequest<'r> for Authenticated { + type Error = LoginError; + + async fn from_request(req: &'r Request<'_>) -> request::Outcome { + let authenticated = req + .cookies() + .get_private(AUTH_COOKIE_KEY) + .and_then(|cookie| cookie.value().parse().ok()) + .map(|value: String| { info!("cookie value: {}", value); Authenticated { is_authenticated: true } }); + match authenticated { + Some(auth) => { + info!("Authenticated!"); + request::Outcome::Success(auth) + }, + None => { + info!("not authenticated!"); + request::Outcome::Failure((Status::Forbidden, LoginError::UsernameDoesNotExist)) + } + } + } +} // HELPERS AND ROUTES FOR /login @@ -48,24 +98,60 @@ pub fn login(flash: Option) -> Template { Template::render("login", &context) } + +#[derive(Debug, Deserialize, 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. +/// +/// Note: currently there is only one user, and the username should always +/// be "admin". +pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> { + password_utils::verify_password(&login_form.password) +} + +#[post("/login", data="")] +pub fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> TemplateOrRedirect { + info!("call to login post"); + let result = verify_login_form(login_form.into_inner()); + match result { + Ok(_) => { + // if successful login, add a cookie indicating the user is authenticated + // and redirect to home page + // NOTE: since we currently have just one user, the value of the cookie + // is just admin (this is arbitrary). + // If we had multiple users, we could put the user_id here. + info!("successfull password form"); + cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, ADMIN_USERNAME)); + TemplateOrRedirect::Redirect(Redirect::to("/")) + } + Err(_) => { + info!("invalid password form"); + // if unsuccessful login, render /login page again + let mut context = LoginContext::build(); + context.back = Some("/".to_string()); + context.title = Some("Login".to_string()); + TemplateOrRedirect::Template(Template::render("login", &context)) + } + } +} + + // HELPERS AND ROUTES FOR /logout -#[post("/logout")] -pub fn logout() -> Flash { +#[get("/logout")] +pub fn logout(cookies: &CookieJar<'_>) -> Flash { // 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", - ), - } - */ + cookies.remove_private(Cookie::named(AUTH_COOKIE_KEY)); Flash::success(Redirect::to("/"), "Logged out") } + // HELPERS AND ROUTES FOR /reset_password #[derive(Debug, Deserialize, FromForm)] diff --git a/peach-web/src/routes/helpers.rs b/peach-web/src/routes/catchers.rs similarity index 88% rename from peach-web/src/routes/helpers.rs rename to peach-web/src/routes/catchers.rs index c1fdf6c..e494395 100644 --- a/peach-web/src/routes/helpers.rs +++ b/peach-web/src/routes/catchers.rs @@ -1,6 +1,7 @@ use log::debug; use rocket::{catch}; use rocket_dyn_templates::Template; +use rocket::response::Redirect; use serde::Serialize; // HELPERS AND ROUTES FOR 404 ERROR @@ -49,3 +50,11 @@ pub fn internal_error() -> Template { Template::render("internal_error", context) } + +// HELPERS AND ROUNTES FOR 403 FORBIDDEN + +#[catch(403)] +pub fn forbidden() -> Redirect { + debug!("403 Forbidden"); + Redirect::to("/login") +} \ No newline at end of file diff --git a/peach-web/src/routes/mod.rs b/peach-web/src/routes/mod.rs index 87368cc..be18d09 100644 --- a/peach-web/src/routes/mod.rs +++ b/peach-web/src/routes/mod.rs @@ -1,6 +1,6 @@ pub mod authentication; pub mod device; -pub mod helpers; +pub mod catchers; pub mod index; pub mod ping; pub mod scuttlebutt; diff --git a/peach-web/src/routes/ping.rs b/peach-web/src/routes/ping.rs index 8e87cd0..e9fc7c1 100644 --- a/peach-web/src/routes/ping.rs +++ b/peach-web/src/routes/ping.rs @@ -8,10 +8,12 @@ use peach_lib::oled_client; use peach_lib::stats_client; use crate::utils::build_json_response; +use crate::routes::authentication::Authenticated; /// Status route: useful for checking connectivity from web client. #[get("/api/v1/ping")] -pub fn ping_pong() -> Value { +pub fn ping_pong(auth: Authenticated) -> Value { +//pub fn ping_pong() -> Value { // ping pong let status = "success".to_string(); let msg = "pong!".to_string(); diff --git a/peach-web/src/utils.rs b/peach-web/src/utils.rs index 9021338..ff4c883 100644 --- a/peach-web/src/utils.rs +++ b/peach-web/src/utils.rs @@ -1,5 +1,8 @@ pub mod monitor; +use rocket_dyn_templates::Template; +use rocket::request::Request; +use rocket::response::{Redirect, Responder}; use rocket::serde::json::{Value, json}; use rocket::serde::{Serialize}; @@ -18,3 +21,12 @@ pub struct FlashContext { pub flash_name: Option, pub flash_msg: Option, } + + +/// A helper enum which allows routes to either return a Template or a Redirect +/// from: https://github.com/SergioBenitez/Rocket/issues/253#issuecomment-532356066 +#[derive(Debug, Responder)] +pub enum TemplateOrRedirect { + Template(Template), + Redirect(Redirect), +} \ No newline at end of file diff --git a/peach-web/templates/login.html.tera b/peach-web/templates/login.html.tera index 39c1093..97390ed 100644 --- a/peach-web/templates/login.html.tera +++ b/peach-web/templates/login.html.tera @@ -3,11 +3,11 @@
-
+ - + - +
Cancel @@ -27,5 +27,5 @@ {%- endif -%}
- + {%- endblock card -%} -- 2.40.1 From 04011e4f1a9aa6ce809757e667a7f8684f7c2681 Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 16:38:10 +0100 Subject: [PATCH 2/7] Add rocket authentication to all routes --- Cargo.lock | 22 +++++- peach-lib/Cargo.toml | 1 + peach-lib/src/config_manager.rs | 39 +++++++++- peach-lib/src/error.rs | 2 + peach-lib/src/password_utils.rs | 96 ++++++++++-------------- peach-web/src/main.rs | 1 + peach-web/src/routes/authentication.rs | 19 +++-- peach-web/src/routes/device.rs | 13 ++-- peach-web/src/routes/index.rs | 4 +- peach-web/src/routes/ping.rs | 6 +- peach-web/src/routes/scuttlebutt.rs | 8 +- peach-web/src/routes/settings/admin.rs | 9 ++- peach-web/src/routes/settings/dns.rs | 7 +- peach-web/src/routes/settings/network.rs | 63 ++++++++-------- peach-web/static/js/common.js | 9 --- peach-web/templates/login.html.tera | 4 +- peach-web/templates/nav.html.tera | 2 +- 17 files changed, 172 insertions(+), 133 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9465229..be13e3b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2582,6 +2582,7 @@ dependencies = [ "log 0.4.14", "rand 0.8.4", "regex", + "rust-crypto", "serde 1.0.130", "serde_derive", "serde_json", @@ -2870,7 +2871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f77e66f6d6d898cbbd4a09c48fd3507cfc210b7c83055de02a38b5f7a1e6d216" dependencies = [ "libc", - "time 0.1.44", + "time 0.2.27", ] [[package]] @@ -3380,12 +3381,31 @@ dependencies = [ "uncased", ] +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time 0.1.44", +] + [[package]] name = "rustc-demangle" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + [[package]] name = "rustc_version" version = "0.1.7" diff --git a/peach-lib/Cargo.toml b/peach-lib/Cargo.toml index 5d56455..e7e77b3 100644 --- a/peach-lib/Cargo.toml +++ b/peach-lib/Cargo.toml @@ -12,6 +12,7 @@ jsonrpc-client-core = "0.5" jsonrpc-client-http = "0.5" jsonrpc-core = "8.0.1" serde = { version = "1.0", features = ["derive"] } +rust-crypto = "0.2.36" serde_derive = "1.0" serde_json = "1.0" serde_yaml = "0.8" diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index 735866a..5940706 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -4,12 +4,13 @@ //! //! The configuration file is located at: "/var/lib/peachcloud/config.yml" -use fslock::LockFile; +use fslock::{LockFile, IntoOsString}; use serde::{Deserialize, Serialize}; use std::fs; use crate::error::PeachError; use crate::error::*; +use crate::password_utils::hash_password; // main configuration file pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml"; @@ -34,6 +35,10 @@ pub struct PeachConfig { pub dyn_enabled: bool, #[serde(default)] // default is empty vector pub ssb_admin_ids: Vec, + #[serde(default)] + pub admin_password_hash: String, + #[serde(default)] + pub temporary_password_hash: String, } // helper functions for serializing and deserializing PeachConfig from disc @@ -69,6 +74,8 @@ pub fn load_peach_config() -> Result { dyn_tsig_key_path: "".to_string(), dyn_enabled: false, ssb_admin_ids: Vec::new(), + admin_password_hash: "".to_string(), + temporary_password_hash: "".to_string(), }; } // otherwise we load peach config from disk @@ -141,3 +148,33 @@ pub fn delete_ssb_admin_id(ssb_id: &str) -> Result { }), } } + +pub fn set_admin_password_hash(password_hash: &str) -> Result { + let mut peach_config = load_peach_config()?; + peach_config.admin_password_hash = password_hash.to_string(); + save_peach_config(peach_config) +} + +pub fn get_admin_password_hash() -> Result { + let peach_config = load_peach_config()?; + if !peach_config.admin_password_hash.is_empty() { + Ok(peach_config.admin_password_hash) + } else { + Err(PeachError::PasswordNotSet) + } +} + +pub fn set_temporary_password_hash(password_hash: &str) -> Result { + let mut peach_config = load_peach_config()?; + peach_config.temporary_password_hash = password_hash.to_string(); + save_peach_config(peach_config) +} + +pub fn get_temporary_password_hash() -> Result { + let peach_config = load_peach_config()?; + if !peach_config.temporary_password_hash.is_empty() { + Ok(peach_config.temporary_password_hash) + } else { + Err(PeachError::PasswordNotSet) + } +} \ No newline at end of file diff --git a/peach-lib/src/error.rs b/peach-lib/src/error.rs index 0068263..96e7b4b 100644 --- a/peach-lib/src/error.rs +++ b/peach-lib/src/error.rs @@ -63,6 +63,8 @@ pub enum PeachError { SaveDynDnsResultError { source: std::io::Error }, #[snafu(display("New passwords do not match"))] PasswordsDoNotMatch, + #[snafu(display("No admin password is set"))] + PasswordNotSet, #[snafu(display("The supplied password was not correct"))] InvalidPassword, #[snafu(display("Error saving new password: {}", msg))] diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index c35edbd..f0e84b9 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -1,34 +1,24 @@ -use crate::config_manager::{get_peachcloud_domain, load_peach_config}; +use crate::config_manager::{get_peachcloud_domain, load_peach_config, + set_admin_password_hash, get_admin_password_hash, + get_temporary_password_hash, set_temporary_password_hash}; use crate::error::PeachError; use crate::error::StdIoError; use crate::sbot_client; +use log::info; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; use snafu::ResultExt; use std::iter; use std::process::Command; - -/// filepath where nginx basic auth passwords are stored -pub const HTPASSWD_FILE: &str = "/var/lib/peachcloud/passwords/htpasswd"; -/// filepath where random temporary password is stored for password resets -pub const HTPASSWD_TEMPORARY_PASSWORD_FILE: &str = - "/var/lib/peachcloud/passwords/temporary_password"; -/// the username of the user for nginx basic auth -pub const PEACHCLOUD_AUTH_USER: &str = "admin"; +use crypto::digest::Digest; +use crypto::sha3::Sha3; /// Returns Ok(()) if the supplied password is correct, /// and returns Err if the supplied password is incorrect. pub fn verify_password(password: &str) -> Result<(), PeachError> { - let output = Command::new("/usr/bin/htpasswd") - .arg("-vb") - .arg(HTPASSWD_FILE) - .arg(PEACHCLOUD_AUTH_USER) - .arg(password) - .output() - .context(StdIoError { - msg: "htpasswd is not installed", - })?; - if output.status.success() { + let real_admin_password_hash = get_admin_password_hash()?; + let password_hash = hash_password(&password.to_string()); + if real_admin_password_hash == password_hash { Ok(()) } else { Err(PeachError::InvalidPassword) @@ -49,57 +39,46 @@ pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Resul /// Uses htpasswd to set a new password for the admin user pub fn set_new_password(new_password: &str) -> Result<(), PeachError> { - let output = Command::new("/usr/bin/htpasswd") - .arg("-cb") - .arg(HTPASSWD_FILE) - .arg(PEACHCLOUD_AUTH_USER) - .arg(new_password) - .output() - .context(StdIoError { - msg: "htpasswd is not installed", - })?; - if output.status.success() { - Ok(()) - } else { - let err_output = String::from_utf8(output.stderr)?; - Err(PeachError::FailedToSetNewPassword { msg: err_output }) + let new_password_hash = hash_password(&new_password.to_string()); + let result = set_admin_password_hash(&new_password_hash); + match result { + Ok(_) => { + Ok(()) + }, + Err(err) => { + Err(PeachError::FailedToSetNewPassword { msg: "failed to save password hash".to_string() }) + } } } +/// Creates a hash from a password string +pub fn hash_password(password: &String) -> String { + let mut hasher = Sha3::sha3_256(); + hasher.input_str(password); + hasher.result_str() +} + /// Uses htpasswd to set a new temporary password for the admin user /// which can be used to reset the permanent password pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> { - let output = Command::new("/usr/bin/htpasswd") - .arg("-cb") - .arg(HTPASSWD_TEMPORARY_PASSWORD_FILE) - .arg(PEACHCLOUD_AUTH_USER) - .arg(new_password) - .output() - .context(StdIoError { - msg: "htpasswd is not installed", - })?; - if output.status.success() { - Ok(()) - } else { - let err_output = String::from_utf8(output.stderr)?; - Err(PeachError::FailedToSetNewPassword { msg: err_output }) + let new_password_hash = hash_password(&new_password.to_string()); + let result = set_temporary_password_hash(&new_password_hash); + match result { + Ok(_) => { + Ok(()) + }, + Err(err) => { + Err(PeachError::FailedToSetNewPassword { msg: "failed to save temporary password hash".to_string() }) + } } } /// Returns Ok(()) if the supplied temp_password is correct, /// and returns Err if the supplied temp_password is incorrect pub fn verify_temporary_password(password: &str) -> Result<(), PeachError> { - // TODO: confirm temporary password has not expired - let output = Command::new("/usr/bin/htpasswd") - .arg("-vb") - .arg(HTPASSWD_TEMPORARY_PASSWORD_FILE) - .arg(PEACHCLOUD_AUTH_USER) - .arg(password) - .output() - .context(StdIoError { - msg: "htpasswd is not installed", - })?; - if output.status.success() { + let temporary_admin_password_hash = get_temporary_password_hash()?; + let password_hash = hash_password(&password.to_string()); + if temporary_admin_password_hash == password_hash { Ok(()) } else { Err(PeachError::InvalidPassword) @@ -143,6 +122,7 @@ using this link: http://peach.local/reset_password", msg += &remote_link; // finally send the message to the admins let peach_config = load_peach_config()?; + info!("sending password reset: {}", msg); for ssb_admin_id in peach_config.ssb_admin_ids { sbot_client::private_message(&msg, &ssb_admin_id)?; } diff --git a/peach-web/src/main.rs b/peach-web/src/main.rs index edd17bd..1f12ab2 100644 --- a/peach-web/src/main.rs +++ b/peach-web/src/main.rs @@ -87,6 +87,7 @@ fn init_rocket() -> Rocket { configure_dns, // WEB ROUTE configure_dns_post, // WEB ROUTE change_password, // WEB ROUTE + change_password_post, // WEB ROUTE reset_password, // WEB ROUTE reset_password_post, // WEB ROUTE send_password_reset_page, // WEB ROUTE diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index ef780d5..1e324c6 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -49,14 +49,12 @@ impl<'r> FromRequest<'r> for Authenticated { .cookies() .get_private(AUTH_COOKIE_KEY) .and_then(|cookie| cookie.value().parse().ok()) - .map(|value: String| { info!("cookie value: {}", value); Authenticated { is_authenticated: true } }); + .map(|value: String| { Authenticated { is_authenticated: true } }); match authenticated { Some(auth) => { - info!("Authenticated!"); request::Outcome::Success(auth) }, None => { - info!("not authenticated!"); request::Outcome::Failure((Status::Forbidden, LoginError::UsernameDoesNotExist)) } } @@ -125,16 +123,17 @@ pub fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> Templ // NOTE: since we currently have just one user, the value of the cookie // is just admin (this is arbitrary). // If we had multiple users, we could put the user_id here. - info!("successfull password form"); cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, ADMIN_USERNAME)); TemplateOrRedirect::Redirect(Redirect::to("/")) } Err(_) => { - info!("invalid password form"); // if unsuccessful login, render /login page again let mut context = LoginContext::build(); context.back = Some("/".to_string()); context.title = Some("Login".to_string()); + context.flash_name = Some("error".to_string()); + let flash_msg = "Invalid password".to_string(); + context.flash_msg = Some(flash_msg); TemplateOrRedirect::Template(Template::render("login", &context)) } } @@ -146,9 +145,9 @@ pub fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> Templ #[get("/logout")] pub fn logout(cookies: &CookieJar<'_>) -> Flash { // logout authenticated user - debug!("Attempting deauthentication of user."); + info!("Attempting deauthentication of user."); cookies.remove_private(Cookie::named(AUTH_COOKIE_KEY)); - Flash::success(Redirect::to("/"), "Logged out") + Flash::success(Redirect::to("/login"), "Logged out") } @@ -376,7 +375,7 @@ pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebErr /// 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) -> Template { +pub fn change_password(flash: Option, auth: Authenticated) -> Template { let mut context = ChangePasswordContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -392,7 +391,7 @@ pub fn change_password(flash: Option) -> Template { /// Change password form request handler. This route is used by a user who is already logged in. #[post("/settings/change_password", data = "")] -pub fn change_password_post(password_form: Form) -> Template { +pub fn change_password_post(password_form: Form, auth: Authenticated) -> Template { let result = save_password_form(password_form.into_inner()); match result { Ok(_) => { @@ -419,7 +418,7 @@ pub fn change_password_post(password_form: Form) -> Template { /// JSON change password form request handler. #[post("/api/v1/settings/change_password", data = "")] -pub fn save_password_form_endpoint(password_form: Json) -> Value { +pub fn save_password_form_endpoint(password_form: Json, auth: Authenticated) -> Value { let result = save_password_form(password_form.into_inner()); match result { Ok(_) => { diff --git a/peach-web/src/routes/device.rs b/peach-web/src/routes/device.rs index 73b2aaf..ef5af99 100644 --- a/peach-web/src/routes/device.rs +++ b/peach-web/src/routes/device.rs @@ -17,6 +17,7 @@ use peach_lib::stats_client::{CpuStatPercentages, DiskUsage, LoadAverage, MemSta use peach_lib::{dyndns_client, network_client, oled_client, sbot_client, stats_client}; use crate::utils::build_json_response; +use crate::routes::authentication::Authenticated; use rocket::serde::json::Value; // HELPERS AND ROUTES FOR /device @@ -150,7 +151,7 @@ impl DeviceContext { } #[get("/device")] -pub fn device_stats(flash: Option) -> Template { +pub fn device_stats(flash: Option, auth: Authenticated) -> Template { // assign context through context_builder call let mut context = DeviceContext::build(); context.back = Some("/".to_string()); @@ -181,7 +182,7 @@ pub fn reboot() -> io::Result { } #[get("/device/reboot")] -pub fn reboot_cmd() -> Flash { +pub fn reboot_cmd(auth: Authenticated) -> Flash { match reboot() { Ok(_) => Flash::success(Redirect::to("/shutdown"), "Rebooting the device"), Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to reboot the device"), @@ -190,7 +191,7 @@ pub fn reboot_cmd() -> Flash { /// JSON request handler for device reboot. #[post("/api/v1/device/reboot")] -pub fn reboot_device() -> Value { +pub fn reboot_device(auth: Authenticated) -> Value { match reboot() { Ok(_) => { debug!("Going down for reboot..."); @@ -219,7 +220,7 @@ pub fn shutdown() -> io::Result { } #[get("/device/shutdown")] -pub fn shutdown_cmd() -> Flash { +pub fn shutdown_cmd(auth: Authenticated) -> Flash { match shutdown() { Ok(_) => Flash::success(Redirect::to("/shutdown"), "Shutting down the device"), Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to shutdown the device"), @@ -228,7 +229,7 @@ pub fn shutdown_cmd() -> Flash { // shutdown the device #[post("/api/v1/device/shutdown")] -pub fn shutdown_device() -> Value { +pub fn shutdown_device(auth: Authenticated) -> Value { match shutdown() { Ok(_) => { debug!("Going down for shutdown..."); @@ -267,7 +268,7 @@ impl ShutdownContext { } #[get("/shutdown")] -pub fn shutdown_menu(flash: Option) -> Template { +pub fn shutdown_menu(flash: Option, auth: Authenticated) -> Template { let mut context = ShutdownContext::build(); context.back = Some("/".to_string()); context.title = Some("Shutdown Device".to_string()); diff --git a/peach-web/src/routes/index.rs b/peach-web/src/routes/index.rs index 4a89487..2d976f5 100644 --- a/peach-web/src/routes/index.rs +++ b/peach-web/src/routes/index.rs @@ -2,6 +2,8 @@ use rocket::{get, request::FlashMessage}; use rocket_dyn_templates::Template; use serde::Serialize; +use crate::routes::authentication::Authenticated; + // HELPERS AND ROUTES FOR / (HOME PAGE) #[derive(Debug, Serialize)] @@ -22,7 +24,7 @@ impl HomeContext { } #[get("/")] -pub fn index() -> Template { +pub fn index(auth: Authenticated) -> Template { let context = HomeContext { flash_name: None, flash_msg: None, diff --git a/peach-web/src/routes/ping.rs b/peach-web/src/routes/ping.rs index e9fc7c1..0c94141 100644 --- a/peach-web/src/routes/ping.rs +++ b/peach-web/src/routes/ping.rs @@ -22,7 +22,7 @@ pub fn ping_pong(auth: Authenticated) -> Value { /// Status route: check availability of `peach-network` microservice. #[get("/api/v1/ping/network")] -pub fn ping_network() -> Value { +pub fn ping_network(auth: Authenticated) -> Value { match network_client::ping() { Ok(_) => { debug!("peach-network responded successfully"); @@ -41,7 +41,7 @@ pub fn ping_network() -> Value { /// Status route: check availability of `peach-oled` microservice. #[get("/api/v1/ping/oled")] -pub fn ping_oled() -> Value { +pub fn ping_oled(auth: Authenticated) -> Value { match oled_client::ping() { Ok(_) => { debug!("peach-oled responded successfully"); @@ -60,7 +60,7 @@ pub fn ping_oled() -> Value { /// Status route: check availability of `peach-stats` microservice. #[get("/api/v1/ping/stats")] -pub fn ping_stats() -> Value { +pub fn ping_stats(auth: Authenticated) -> Value { match stats_client::ping() { Ok(_) => { debug!("peach-stats responded successfully"); diff --git a/peach-web/src/routes/scuttlebutt.rs b/peach-web/src/routes/scuttlebutt.rs index 9a705a8..1934ce7 100644 --- a/peach-web/src/routes/scuttlebutt.rs +++ b/peach-web/src/routes/scuttlebutt.rs @@ -4,6 +4,8 @@ use rocket::{get, request::FlashMessage}; use rocket_dyn_templates::Template; use serde::Serialize; +use crate::routes::authentication::Authenticated; + // HELPERS AND ROUTES FOR /messages #[derive(Debug, Serialize)] @@ -26,7 +28,7 @@ impl MessageContext { } #[get("/messages")] -pub fn messages(flash: Option) -> Template { +pub fn messages(flash: Option, auth: Authenticated) -> Template { let mut context = MessageContext::build(); context.back = Some("/".to_string()); context.title = Some("Private Messages".to_string()); @@ -61,7 +63,7 @@ impl PeerContext { } #[get("/peers")] -pub fn peers(flash: Option) -> Template { +pub fn peers(flash: Option, auth: Authenticated) -> Template { let mut context = PeerContext::build(); context.back = Some("/".to_string()); context.title = Some("Scuttlebutt Peers".to_string()); @@ -96,7 +98,7 @@ impl ProfileContext { } #[get("/profile")] -pub fn profile(flash: Option) -> Template { +pub fn profile(flash: Option, auth: Authenticated) -> Template { let mut context = ProfileContext::build(); context.back = Some("/".to_string()); context.title = Some("Profile".to_string()); diff --git a/peach-web/src/routes/settings/admin.rs b/peach-web/src/routes/settings/admin.rs index a688744..899c2f8 100644 --- a/peach-web/src/routes/settings/admin.rs +++ b/peach-web/src/routes/settings/admin.rs @@ -12,6 +12,7 @@ use peach_lib::config_manager; use peach_lib::config_manager::load_peach_config; use crate::error::PeachWebError; +use crate::routes::authentication::Authenticated; // HELPERS AND ROUTES FOR /settings/configure_admin @@ -40,7 +41,7 @@ impl ConfigureAdminContext { /// View and delete currently configured admin. #[get("/settings/configure_admin")] -pub fn configure_admin(flash: Option) -> Template { +pub fn configure_admin(flash: Option, auth: Authenticated) -> Template { let mut context = ConfigureAdminContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -87,7 +88,7 @@ pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError } #[get("/settings/admin/add")] -pub fn add_admin(flash: Option) -> Template { +pub fn add_admin(flash: Option, auth: Authenticated) -> Template { let mut context = AddAdminContext::build(); context.back = Some("/settings/configure_admin".to_string()); context.title = Some("Add Admin".to_string()); @@ -102,7 +103,7 @@ pub fn add_admin(flash: Option) -> Template { } #[post("/settings/admin/add", data = "")] -pub fn add_admin_post(add_admin_form: Form) -> Flash { +pub fn add_admin_post(add_admin_form: Form, auth: Authenticated) -> Flash { let result = save_add_admin_form(add_admin_form.into_inner()); let url = uri!(configure_admin); match result { @@ -119,7 +120,7 @@ pub struct DeleteAdminForm { } #[post("/settings/admin/delete", data = "")] -pub fn delete_admin_post(delete_admin_form: Form) -> Flash { +pub fn delete_admin_post(delete_admin_form: Form, auth: Authenticated) -> Flash { let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id); let url = uri!(configure_admin); match result { diff --git a/peach-web/src/routes/settings/dns.rs b/peach-web/src/routes/settings/dns.rs index 6b021a3..5b3df65 100644 --- a/peach-web/src/routes/settings/dns.rs +++ b/peach-web/src/routes/settings/dns.rs @@ -20,6 +20,7 @@ use peach_lib::jsonrpc_client_core::{Error, ErrorKind}; use peach_lib::jsonrpc_core::types::error::ErrorCode; use crate::error::PeachWebError; +use crate::routes::authentication::Authenticated; use crate::utils::build_json_response; use rocket::serde::json::Value; @@ -113,7 +114,7 @@ impl ConfigureDNSContext { } #[get("/network/dns")] -pub fn configure_dns(flash: Option) -> Template { +pub fn configure_dns(flash: Option, auth: Authenticated) -> Template { let mut context = ConfigureDNSContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -128,7 +129,7 @@ pub fn configure_dns(flash: Option) -> Template { } #[post("/network/dns", data = "")] -pub fn configure_dns_post(dns: Form) -> Template { +pub fn configure_dns_post(dns: Form, auth: Authenticated) -> Template { let result = save_dns_configuration(dns.into_inner()); match result { Ok(_) => { @@ -153,7 +154,7 @@ pub fn configure_dns_post(dns: Form) -> Template { } #[post("/api/v1/dns/configure", data = "")] -pub fn save_dns_configuration_endpoint(dns_form: Json) -> Value { +pub fn save_dns_configuration_endpoint(dns_form: Json, auth: Authenticated) -> Value { let result = save_dns_configuration(dns_form.into_inner()); match result { Ok(_) => { diff --git a/peach-web/src/routes/settings/network.rs b/peach-web/src/routes/settings/network.rs index 28fddd8..0c07092 100644 --- a/peach-web/src/routes/settings/network.rs +++ b/peach-web/src/routes/settings/network.rs @@ -20,6 +20,7 @@ use peach_lib::stats_client::Traffic; use crate::utils::monitor; use crate::utils::monitor::{Alert, Data, Threshold}; use crate::utils::build_json_response; +use crate::routes::authentication::Authenticated; use rocket::serde::json::Value; // STRUCTS USED BY NETWORK ROUTES @@ -38,7 +39,7 @@ pub struct WiFi { // HELPERS AND ROUTES FOR /network/wifi/usage/reset #[get("/network/wifi/usage/reset")] -pub fn wifi_usage_reset() -> Flash { +pub fn wifi_usage_reset(auth: Authenticated) -> Flash { let url = uri!(wifi_usage); match monitor::reset_data() { Ok(_) => Flash::success(Redirect::to(url), "Reset stored network traffic total"), @@ -50,7 +51,7 @@ pub fn wifi_usage_reset() -> Flash { } #[post("/network/wifi/connect", data = "")] -pub fn connect_wifi(network: Form) -> Flash { +pub fn connect_wifi(network: Form, auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_detail(ssid = ssid)); match network_client::id("wlan0", ssid) { @@ -63,7 +64,7 @@ pub fn connect_wifi(network: Form) -> Flash { } #[post("/network/wifi/disconnect", data = "")] -pub fn disconnect_wifi(network: Form) -> Flash { +pub fn disconnect_wifi(network: Form, auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_home); match network_client::disable("wlan0", ssid) { @@ -73,7 +74,7 @@ pub fn disconnect_wifi(network: Form) -> Flash { } #[post("/network/wifi/forget", data = "")] -pub fn forget_wifi(network: Form) -> Flash { +pub fn forget_wifi(network: Form, auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_home); match network_client::forget("wlan0", ssid) { @@ -86,7 +87,7 @@ pub fn forget_wifi(network: Form) -> Flash { } #[get("/network/wifi/modify?")] -pub fn wifi_password(ssid: &str, flash: Option) -> Template { +pub fn wifi_password(ssid: &str, flash: Option, auth: Authenticated) -> Template { let mut context = NetworkAddContext { back: Some("/network/wifi".to_string()), flash_name: None, @@ -105,7 +106,7 @@ pub fn wifi_password(ssid: &str, flash: Option) -> Template { } #[post("/network/wifi/modify", data = "")] -pub fn wifi_set_password(wifi: Form) -> Flash { +pub fn wifi_set_password(wifi: Form, auth: Authenticated) -> Flash { let ssid = &wifi.ssid; let pass = &wifi.pass; let url = uri!(network_detail(ssid = ssid)); @@ -273,7 +274,7 @@ impl NetworkContext { } #[get("/network")] -pub fn network_home(flash: Option) -> Template { +pub fn network_home(flash: Option, auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkContext::build(); // set back button (nav) url @@ -293,7 +294,7 @@ pub fn network_home(flash: Option) -> Template { // HELPERS AND ROUTES FOR /network/ap/activate #[get("/network/ap/activate")] -pub fn deploy_ap() -> Flash { +pub fn deploy_ap(auth: Authenticated) -> Flash { // activate the wireless access point debug!("Activating WiFi access point."); match network_client::activate_ap() { @@ -375,7 +376,7 @@ impl NetworkListContext { } #[get("/network/wifi")] -pub fn wifi_list(flash: Option) -> Template { +pub fn wifi_list(flash: Option, auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkListContext::build(); context.back = Some("/network".to_string()); @@ -540,7 +541,7 @@ impl NetworkDetailContext { } #[get("/network/wifi?")] -pub fn network_detail(ssid: &str, flash: Option) -> Template { +pub fn network_detail(ssid: &str, flash: Option, auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkDetailContext::build(); context.back = Some("/network/wifi".to_string()); @@ -559,7 +560,7 @@ pub fn network_detail(ssid: &str, flash: Option) -> Template { // HELPERS AND ROUTES FOR /network/wifi/activate #[get("/network/wifi/activate")] -pub fn deploy_client() -> Flash { +pub fn deploy_client(auth: Authenticated) -> Flash { // activate the wireless client debug!("Activating WiFi client mode."); match network_client::activate_client() { @@ -571,7 +572,7 @@ pub fn deploy_client() -> Flash { // HELPERS AND ROUTES FOR /network/wifi/add #[get("/network/wifi/add")] -pub fn network_add_wifi(flash: Option) -> Template { +pub fn network_add_wifi(flash: Option, auth: Authenticated) -> Template { let mut context = NetworkContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -609,7 +610,7 @@ impl NetworkAddContext { } #[get("/network/wifi/add?")] -pub fn network_add_ssid(ssid: &str, flash: Option) -> Template { +pub fn network_add_ssid(ssid: &str, flash: Option, auth: Authenticated) -> Template { let mut context = NetworkAddContext::build(); context.back = Some("/network/wifi".to_string()); context.selected = Some(ssid.to_string()); @@ -625,7 +626,7 @@ pub fn network_add_ssid(ssid: &str, flash: Option) -> Template { } #[post("/network/wifi/add", data = "")] -pub fn add_credentials(wifi: Form) -> Template { +pub fn add_credentials(wifi: Form, auth: Authenticated) -> Template { // check if the credentials already exist for this access point // note: this is nicer but it's an unstable feature: // if check_saved_aps(&wifi.ssid).contains(true) @@ -719,7 +720,7 @@ impl NetworkAlertContext { } #[get("/network/wifi/usage")] -pub fn wifi_usage(flash: Option) -> Template { +pub fn wifi_usage(flash: Option, auth: Authenticated) -> Template { let mut context = NetworkAlertContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -735,7 +736,7 @@ pub fn wifi_usage(flash: Option) -> Template { } #[post("/network/wifi/usage", data = "")] -pub fn wifi_usage_alerts(thresholds: Form) -> Flash { +pub fn wifi_usage_alerts(thresholds: Form, auth: Authenticated) -> Flash { match monitor::update_store(thresholds.into_inner()) { Ok(_) => { debug!("WiFi data usage thresholds updated."); @@ -755,7 +756,7 @@ pub fn wifi_usage_alerts(thresholds: Form) -> Flash { } #[post("/api/v1/network/wifi/usage", data = "")] -pub fn update_wifi_alerts(thresholds: Json) -> Value { +pub fn update_wifi_alerts(thresholds: Json, auth: Authenticated) -> Value { match monitor::update_store(thresholds.into_inner()) { Ok(_) => { debug!("WiFi data usage thresholds updated."); @@ -773,7 +774,7 @@ pub fn update_wifi_alerts(thresholds: Json) -> Value { } #[post("/api/v1/network/wifi/usage/reset")] -pub fn reset_data_total() -> Value { +pub fn reset_data_total(auth: Authenticated) -> Value { match monitor::reset_data() { Ok(_) => { debug!("Reset network data usage total."); @@ -805,7 +806,7 @@ pub fn reset_data_total() -> Value { // HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION #[post("/api/v1/network/activate_ap")] -pub fn activate_ap() -> Value { +pub fn activate_ap(auth: Authenticated) -> Value { // activate the wireless access point debug!("Activating WiFi access point."); match network_client::activate_ap() { @@ -824,7 +825,7 @@ pub fn activate_ap() -> Value { // HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT #[post("/api/v1/network/activate_client")] -pub fn activate_client() -> Value { +pub fn activate_client(auth: Authenticated) -> Value { // activate the wireless client debug!("Activating WiFi client mode."); match network_client::activate_client() { @@ -841,7 +842,7 @@ pub fn activate_client() -> Value { } #[post("/api/v1/network/wifi", data = "")] -pub fn add_wifi(wifi: Json) -> Value { +pub fn add_wifi(wifi: Json, auth: Authenticated) -> Value { // generate and write wifi config to wpa_supplicant match network_client::add(&wifi.ssid, &wifi.pass) { Ok(_) => { @@ -867,7 +868,7 @@ pub fn add_wifi(wifi: Json) -> Value { } #[post("/api/v1/network/wifi/connect", data = "")] -pub fn connect_ap(ssid: Json) -> Value { +pub fn connect_ap(ssid: Json, auth: Authenticated) -> Value { // retrieve the id for the given network ssid match network_client::id("wlan0", &ssid.ssid) { // attempt connection with the given network @@ -892,7 +893,7 @@ pub fn connect_ap(ssid: Json) -> Value { } #[post("/api/v1/network/wifi/disconnect", data = "")] -pub fn disconnect_ap(ssid: Json) -> Value { +pub fn disconnect_ap(ssid: Json, auth: Authenticated) -> Value { // attempt to disable the current network for wlan0 interface match network_client::disable("wlan0", &ssid.ssid) { Ok(_) => { @@ -909,7 +910,7 @@ pub fn disconnect_ap(ssid: Json) -> Value { } #[post("/api/v1/network/wifi/forget", data = "")] -pub fn forget_ap(network: Json) -> Value { +pub fn forget_ap(network: Json, auth: Authenticated) -> Value { let ssid = &network.ssid; match network_client::forget("wlan0", ssid) { Ok(_) => { @@ -928,7 +929,7 @@ pub fn forget_ap(network: Json) -> Value { } #[post("/api/v1/network/wifi/modify", data = "")] -pub fn modify_password(wifi: Json) -> Value { +pub fn modify_password(wifi: Json, auth: Authenticated) -> Value { let ssid = &wifi.ssid; let pass = &wifi.pass; // we are using a helper function (`update`) to delete the old @@ -953,7 +954,7 @@ pub fn modify_password(wifi: Json) -> Value { // HELPERS AND ROUTES FOR NETWORK STATE QUERIES #[get("/api/v1/network/ip")] -pub fn return_ip() -> Value { +pub fn return_ip(auth: Authenticated) -> Value { // retrieve ip for wlan0 or set to x.x.x.x if not found let wlan_ip = match network_client::ip("wlan0") { Ok(ip) => ip, @@ -973,7 +974,7 @@ pub fn return_ip() -> Value { } #[get("/api/v1/network/rssi")] -pub fn return_rssi() -> Value { +pub fn return_rssi(auth: Authenticated) -> Value { // retrieve rssi for connected network match network_client::rssi("wlan0") { Ok(rssi) => { @@ -990,7 +991,7 @@ pub fn return_rssi() -> Value { } #[get("/api/v1/network/ssid")] -pub fn return_ssid() -> Value { +pub fn return_ssid(auth: Authenticated) -> Value { // retrieve ssid for connected network match network_client::ssid("wlan0") { Ok(network) => { @@ -1007,7 +1008,7 @@ pub fn return_ssid() -> Value { } #[get("/api/v1/network/state")] -pub fn return_state() -> Value { +pub fn return_state(auth: Authenticated) -> Value { // retrieve state of wlan0 or set to x.x.x.x if not found let wlan_state = match network_client::state("wlan0") { Ok(state) => state, @@ -1027,7 +1028,7 @@ pub fn return_state() -> Value { } #[get("/api/v1/network/status")] -pub fn return_status() -> Value { +pub fn return_status(auth: Authenticated) -> Value { // retrieve status info for wlan0 interface match network_client::status("wlan0") { Ok(network) => { @@ -1044,7 +1045,7 @@ pub fn return_status() -> Value { } #[get("/api/v1/network/wifi")] -pub fn scan_networks() -> Value { +pub fn scan_networks(auth: Authenticated) -> Value { // retrieve scan results for access-points within range of wlan0 match network_client::available_networks("wlan0") { Ok(networks) => { diff --git a/peach-web/static/js/common.js b/peach-web/static/js/common.js index 9a5d8a4..05167d2 100644 --- a/peach-web/static/js/common.js +++ b/peach-web/static/js/common.js @@ -43,14 +43,5 @@ PEACH.flashMsg = function(status, msg) { } } -// add click event to logout button which logs out of http basic auth -// by "trying to login" with invalid credentials (user@logout) -document.getElementById('logoutButton').onclick = function(e){ - e.preventDefault(); - var logoutUrl = "http://user:logout@" + window.location.hostname - window.location = logoutUrl; -} - var addInstance = PEACH; addInstance.add(); -addInstance.logout(); diff --git a/peach-web/templates/login.html.tera b/peach-web/templates/login.html.tera index 97390ed..c64943b 100644 --- a/peach-web/templates/login.html.tera +++ b/peach-web/templates/login.html.tera @@ -5,9 +5,9 @@
- + - +
Cancel diff --git a/peach-web/templates/nav.html.tera b/peach-web/templates/nav.html.tera index 004ca7b..33c50e7 100644 --- a/peach-web/templates/nav.html.tera +++ b/peach-web/templates/nav.html.tera @@ -6,7 +6,7 @@ Back

{{ title }}

- + Enter -- 2.40.1 From 18fbb7fa5f4f1f5a0cdc429de1b5eb02655d447c Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 16:54:18 +0100 Subject: [PATCH 3/7] Fix typos --- peach-web/src/routes/authentication.rs | 6 ++---- peach-web/src/routes/catchers.rs | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 1e324c6..9bcdf2d 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -30,9 +30,7 @@ pub struct Authenticated { #[derive(Debug)] pub enum LoginError { - InvalidData, - UsernameDoesNotExist, - WrongPassword + UserNotLoggedIn } /// Request guard which returns an Authenticated struct with is_authenticated=true @@ -55,7 +53,7 @@ impl<'r> FromRequest<'r> for Authenticated { request::Outcome::Success(auth) }, None => { - request::Outcome::Failure((Status::Forbidden, LoginError::UsernameDoesNotExist)) + request::Outcome::Failure((Status::Forbidden, LoginError::UserNotLoggedIn)) } } } diff --git a/peach-web/src/routes/catchers.rs b/peach-web/src/routes/catchers.rs index e494395..a096cac 100644 --- a/peach-web/src/routes/catchers.rs +++ b/peach-web/src/routes/catchers.rs @@ -51,7 +51,7 @@ pub fn internal_error() -> Template { Template::render("internal_error", context) } -// HELPERS AND ROUNTES FOR 403 FORBIDDEN +// HELPERS AND ROUTES FOR 403 FORBIDDEN #[catch(403)] pub fn forbidden() -> Redirect { -- 2.40.1 From 6605af7bedb980c02eebf8cc6af9aa2fe68d28c5 Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 16:55:58 +0100 Subject: [PATCH 4/7] Fix clippy errors --- peach-lib/src/password_utils.rs | 4 +- peach-web/src/routes/authentication.rs | 14 +++--- peach-web/src/routes/device.rs | 12 ++--- peach-web/src/routes/index.rs | 2 +- peach-web/src/routes/ping.rs | 8 +-- peach-web/src/routes/scuttlebutt.rs | 6 +-- peach-web/src/routes/settings/admin.rs | 8 +-- peach-web/src/routes/settings/dns.rs | 6 +-- peach-web/src/routes/settings/network.rs | 62 ++++++++++++------------ peach-web/src/utils.rs | 2 +- 10 files changed, 62 insertions(+), 62 deletions(-) diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index f0e84b9..8612329 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -45,7 +45,7 @@ pub fn set_new_password(new_password: &str) -> Result<(), PeachError> { Ok(_) => { Ok(()) }, - Err(err) => { + Err(_err) => { Err(PeachError::FailedToSetNewPassword { msg: "failed to save password hash".to_string() }) } } @@ -67,7 +67,7 @@ pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> Ok(_) => { Ok(()) }, - Err(err) => { + Err(_err) => { Err(PeachError::FailedToSetNewPassword { msg: "failed to save temporary password hash".to_string() }) } } diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 9bcdf2d..3e0670a 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -1,4 +1,4 @@ -use log::{debug, info}; +use log::{info}; use rocket::request::{FlashMessage}; use rocket::form::{Form, FromForm}; use rocket::response::{Flash, Redirect}; @@ -15,8 +15,8 @@ use crate::utils::{build_json_response, TemplateOrRedirect}; use rocket::serde::json::Value; use rocket::request::{self, FromRequest, Request}; use rocket::http::{Cookie, CookieJar, Status}; -use rocket::outcome::IntoOutcome; -use websocket::futures::future::Ok; + + // HELPERS AND STRUCTS FOR AUTHENTICATION WITH COOKIES @@ -47,7 +47,7 @@ impl<'r> FromRequest<'r> for Authenticated { .cookies() .get_private(AUTH_COOKIE_KEY) .and_then(|cookie| cookie.value().parse().ok()) - .map(|value: String| { Authenticated { is_authenticated: true } }); + .map(|_value: String| { Authenticated { is_authenticated: true } }); match authenticated { Some(auth) => { request::Outcome::Success(auth) @@ -373,7 +373,7 @@ pub fn save_password_form(password_form: PasswordForm) -> Result<(), PeachWebErr /// 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, auth: Authenticated) -> Template { +pub fn change_password(flash: Option, _auth: Authenticated) -> Template { let mut context = ChangePasswordContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -389,7 +389,7 @@ pub fn change_password(flash: Option, auth: Authenticated) -> Temp /// Change password form request handler. This route is used by a user who is already logged in. #[post("/settings/change_password", data = "")] -pub fn change_password_post(password_form: Form, auth: Authenticated) -> Template { +pub fn change_password_post(password_form: Form, _auth: Authenticated) -> Template { let result = save_password_form(password_form.into_inner()); match result { Ok(_) => { @@ -416,7 +416,7 @@ pub fn change_password_post(password_form: Form, auth: Authenticat /// JSON change password form request handler. #[post("/api/v1/settings/change_password", data = "")] -pub fn save_password_form_endpoint(password_form: Json, auth: Authenticated) -> Value { +pub fn save_password_form_endpoint(password_form: Json, _auth: Authenticated) -> Value { let result = save_password_form(password_form.into_inner()); match result { Ok(_) => { diff --git a/peach-web/src/routes/device.rs b/peach-web/src/routes/device.rs index ef5af99..d06983b 100644 --- a/peach-web/src/routes/device.rs +++ b/peach-web/src/routes/device.rs @@ -151,7 +151,7 @@ impl DeviceContext { } #[get("/device")] -pub fn device_stats(flash: Option, auth: Authenticated) -> Template { +pub fn device_stats(flash: Option, _auth: Authenticated) -> Template { // assign context through context_builder call let mut context = DeviceContext::build(); context.back = Some("/".to_string()); @@ -182,7 +182,7 @@ pub fn reboot() -> io::Result { } #[get("/device/reboot")] -pub fn reboot_cmd(auth: Authenticated) -> Flash { +pub fn reboot_cmd(_auth: Authenticated) -> Flash { match reboot() { Ok(_) => Flash::success(Redirect::to("/shutdown"), "Rebooting the device"), Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to reboot the device"), @@ -191,7 +191,7 @@ pub fn reboot_cmd(auth: Authenticated) -> Flash { /// JSON request handler for device reboot. #[post("/api/v1/device/reboot")] -pub fn reboot_device(auth: Authenticated) -> Value { +pub fn reboot_device(_auth: Authenticated) -> Value { match reboot() { Ok(_) => { debug!("Going down for reboot..."); @@ -220,7 +220,7 @@ pub fn shutdown() -> io::Result { } #[get("/device/shutdown")] -pub fn shutdown_cmd(auth: Authenticated) -> Flash { +pub fn shutdown_cmd(_auth: Authenticated) -> Flash { match shutdown() { Ok(_) => Flash::success(Redirect::to("/shutdown"), "Shutting down the device"), Err(_) => Flash::error(Redirect::to("/shutdown"), "Failed to shutdown the device"), @@ -229,7 +229,7 @@ pub fn shutdown_cmd(auth: Authenticated) -> Flash { // shutdown the device #[post("/api/v1/device/shutdown")] -pub fn shutdown_device(auth: Authenticated) -> Value { +pub fn shutdown_device(_auth: Authenticated) -> Value { match shutdown() { Ok(_) => { debug!("Going down for shutdown..."); @@ -268,7 +268,7 @@ impl ShutdownContext { } #[get("/shutdown")] -pub fn shutdown_menu(flash: Option, auth: Authenticated) -> Template { +pub fn shutdown_menu(flash: Option, _auth: Authenticated) -> Template { let mut context = ShutdownContext::build(); context.back = Some("/".to_string()); context.title = Some("Shutdown Device".to_string()); diff --git a/peach-web/src/routes/index.rs b/peach-web/src/routes/index.rs index 2d976f5..1ca4443 100644 --- a/peach-web/src/routes/index.rs +++ b/peach-web/src/routes/index.rs @@ -24,7 +24,7 @@ impl HomeContext { } #[get("/")] -pub fn index(auth: Authenticated) -> Template { +pub fn index(_auth: Authenticated) -> Template { let context = HomeContext { flash_name: None, flash_msg: None, diff --git a/peach-web/src/routes/ping.rs b/peach-web/src/routes/ping.rs index 0c94141..aed39ec 100644 --- a/peach-web/src/routes/ping.rs +++ b/peach-web/src/routes/ping.rs @@ -12,7 +12,7 @@ use crate::routes::authentication::Authenticated; /// Status route: useful for checking connectivity from web client. #[get("/api/v1/ping")] -pub fn ping_pong(auth: Authenticated) -> Value { +pub fn ping_pong(_auth: Authenticated) -> Value { //pub fn ping_pong() -> Value { // ping pong let status = "success".to_string(); @@ -22,7 +22,7 @@ pub fn ping_pong(auth: Authenticated) -> Value { /// Status route: check availability of `peach-network` microservice. #[get("/api/v1/ping/network")] -pub fn ping_network(auth: Authenticated) -> Value { +pub fn ping_network(_auth: Authenticated) -> Value { match network_client::ping() { Ok(_) => { debug!("peach-network responded successfully"); @@ -41,7 +41,7 @@ pub fn ping_network(auth: Authenticated) -> Value { /// Status route: check availability of `peach-oled` microservice. #[get("/api/v1/ping/oled")] -pub fn ping_oled(auth: Authenticated) -> Value { +pub fn ping_oled(_auth: Authenticated) -> Value { match oled_client::ping() { Ok(_) => { debug!("peach-oled responded successfully"); @@ -60,7 +60,7 @@ pub fn ping_oled(auth: Authenticated) -> Value { /// Status route: check availability of `peach-stats` microservice. #[get("/api/v1/ping/stats")] -pub fn ping_stats(auth: Authenticated) -> Value { +pub fn ping_stats(_auth: Authenticated) -> Value { match stats_client::ping() { Ok(_) => { debug!("peach-stats responded successfully"); diff --git a/peach-web/src/routes/scuttlebutt.rs b/peach-web/src/routes/scuttlebutt.rs index 1934ce7..7e9200d 100644 --- a/peach-web/src/routes/scuttlebutt.rs +++ b/peach-web/src/routes/scuttlebutt.rs @@ -28,7 +28,7 @@ impl MessageContext { } #[get("/messages")] -pub fn messages(flash: Option, auth: Authenticated) -> Template { +pub fn messages(flash: Option, _auth: Authenticated) -> Template { let mut context = MessageContext::build(); context.back = Some("/".to_string()); context.title = Some("Private Messages".to_string()); @@ -63,7 +63,7 @@ impl PeerContext { } #[get("/peers")] -pub fn peers(flash: Option, auth: Authenticated) -> Template { +pub fn peers(flash: Option, _auth: Authenticated) -> Template { let mut context = PeerContext::build(); context.back = Some("/".to_string()); context.title = Some("Scuttlebutt Peers".to_string()); @@ -98,7 +98,7 @@ impl ProfileContext { } #[get("/profile")] -pub fn profile(flash: Option, auth: Authenticated) -> Template { +pub fn profile(flash: Option, _auth: Authenticated) -> Template { let mut context = ProfileContext::build(); context.back = Some("/".to_string()); context.title = Some("Profile".to_string()); diff --git a/peach-web/src/routes/settings/admin.rs b/peach-web/src/routes/settings/admin.rs index 899c2f8..4f8980a 100644 --- a/peach-web/src/routes/settings/admin.rs +++ b/peach-web/src/routes/settings/admin.rs @@ -41,7 +41,7 @@ impl ConfigureAdminContext { /// View and delete currently configured admin. #[get("/settings/configure_admin")] -pub fn configure_admin(flash: Option, auth: Authenticated) -> Template { +pub fn configure_admin(flash: Option, _auth: Authenticated) -> Template { let mut context = ConfigureAdminContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -88,7 +88,7 @@ pub fn save_add_admin_form(admin_form: AddAdminForm) -> Result<(), PeachWebError } #[get("/settings/admin/add")] -pub fn add_admin(flash: Option, auth: Authenticated) -> Template { +pub fn add_admin(flash: Option, _auth: Authenticated) -> Template { let mut context = AddAdminContext::build(); context.back = Some("/settings/configure_admin".to_string()); context.title = Some("Add Admin".to_string()); @@ -103,7 +103,7 @@ pub fn add_admin(flash: Option, auth: Authenticated) -> Template { } #[post("/settings/admin/add", data = "")] -pub fn add_admin_post(add_admin_form: Form, auth: Authenticated) -> Flash { +pub fn add_admin_post(add_admin_form: Form, _auth: Authenticated) -> Flash { let result = save_add_admin_form(add_admin_form.into_inner()); let url = uri!(configure_admin); match result { @@ -120,7 +120,7 @@ pub struct DeleteAdminForm { } #[post("/settings/admin/delete", data = "")] -pub fn delete_admin_post(delete_admin_form: Form, auth: Authenticated) -> Flash { +pub fn delete_admin_post(delete_admin_form: Form, _auth: Authenticated) -> Flash { let result = config_manager::delete_ssb_admin_id(&delete_admin_form.ssb_id); let url = uri!(configure_admin); match result { diff --git a/peach-web/src/routes/settings/dns.rs b/peach-web/src/routes/settings/dns.rs index 5b3df65..7bca89c 100644 --- a/peach-web/src/routes/settings/dns.rs +++ b/peach-web/src/routes/settings/dns.rs @@ -114,7 +114,7 @@ impl ConfigureDNSContext { } #[get("/network/dns")] -pub fn configure_dns(flash: Option, auth: Authenticated) -> Template { +pub fn configure_dns(flash: Option, _auth: Authenticated) -> Template { let mut context = ConfigureDNSContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -129,7 +129,7 @@ pub fn configure_dns(flash: Option, auth: Authenticated) -> Templa } #[post("/network/dns", data = "")] -pub fn configure_dns_post(dns: Form, auth: Authenticated) -> Template { +pub fn configure_dns_post(dns: Form, _auth: Authenticated) -> Template { let result = save_dns_configuration(dns.into_inner()); match result { Ok(_) => { @@ -154,7 +154,7 @@ pub fn configure_dns_post(dns: Form, auth: Authenticated) -> Template { } #[post("/api/v1/dns/configure", data = "")] -pub fn save_dns_configuration_endpoint(dns_form: Json, auth: Authenticated) -> Value { +pub fn save_dns_configuration_endpoint(dns_form: Json, _auth: Authenticated) -> Value { let result = save_dns_configuration(dns_form.into_inner()); match result { Ok(_) => { diff --git a/peach-web/src/routes/settings/network.rs b/peach-web/src/routes/settings/network.rs index 0c07092..9b80a37 100644 --- a/peach-web/src/routes/settings/network.rs +++ b/peach-web/src/routes/settings/network.rs @@ -39,7 +39,7 @@ pub struct WiFi { // HELPERS AND ROUTES FOR /network/wifi/usage/reset #[get("/network/wifi/usage/reset")] -pub fn wifi_usage_reset(auth: Authenticated) -> Flash { +pub fn wifi_usage_reset(_auth: Authenticated) -> Flash { let url = uri!(wifi_usage); match monitor::reset_data() { Ok(_) => Flash::success(Redirect::to(url), "Reset stored network traffic total"), @@ -51,7 +51,7 @@ pub fn wifi_usage_reset(auth: Authenticated) -> Flash { } #[post("/network/wifi/connect", data = "")] -pub fn connect_wifi(network: Form, auth: Authenticated) -> Flash { +pub fn connect_wifi(network: Form, _auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_detail(ssid = ssid)); match network_client::id("wlan0", ssid) { @@ -64,7 +64,7 @@ pub fn connect_wifi(network: Form, auth: Authenticated) -> Flash } #[post("/network/wifi/disconnect", data = "")] -pub fn disconnect_wifi(network: Form, auth: Authenticated) -> Flash { +pub fn disconnect_wifi(network: Form, _auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_home); match network_client::disable("wlan0", ssid) { @@ -74,7 +74,7 @@ pub fn disconnect_wifi(network: Form, auth: Authenticated) -> Flash, auth: Authenticated) -> Flash { +pub fn forget_wifi(network: Form, _auth: Authenticated) -> Flash { let ssid = &network.ssid; let url = uri!(network_home); match network_client::forget("wlan0", ssid) { @@ -87,7 +87,7 @@ pub fn forget_wifi(network: Form, auth: Authenticated) -> Flash } #[get("/network/wifi/modify?")] -pub fn wifi_password(ssid: &str, flash: Option, auth: Authenticated) -> Template { +pub fn wifi_password(ssid: &str, flash: Option, _auth: Authenticated) -> Template { let mut context = NetworkAddContext { back: Some("/network/wifi".to_string()), flash_name: None, @@ -106,7 +106,7 @@ pub fn wifi_password(ssid: &str, flash: Option, auth: Authenticate } #[post("/network/wifi/modify", data = "")] -pub fn wifi_set_password(wifi: Form, auth: Authenticated) -> Flash { +pub fn wifi_set_password(wifi: Form, _auth: Authenticated) -> Flash { let ssid = &wifi.ssid; let pass = &wifi.pass; let url = uri!(network_detail(ssid = ssid)); @@ -274,7 +274,7 @@ impl NetworkContext { } #[get("/network")] -pub fn network_home(flash: Option, auth: Authenticated) -> Template { +pub fn network_home(flash: Option, _auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkContext::build(); // set back button (nav) url @@ -294,7 +294,7 @@ pub fn network_home(flash: Option, auth: Authenticated) -> Templat // HELPERS AND ROUTES FOR /network/ap/activate #[get("/network/ap/activate")] -pub fn deploy_ap(auth: Authenticated) -> Flash { +pub fn deploy_ap(_auth: Authenticated) -> Flash { // activate the wireless access point debug!("Activating WiFi access point."); match network_client::activate_ap() { @@ -376,7 +376,7 @@ impl NetworkListContext { } #[get("/network/wifi")] -pub fn wifi_list(flash: Option, auth: Authenticated) -> Template { +pub fn wifi_list(flash: Option, _auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkListContext::build(); context.back = Some("/network".to_string()); @@ -541,7 +541,7 @@ impl NetworkDetailContext { } #[get("/network/wifi?")] -pub fn network_detail(ssid: &str, flash: Option, auth: Authenticated) -> Template { +pub fn network_detail(ssid: &str, flash: Option, _auth: Authenticated) -> Template { // assign context through context_builder call let mut context = NetworkDetailContext::build(); context.back = Some("/network/wifi".to_string()); @@ -560,7 +560,7 @@ pub fn network_detail(ssid: &str, flash: Option, auth: Authenticat // HELPERS AND ROUTES FOR /network/wifi/activate #[get("/network/wifi/activate")] -pub fn deploy_client(auth: Authenticated) -> Flash { +pub fn deploy_client(_auth: Authenticated) -> Flash { // activate the wireless client debug!("Activating WiFi client mode."); match network_client::activate_client() { @@ -572,7 +572,7 @@ pub fn deploy_client(auth: Authenticated) -> Flash { // HELPERS AND ROUTES FOR /network/wifi/add #[get("/network/wifi/add")] -pub fn network_add_wifi(flash: Option, auth: Authenticated) -> Template { +pub fn network_add_wifi(flash: Option, _auth: Authenticated) -> Template { let mut context = NetworkContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -610,7 +610,7 @@ impl NetworkAddContext { } #[get("/network/wifi/add?")] -pub fn network_add_ssid(ssid: &str, flash: Option, auth: Authenticated) -> Template { +pub fn network_add_ssid(ssid: &str, flash: Option, _auth: Authenticated) -> Template { let mut context = NetworkAddContext::build(); context.back = Some("/network/wifi".to_string()); context.selected = Some(ssid.to_string()); @@ -626,7 +626,7 @@ pub fn network_add_ssid(ssid: &str, flash: Option, auth: Authentic } #[post("/network/wifi/add", data = "")] -pub fn add_credentials(wifi: Form, auth: Authenticated) -> Template { +pub fn add_credentials(wifi: Form, _auth: Authenticated) -> Template { // check if the credentials already exist for this access point // note: this is nicer but it's an unstable feature: // if check_saved_aps(&wifi.ssid).contains(true) @@ -720,7 +720,7 @@ impl NetworkAlertContext { } #[get("/network/wifi/usage")] -pub fn wifi_usage(flash: Option, auth: Authenticated) -> Template { +pub fn wifi_usage(flash: Option, _auth: Authenticated) -> Template { let mut context = NetworkAlertContext::build(); // set back icon link to network route context.back = Some("/network".to_string()); @@ -736,7 +736,7 @@ pub fn wifi_usage(flash: Option, auth: Authenticated) -> Template } #[post("/network/wifi/usage", data = "")] -pub fn wifi_usage_alerts(thresholds: Form, auth: Authenticated) -> Flash { +pub fn wifi_usage_alerts(thresholds: Form, _auth: Authenticated) -> Flash { match monitor::update_store(thresholds.into_inner()) { Ok(_) => { debug!("WiFi data usage thresholds updated."); @@ -756,7 +756,7 @@ pub fn wifi_usage_alerts(thresholds: Form, auth: Authenticated) -> Fl } #[post("/api/v1/network/wifi/usage", data = "")] -pub fn update_wifi_alerts(thresholds: Json, auth: Authenticated) -> Value { +pub fn update_wifi_alerts(thresholds: Json, _auth: Authenticated) -> Value { match monitor::update_store(thresholds.into_inner()) { Ok(_) => { debug!("WiFi data usage thresholds updated."); @@ -774,7 +774,7 @@ pub fn update_wifi_alerts(thresholds: Json, auth: Authenticated) -> V } #[post("/api/v1/network/wifi/usage/reset")] -pub fn reset_data_total(auth: Authenticated) -> Value { +pub fn reset_data_total(_auth: Authenticated) -> Value { match monitor::reset_data() { Ok(_) => { debug!("Reset network data usage total."); @@ -806,7 +806,7 @@ pub fn reset_data_total(auth: Authenticated) -> Value { // HELPERS AND ROUTES FOR ACCESS POINT ACTIVATION #[post("/api/v1/network/activate_ap")] -pub fn activate_ap(auth: Authenticated) -> Value { +pub fn activate_ap(_auth: Authenticated) -> Value { // activate the wireless access point debug!("Activating WiFi access point."); match network_client::activate_ap() { @@ -825,7 +825,7 @@ pub fn activate_ap(auth: Authenticated) -> Value { // HELPERS AND ROUTES FOR WIFI CLIENT MANAGEMENT #[post("/api/v1/network/activate_client")] -pub fn activate_client(auth: Authenticated) -> Value { +pub fn activate_client(_auth: Authenticated) -> Value { // activate the wireless client debug!("Activating WiFi client mode."); match network_client::activate_client() { @@ -842,7 +842,7 @@ pub fn activate_client(auth: Authenticated) -> Value { } #[post("/api/v1/network/wifi", data = "")] -pub fn add_wifi(wifi: Json, auth: Authenticated) -> Value { +pub fn add_wifi(wifi: Json, _auth: Authenticated) -> Value { // generate and write wifi config to wpa_supplicant match network_client::add(&wifi.ssid, &wifi.pass) { Ok(_) => { @@ -868,7 +868,7 @@ pub fn add_wifi(wifi: Json, auth: Authenticated) -> Value { } #[post("/api/v1/network/wifi/connect", data = "")] -pub fn connect_ap(ssid: Json, auth: Authenticated) -> Value { +pub fn connect_ap(ssid: Json, _auth: Authenticated) -> Value { // retrieve the id for the given network ssid match network_client::id("wlan0", &ssid.ssid) { // attempt connection with the given network @@ -893,7 +893,7 @@ pub fn connect_ap(ssid: Json, auth: Authenticated) -> Value { } #[post("/api/v1/network/wifi/disconnect", data = "")] -pub fn disconnect_ap(ssid: Json, auth: Authenticated) -> Value { +pub fn disconnect_ap(ssid: Json, _auth: Authenticated) -> Value { // attempt to disable the current network for wlan0 interface match network_client::disable("wlan0", &ssid.ssid) { Ok(_) => { @@ -910,7 +910,7 @@ pub fn disconnect_ap(ssid: Json, auth: Authenticated) -> Value { } #[post("/api/v1/network/wifi/forget", data = "")] -pub fn forget_ap(network: Json, auth: Authenticated) -> Value { +pub fn forget_ap(network: Json, _auth: Authenticated) -> Value { let ssid = &network.ssid; match network_client::forget("wlan0", ssid) { Ok(_) => { @@ -929,7 +929,7 @@ pub fn forget_ap(network: Json, auth: Authenticated) -> Value { } #[post("/api/v1/network/wifi/modify", data = "")] -pub fn modify_password(wifi: Json, auth: Authenticated) -> Value { +pub fn modify_password(wifi: Json, _auth: Authenticated) -> Value { let ssid = &wifi.ssid; let pass = &wifi.pass; // we are using a helper function (`update`) to delete the old @@ -954,7 +954,7 @@ pub fn modify_password(wifi: Json, auth: Authenticated) -> Value { // HELPERS AND ROUTES FOR NETWORK STATE QUERIES #[get("/api/v1/network/ip")] -pub fn return_ip(auth: Authenticated) -> Value { +pub fn return_ip(_auth: Authenticated) -> Value { // retrieve ip for wlan0 or set to x.x.x.x if not found let wlan_ip = match network_client::ip("wlan0") { Ok(ip) => ip, @@ -974,7 +974,7 @@ pub fn return_ip(auth: Authenticated) -> Value { } #[get("/api/v1/network/rssi")] -pub fn return_rssi(auth: Authenticated) -> Value { +pub fn return_rssi(_auth: Authenticated) -> Value { // retrieve rssi for connected network match network_client::rssi("wlan0") { Ok(rssi) => { @@ -991,7 +991,7 @@ pub fn return_rssi(auth: Authenticated) -> Value { } #[get("/api/v1/network/ssid")] -pub fn return_ssid(auth: Authenticated) -> Value { +pub fn return_ssid(_auth: Authenticated) -> Value { // retrieve ssid for connected network match network_client::ssid("wlan0") { Ok(network) => { @@ -1008,7 +1008,7 @@ pub fn return_ssid(auth: Authenticated) -> Value { } #[get("/api/v1/network/state")] -pub fn return_state(auth: Authenticated) -> Value { +pub fn return_state(_auth: Authenticated) -> Value { // retrieve state of wlan0 or set to x.x.x.x if not found let wlan_state = match network_client::state("wlan0") { Ok(state) => state, @@ -1028,7 +1028,7 @@ pub fn return_state(auth: Authenticated) -> Value { } #[get("/api/v1/network/status")] -pub fn return_status(auth: Authenticated) -> Value { +pub fn return_status(_auth: Authenticated) -> Value { // retrieve status info for wlan0 interface match network_client::status("wlan0") { Ok(network) => { @@ -1045,7 +1045,7 @@ pub fn return_status(auth: Authenticated) -> Value { } #[get("/api/v1/network/wifi")] -pub fn scan_networks(auth: Authenticated) -> Value { +pub fn scan_networks(_auth: Authenticated) -> Value { // retrieve scan results for access-points within range of wlan0 match network_client::available_networks("wlan0") { Ok(networks) => { diff --git a/peach-web/src/utils.rs b/peach-web/src/utils.rs index ff4c883..8055473 100644 --- a/peach-web/src/utils.rs +++ b/peach-web/src/utils.rs @@ -1,7 +1,7 @@ pub mod monitor; use rocket_dyn_templates::Template; -use rocket::request::Request; + use rocket::response::{Redirect, Responder}; use rocket::serde::json::{Value, json}; use rocket::serde::{Serialize}; -- 2.40.1 From 0d23307f861f4b5052dd9a5a6098e81d8734f1dc Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 17:01:38 +0100 Subject: [PATCH 5/7] Use empty struct for Authenticated --- peach-lib/src/config_manager.rs | 3 +-- peach-lib/src/password_utils.rs | 5 +---- peach-web/src/routes/authentication.rs | 10 ++++++---- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/peach-lib/src/config_manager.rs b/peach-lib/src/config_manager.rs index 5940706..2581408 100644 --- a/peach-lib/src/config_manager.rs +++ b/peach-lib/src/config_manager.rs @@ -4,13 +4,12 @@ //! //! The configuration file is located at: "/var/lib/peachcloud/config.yml" -use fslock::{LockFile, IntoOsString}; +use fslock::{LockFile}; use serde::{Deserialize, Serialize}; use std::fs; use crate::error::PeachError; use crate::error::*; -use crate::password_utils::hash_password; // main configuration file pub const YAML_PATH: &str = "/var/lib/peachcloud/config.yml"; diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index 8612329..28e5e18 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -2,14 +2,11 @@ use crate::config_manager::{get_peachcloud_domain, load_peach_config, set_admin_password_hash, get_admin_password_hash, get_temporary_password_hash, set_temporary_password_hash}; use crate::error::PeachError; -use crate::error::StdIoError; use crate::sbot_client; use log::info; use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; -use snafu::ResultExt; use std::iter; -use std::process::Command; use crypto::digest::Digest; use crypto::sha3::Sha3; @@ -52,7 +49,7 @@ pub fn set_new_password(new_password: &str) -> Result<(), PeachError> { } /// Creates a hash from a password string -pub fn hash_password(password: &String) -> String { +pub fn hash_password(password: &str) -> String { let mut hasher = Sha3::sha3_256(); hasher.input_str(password); hasher.result_str() diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 3e0670a..18639d9 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -24,9 +24,11 @@ use rocket::http::{Cookie, CookieJar, Status}; pub const AUTH_COOKIE_KEY: &str = "peachweb_auth"; pub const ADMIN_USERNAME: &str = "admin"; -pub struct Authenticated { - is_authenticated: bool, -} +/// Note: Currently we use an empty struct for the Authenticated request guard +/// because there is only one user to be authenticated, and no data needs to be stored here. +/// In a multi-user authentication scheme, we would store the user_id in this struct, +/// and retrieve the correct user via the user_id stored in the cookie. +pub struct Authenticated; #[derive(Debug)] pub enum LoginError { @@ -47,7 +49,7 @@ impl<'r> FromRequest<'r> for Authenticated { .cookies() .get_private(AUTH_COOKIE_KEY) .and_then(|cookie| cookie.value().parse().ok()) - .map(|_value: String| { Authenticated { is_authenticated: true } }); + .map(|_value: String| { Authenticated { } }); match authenticated { Some(auth) => { request::Outcome::Success(auth) -- 2.40.1 From 309ec94cdb9b9670059625c51fdb28b142f7c987 Mon Sep 17 00:00:00 2001 From: notplants Date: Mon, 8 Nov 2021 17:03:47 +0100 Subject: [PATCH 6/7] Allow large enum variant to avoid clippy warning --- peach-web/src/utils.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/peach-web/src/utils.rs b/peach-web/src/utils.rs index 8055473..1c31c25 100644 --- a/peach-web/src/utils.rs +++ b/peach-web/src/utils.rs @@ -25,6 +25,7 @@ pub struct FlashContext { /// A helper enum which allows routes to either return a Template or a Redirect /// from: https://github.com/SergioBenitez/Rocket/issues/253#issuecomment-532356066 +#[allow(clippy::large_enum_variant)] #[derive(Debug, Responder)] pub enum TemplateOrRedirect { Template(Template), -- 2.40.1 From e3640f0885db1aa736e2a68fa72be28ef79af45c Mon Sep 17 00:00:00 2001 From: notplants Date: Wed, 10 Nov 2021 12:21:17 +0100 Subject: [PATCH 7/7] Clean up comments --- peach-lib/src/password_utils.rs | 7 +++---- peach-web/src/routes/authentication.rs | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/peach-lib/src/password_utils.rs b/peach-lib/src/password_utils.rs index 28e5e18..047d3be 100644 --- a/peach-lib/src/password_utils.rs +++ b/peach-lib/src/password_utils.rs @@ -34,7 +34,7 @@ pub fn validate_new_passwords(new_password1: &str, new_password2: &str) -> Resul } } -/// Uses htpasswd to set a new password for the admin user +/// Sets a new password for the admin user pub fn set_new_password(new_password: &str) -> Result<(), PeachError> { let new_password_hash = hash_password(&new_password.to_string()); let result = set_admin_password_hash(&new_password_hash); @@ -55,7 +55,7 @@ pub fn hash_password(password: &str) -> String { hasher.result_str() } -/// Uses htpasswd to set a new temporary password for the admin user +/// Sets a new temporary password for the admin user /// which can be used to reset the permanent password pub fn set_new_temporary_password(new_password: &str) -> Result<(), PeachError> { let new_password_hash = hash_password(&new_password.to_string()); @@ -82,7 +82,7 @@ pub fn verify_temporary_password(password: &str) -> Result<(), PeachError> { } } -/// generates a temporary password and sends it via ssb dm +/// Generates a temporary password and sends it via ssb dm /// to the ssb id configured to be the admin of the peachcloud device pub fn send_password_reset() -> Result<(), PeachError> { // first generate a new random password of ascii characters @@ -119,7 +119,6 @@ using this link: http://peach.local/reset_password", msg += &remote_link; // finally send the message to the admins let peach_config = load_peach_config()?; - info!("sending password reset: {}", msg); for ssb_admin_id in peach_config.ssb_admin_ids { sbot_client::private_message(&msg, &ssb_admin_id)?; } diff --git a/peach-web/src/routes/authentication.rs b/peach-web/src/routes/authentication.rs index 18639d9..a55a0c0 100644 --- a/peach-web/src/routes/authentication.rs +++ b/peach-web/src/routes/authentication.rs @@ -35,8 +35,8 @@ pub enum LoginError { UserNotLoggedIn } -/// Request guard which returns an Authenticated struct with is_authenticated=true -/// iff the user has a cookie which proves they are authenticated with peach-web. +/// Request guard which returns an empty Authenticated struct from the request +/// if and only if the user has a cookie which proves they are authenticated with peach-web. /// /// Note that cookies.get_private uses encryption, which means that this private cookie /// cannot be inspected, tampered with, or manufactured by clients. @@ -114,7 +114,6 @@ pub fn verify_login_form(login_form: LoginForm) -> Result<(), PeachError> { #[post("/login", data="")] pub fn login_post(login_form: Form, cookies: &CookieJar<'_>) -> TemplateOrRedirect { - info!("call to login post"); let result = verify_login_form(login_form.into_inner()); match result { Ok(_) => { -- 2.40.1