From 5cd6d6f424c28291d6253fb42c8bb455b8db37ba Mon Sep 17 00:00:00 2001 From: didier amyot Date: Sun, 26 Feb 2023 11:01:55 -0500 Subject: [PATCH] Continuing with the login. --- Cargo.lock | 143 ++++++++++++++++++++++++++++++++- Cargo.toml | 3 + resources/templates/index.html | 6 +- src/webui/index_views.rs | 87 ++++++++++++++++++++ src/webui/mod.rs | 7 +- src/webui/views.rs | 31 ------- 6 files changed, 241 insertions(+), 36 deletions(-) create mode 100644 src/webui/index_views.rs diff --git a/Cargo.lock b/Cargo.lock index 9991f62..cb98961 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,7 +53,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash 0.7.6", - "base64", + "base64 0.21.0", "bitflags", "brotli", "bytes", @@ -268,6 +268,41 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "aead" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c192eb8f11fc081b0fe4259ba5af04217d4e0faddd02417310a927911abd7c8" +dependencies = [ + "crypto-common", + "generic-array", +] + +[[package]] +name = "aes" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aes-gcm" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82e1366e0c69c9f927b1fa5ce2c7bf9eafc8f9268c0b9800729e8b267612447c" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "ahash" version = "0.7.6" @@ -392,6 +427,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" + [[package]] name = "base64" version = "0.21.0" @@ -482,6 +523,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cipher" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1873270f8f7942c191139cb8a40fd228da6c3fd2fc376d7e92d47aa14aeb59e" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "clap" version = "4.0.26" @@ -561,7 +612,14 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ + "aes-gcm", + "base64 0.20.0", + "hkdf", + "hmac", "percent-encoding", + "rand", + "sha2", + "subtle", "time", "version_check", ] @@ -600,6 +658,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core", "typenum", ] @@ -625,6 +684,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctr" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" +dependencies = [ + "cipher", +] + [[package]] name = "derive_more" version = "0.99.17" @@ -646,6 +714,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -675,6 +744,7 @@ dependencies = [ "async-channel", "clap", "clap-verbosity-flag", + "cookie", "fraction", "futures", "hex", @@ -887,6 +957,16 @@ dependencies = [ "wasi", ] +[[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + [[package]] name = "h2" version = "0.3.15" @@ -960,6 +1040,24 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.4" @@ -1044,6 +1142,15 @@ dependencies = [ "libc", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "instant" version = "0.1.12" @@ -1383,6 +1490,12 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + [[package]] name = "os_str_bytes" version = "6.4.0" @@ -1442,6 +1555,18 @@ version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +[[package]] +name = "polyval" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +dependencies = [ + "cfg-if", + "cpufeatures", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1801,6 +1926,12 @@ dependencies = [ "syn", ] +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + [[package]] name = "syn" version = "1.0.103" @@ -2024,6 +2155,16 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "universal-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d3160b73c9a19f7e2939a2fdad446c57c1bbbbf4d919d3213ff1267a580d8b5" +dependencies = [ + "crypto-common", + "subtle", +] + [[package]] name = "url" version = "2.3.1" diff --git a/Cargo.toml b/Cargo.toml index da5930e..4826441 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,6 @@ async-channel = "1.8.0" [dependencies.apache-avro] version = "0.14.0" features = ["zstandard"] + +[dependencies.cookie] +features = ["secure", "percent-encode"] \ No newline at end of file diff --git a/resources/templates/index.html b/resources/templates/index.html index bc08809..e5817a9 100644 --- a/resources/templates/index.html +++ b/resources/templates/index.html @@ -10,6 +10,10 @@
- {% include "partials/index/form.html" %} + {% if is_logged_in %} + {% include "partials/index/success.html" %} + {% else %} + {% include "partials/index/form.html" %} + {% endif %}
{% endblock %} \ No newline at end of file diff --git a/src/webui/index_views.rs b/src/webui/index_views.rs new file mode 100644 index 0000000..00a9507 --- /dev/null +++ b/src/webui/index_views.rs @@ -0,0 +1,87 @@ +use std::time::Instant; +use actix_web::{HttpRequest, Responder, web}; +use cookie::{CookieJar, Cookie, Key}; +use cookie::time::macros::datetime; +use cookie::time::OffsetDateTime; + +use minijinja::context; +use crate::webui::MiniJinjaRenderer; +use serde::Deserialize; +use crate::db::external_id::UserId; + +struct SessionService { + key: Key, + +} + +impl SessionService { + fn new() -> SessionService{ + let super_secret_not_hardcoded_at_all: [u8; 32] = [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]; + SessionService{key: Key::derive_from(&super_secret_not_hardcoded_at_all)} + } + + fn create_login_cookie(&self, user_id: &String ) -> Cookie{ + let mut jar = CookieJar::new(); + jar.private_mut(& self.key).add(Cookie::new("ledger-user-id", user_id.clone())); + jar.get("ledger-user-id").unwrap_or(& Cookie::new("ledger-user-id", "")).clone() + } + + fn _decrypt_cookie(&self, cookie: Cookie<'static>) -> Option{ + let mut jar = CookieJar::new(); + jar.private(& self.key).decrypt(cookie) + } + + fn get_login(&self, req: &HttpRequest) -> Option { + req.cookie("ledger-user-id").and_then(|c|self._decrypt_cookie(c)).map(|c| c.value().to_string()) + } + + fn logout(&self) -> Cookie{ + let mut cookie = Cookie::new("ledger-user-id", ""); + let epoch = OffsetDateTime::from_unix_timestamp_nanos(0).unwrap(); + cookie.set_expires(epoch); + cookie + } +} + +#[derive(Deserialize, Debug)] +struct LoginPayload { + username: String, + password: String, +} + +#[actix_web::post("/login")] +pub async fn login( + tmpl_env: MiniJinjaRenderer, + web::Json(form): web::Json, +) -> actix_web::Result { + let ctx = context! { + name => form.username, + }; + let mut ret = tmpl_env.render("partials/index/success.html", ctx)?.customize(); + let s = SessionService::new(); + let cookie = s.create_login_cookie(& form.username).to_string(); + Ok(ret.insert_header(("Set-Cookie", cookie))) +} + +#[actix_web::post("/logout")] +pub async fn logout(tmpl_env: MiniJinjaRenderer) -> actix_web::Result { + let ctx = context! {}; + let s = SessionService::new(); + let res = tmpl_env.render("partials/index/form.html", ctx)?; + Ok(res + .customize() + .insert_header(("Set-Cookie", s.logout().to_string()))) +} + +#[actix_web::get("/")] +pub async fn index(req: HttpRequest, + tmpl_env: MiniJinjaRenderer) -> actix_web::Result { + let s = SessionService::new(); + let userid = & s.get_login(& req); + let username = userid.clone().unwrap_or("".to_string()); + let ctx = context!{ + is_logged_in => userid.is_some(), + name => username, + }; + tmpl_env.render("index.html", ctx) +} \ No newline at end of file diff --git a/src/webui/mod.rs b/src/webui/mod.rs index 84503b8..d448044 100644 --- a/src/webui/mod.rs +++ b/src/webui/mod.rs @@ -23,6 +23,7 @@ use crate::webui::worker::{bob, event_processor_loop}; mod views; mod worker; +mod index_views; struct MiniJinjaRenderer { @@ -97,9 +98,9 @@ pub async fn web_ui_main( .service( actix_files::Files::new("/static", "./resources/static/").use_last_modified(true), ) - .service(views::index) - .service(views::login) - .service(views::logout) + .service(index_views::index) + .service(index_views::login) + .service(index_views::logout) .service(views::actions) .service(views::overview) .service(views::overview_submit_button) diff --git a/src/webui/views.rs b/src/webui/views.rs index 07fabcf..3288c1f 100644 --- a/src/webui/views.rs +++ b/src/webui/views.rs @@ -12,11 +12,6 @@ use crate::services::ServiceFactory; use crate::util::SQLiteConnectionFactory; use crate::webui::MiniJinjaRenderer; -#[actix_web::get("/")] -pub async fn index(tmpl_env: MiniJinjaRenderer) -> actix_web::Result { - tmpl_env.render("index.html", ()) -} - #[actix_web::get("/actions")] pub async fn actions(tmpl_env: MiniJinjaRenderer) -> actix_web::Result { tmpl_env.render("actions.html", ()) @@ -37,32 +32,6 @@ pub async fn overview( tmpl_env.render("overview.html", ctx) } -#[derive(Deserialize, Debug)] -struct LoginPayload { - username: String, - password: String, -} - -#[actix_web::post("/login")] -pub async fn login( - tmpl_env: MiniJinjaRenderer, - web::Json(form): web::Json, -) -> actix_web::Result { - let ctx = context! { - name => form.username, - }; - tmpl_env.render("partials/index/success.html", ctx) -} - -#[actix_web::post("/logout")] -pub async fn logout(tmpl_env: MiniJinjaRenderer) -> actix_web::Result { - let ctx = context! {}; - let res = tmpl_env.render("partials/index/form.html", ctx)?; - Ok(res - .customize() - .insert_header(("Set-Cookie", "username=nobody"))) -} - #[actix_web::get("/partials/overview/submit_button")] pub async fn overview_submit_button( _req: HttpRequest,