diff --git a/Cargo.lock b/Cargo.lock index 1faae5b..d9103b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,6 +15,60 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e" +[[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.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd2bc6d3f370b5666245ff421e231cba4353df936e26986d2918e61a8fd6aef6" +dependencies = [ + "aes-soft", + "aesni", + "block-cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0301c9e9c443494d970a07885e8cf3e587bae8356a1d5abd0999068413f7205f" +dependencies = [ + "aead", + "aes", + "block-cipher", + "ghash", + "subtle", +] + +[[package]] +name = "aes-soft" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63dd91889c49327ad7ef3b500fd1109dbd3c509a03db0d4a9ce413b79f575cb6" +dependencies = [ + "block-cipher", + "byteorder", + "opaque-debug 0.3.0", +] + +[[package]] +name = "aesni" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6fe808308bb07d393e2ea47780043ec47683fcf19cf5efc8ca51c50cc8c68a" +dependencies = [ + "block-cipher", + "opaque-debug 0.3.0", +] + [[package]] name = "aho-corasick" version = "0.7.14" @@ -87,6 +141,12 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" +[[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" @@ -118,7 +178,25 @@ dependencies = [ "block-padding", "byte-tools", "byteorder", - "generic-array", + "generic-array 0.12.3", +] + +[[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]] +name = "block-cipher" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80" +dependencies = [ + "generic-array 0.14.4", ] [[package]] @@ -215,11 +293,23 @@ name = "cookie" version = "0.15.0-dev" source = "git+https://github.com/SergioBenitez/cookie-rs.git?rev=1c3ca83#1c3ca838543b60a4448d279dc4b903cc7a2bc22a" dependencies = [ + "aes-gcm", + "base64", + "hkdf", "percent-encoding", + "rand 0.7.3", + "sha2", + "subtle", "time 0.2.22", "version_check", ] +[[package]] +name = "cpuid-bool" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" + [[package]] name = "crc32fast" version = "1.2.1" @@ -255,6 +345,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + [[package]] name = "devise" version = "0.3.0" @@ -291,7 +391,16 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" dependencies = [ - "generic-array", + "generic-array 0.12.3", +] + +[[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]] @@ -550,6 +659,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", +] + [[package]] name = "getrandom" version = "0.1.15" @@ -561,6 +680,15 @@ dependencies = [ "wasi 0.9.0+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6e27f0689a6e15944bdce7e45425efb87eaa8ab0c6e87f11d0987a9133e2531" +dependencies = [ + "polyval", +] + [[package]] name = "gimli" version = "0.22.0" @@ -622,6 +750,26 @@ dependencies = [ "libc", ] +[[package]] +name = "hkdf" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe1149865383e4526a43aee8495f9a325f0b806c63ce6427d06336a590abbbc9" +dependencies = [ + "digest 0.9.0", + "hmac", +] + +[[package]] +name = "hmac" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" +dependencies = [ + "crypto-mac", + "digest 0.9.0", +] + [[package]] name = "http" version = "0.2.1" @@ -958,6 +1106,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 = "parking_lot" version = "0.11.0" @@ -1108,6 +1262,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "polyval" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5884790f1ce3553ad55fec37b5aaac5882e0e845a2612df744d6c85c9bf046c" +dependencies = [ + "cfg-if 0.1.10", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.9" @@ -1575,10 +1739,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]] @@ -1587,6 +1751,19 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" +[[package]] +name = "sha2" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8" +dependencies = [ + "block-buffer 0.9.0", + "cfg-if 1.0.0", + "cpuid-bool", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + [[package]] name = "signal-hook-registry" version = "1.2.2" @@ -1721,6 +1898,12 @@ dependencies = [ "uuid", ] +[[package]] +name = "subtle" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "343f3f510c2915908f155e94f17220b19ccfacf2a64a2a5d8004f2c3e311e7fd" + [[package]] name = "syn" version = "1.0.48" @@ -1938,6 +2121,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +[[package]] +name = "universal-hash" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8326b2c654932e3e4f9196e69d08fdf7cfd718e1dc6f66b347e6024a0c961402" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + [[package]] name = "uuid" version = "0.7.4" diff --git a/Cargo.toml b/Cargo.toml index 36b7f9d..ec1964b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ handlebars = "3" #version = "0.4" git = "https://github.com/SergioBenitez/Rocket" branch = "master" +features = ["secrets"] [dependencies.rocket_contrib] #version = "0.4" diff --git a/Dockerfile b/Dockerfile index 472ec47..4e711fd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,6 +24,7 @@ FROM scratch ENV ROCKET_ENV="staging" ENV ROCKET_TEMPLATE_DIR="/templates" + ENV DB_PATH="/database" VOLUME "/database" diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..8909d2c --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,62 @@ +use crate::routes::pages; +use rocket::{ + catch, + http::{Cookie, CookieJar, Status}, + post, + request::{Form, FromForm, FromRequest, Outcome, Request}, + response::Redirect, + uri, State, +}; +use rocket_contrib::templates::Template; + +pub struct MasterPassword(String); +impl From for MasterPassword { + fn from(pass: String) -> Self { + Self(pass) + } +} + +const AUTH_COOKIE_KEY: &str = "authorized"; +const AUTH_COOKIE_VAL: &str = "true"; + +#[derive(Debug)] +pub struct Authorized; + +#[derive(Debug)] +pub struct Unauthorized; + +#[rocket::async_trait] +impl<'a, 'r> FromRequest<'a, 'r> for Authorized { + type Error = Unauthorized; + + async fn from_request(request: &'a Request<'r>) -> Outcome { + let cookies = request.cookies(); + + match cookies.get_private(AUTH_COOKIE_KEY) { + Some(cookie) if cookie.value() == AUTH_COOKIE_VAL => Outcome::Success(Authorized), + _ => Outcome::Failure((Status::Unauthorized, Unauthorized)), + } + } +} + +#[catch(401)] +pub fn login_page(_req: &Request) -> Template { + Template::render("login", &()) +} + +#[derive(FromForm)] +pub struct Login { + password: String, +} + +#[post("/login", data = "")] +pub fn login( + cookies: &CookieJar, + login: Form, + master_pass: State, +) -> Redirect { + if login.password == master_pass.0 { + cookies.add_private(Cookie::new(AUTH_COOKIE_KEY, AUTH_COOKIE_VAL)); + } + Redirect::to(uri!(pages::index)) +} diff --git a/src/main.rs b/src/main.rs index b53ac1a..7bc08b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,10 @@ +mod auth; mod database; mod handlebars_util; mod routes; mod status_json; +use crate::auth::MasterPassword; use crate::database::migrations::migrate; use crate::database::unversioned::global::schema_version; use crate::database::SCHEMA_VERSION; @@ -18,6 +20,13 @@ async fn main() -> io::Result<()> { let db_path = env::var("DB_PATH").expect("DB_PATH not set"); + let master_pass: MasterPassword = env::var("STL_PASSWORD") + .unwrap_or_else(|_| { + eprintln!(r#"STL_PASSWORD not set, defaulting to "password""#); + "password".into() + }) + .into(); + let mut sled = sled::open(db_path)?; match sled.insert( serialize(schema_version::K).unwrap(), @@ -42,6 +51,7 @@ async fn main() -> io::Result<()> { handlebars_util::register_helpers(engines) })) .manage(sled) + .manage(master_pass) .mount("/static", StaticFiles::from("static")) .mount( "/", @@ -56,8 +66,10 @@ async fn main() -> io::Result<()> { routes::api::end_session, routes::api::bump_session, routes::api::delete_session, + auth::login, ], - ); + ) + .register(rocket::catchers![auth::login_page,]); rocket.launch().await.expect("rocket failed to launch"); diff --git a/src/routes/api.rs b/src/routes/api.rs index 1e920e9..d787b85 100644 --- a/src/routes/api.rs +++ b/src/routes/api.rs @@ -1,3 +1,4 @@ +use crate::auth::Authorized; use crate::database::latest::trees::{categories, sessions}; use crate::routes::pages; use crate::status_json::StatusJson; @@ -18,6 +19,7 @@ pub struct NewCategory { #[post("/create_category", data = "")] pub fn create_category( + _auth: Authorized, category: Form, db: State<'_, sled::Db>, ) -> Result { @@ -39,21 +41,9 @@ pub fn create_category( Ok(Status::Ok.into()) } -#[post("/category//start_session")] -pub fn start_session( - category_uuid: Uuid, - db: State<'_, sled::Db>, -) -> Result { - toggle_category_session(category_uuid, true, db) -} - -#[post("/category//end_session")] -pub fn end_session(category_uuid: Uuid, db: State<'_, sled::Db>) -> Result { - toggle_category_session(category_uuid, false, db) -} - #[post("/category//bump_session/minutes/")] pub fn bump_session( + _auth: Authorized, category_uuid: Uuid, minutes: i64, db: State<'_, sled::Db>, @@ -95,6 +85,24 @@ pub fn bump_session( })??) } +#[post("/category//start_session")] +pub fn start_session( + _auth: Authorized, + category_uuid: Uuid, + db: State<'_, sled::Db>, +) -> Result { + toggle_category_session(category_uuid, true, db) +} + +#[post("/category//end_session")] +pub fn end_session( + _auth: Authorized, + category_uuid: Uuid, + db: State<'_, sled::Db>, +) -> Result { + toggle_category_session(category_uuid, false, db) +} + pub fn toggle_category_session( category_uuid: Uuid, set_active: bool, @@ -156,6 +164,7 @@ pub struct EditSession { #[post("/session//edit", data = "")] pub fn edit_session( + _auth: Authorized, session_uuid: Uuid, session: Form, db: State<'_, sled::Db>, @@ -195,7 +204,11 @@ pub fn edit_session( } #[post("/session//delete")] -pub fn delete_session(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result { +pub fn delete_session( + _auth: Authorized, + session_uuid: Uuid, + db: State<'_, sled::Db>, +) -> Result { let session_uuid_s = sled::IVec::from(serialize(&session_uuid.into_inner())?); let sessions_tree = db.open_tree(sessions::NAME)?; diff --git a/src/routes/pages.rs b/src/routes/pages.rs index 88bf0aa..a189d53 100644 --- a/src/routes/pages.rs +++ b/src/routes/pages.rs @@ -1,3 +1,4 @@ +use crate::auth::Authorized; use crate::database::latest::trees::{categories, sessions}; use crate::status_json::StatusJson; use bincode::deserialize; @@ -12,7 +13,7 @@ use std::collections::{BTreeMap, HashMap}; use std::time::Duration; #[get("/")] -pub fn index(db: State<'_, sled::Db>) -> Result { +pub fn index(_auth: Authorized, db: State<'_, sled::Db>) -> Result { #[derive(Debug, Serialize, Deserialize)] struct TemplateContext { categories: Vec<(uuid::Uuid, categories::V)>, @@ -33,7 +34,11 @@ pub fn index(db: State<'_, sled::Db>) -> Result { } #[get("/session//edit")] -pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result { +pub fn session_edit( + _auth: Authorized, + session_uuid: Uuid, + db: State<'_, sled::Db>, +) -> Result { #[derive(Debug, Serialize, Deserialize)] struct SessionPageContext { session: sessions::V, @@ -57,7 +62,7 @@ pub fn session_edit(session_uuid: Uuid, db: State<'_, sled::Db>) -> Result) -> Result { +pub fn history(_auth: Authorized, db: State<'_, sled::Db>) -> Result { #[derive(Debug, Serialize, Deserialize)] struct HistoryEntryContext { category: categories::V, @@ -108,7 +113,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result { } #[get("/stats")] -pub fn stats(db: State<'_, sled::Db>) -> Result { +pub fn stats(_auth: Authorized, db: State<'_, sled::Db>) -> Result { #[derive(Debug, Serialize, Deserialize)] struct StatsContext { categories_stats: Vec, diff --git a/templates/login.hbs b/templates/login.hbs new file mode 100644 index 0000000..58d3962 --- /dev/null +++ b/templates/login.hbs @@ -0,0 +1,22 @@ + + + + + + + + + + + + stl + + + +

stl

+

Logga in

+
+ +
+ +