diff --git a/Cargo.lock b/Cargo.lock index 929b28a..45bb96d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + [[package]] name = "argh" version = "0.1.19" @@ -91,12 +97,43 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-extra" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be44683b41ccb9ab2d23a5230015c9c3c55be97a25e4428366de8873103f7970" +dependencies = [ + "axum", + "axum-core", + "bytes", + "cookie", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -115,16 +152,112 @@ version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" +[[package]] +name = "cc" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cpufeatures" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "display_full_error" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7c47e9a2fd28a8edd1446f10dabe8ac5d26bb77ed2b1077bfcd8308904e8c6" + [[package]] name = "dn_escape" version = "0.1.0" +[[package]] +name = "find-msvc-tools" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" + +[[package]] +name = "flate2" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -197,6 +330,12 @@ dependencies = [ "r-efi", ] +[[package]] +name = "glob" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" + [[package]] name = "http" version = "1.5.0" @@ -242,6 +381,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.11.1" @@ -283,6 +431,16 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "jobserver" +version = "0.1.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" +dependencies = [ + "getrandom", + "libc", +] + [[package]] name = "js-sys" version = "0.3.104" @@ -339,11 +497,15 @@ version = "0.1.0" dependencies = [ "argh", "axum", + "axum-extra", "camino", "dn_escape", "futures-util", + "http", "ldap3_proto", + "minijinja", "serde", + "static-serve", "tokio", "tokio-util", "tower", @@ -370,18 +532,54 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "memo-map" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5449c8c750f1a07ea702bbd212bd999fceece9b3d1508b17023b3e174583124b" + [[package]] name = "mime" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minijinja" +version = "2.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86886cf6dbf4e614b19c9a1eec9775f021869d7eadde0fc73921a81b90c9b4c9" +dependencies = [ + "memo-map", + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "mio" version = "1.2.2" @@ -412,6 +610,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + [[package]] name = "once_cell" version = "1.21.4" @@ -457,6 +661,18 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" +[[package]] +name = "pkg-config" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "proc-macro2" version = "1.0.107" @@ -481,6 +697,18 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" +[[package]] +name = "range-requests" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e30cea6780132baa8257e81ab3f6e11526a9271e9032963eab8d830972e68b6" +dependencies = [ + "axum-core", + "bytes", + "http", + "thiserror", +] + [[package]] name = "rustversion" version = "1.0.23" @@ -559,6 +787,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -568,6 +807,18 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "simd-adler32" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" + [[package]] name = "slab" version = "0.4.12" @@ -590,6 +841,36 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "static-serve" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49c7a4b3dbe44375f7e30895c04d3ca4fde020eb7336c0f6a71ed495c5302e1a" +dependencies = [ + "axum", + "bytes", + "range-requests", + "static-serve-macro", +] + +[[package]] +name = "static-serve-macro" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cb887d817cf39e80fd7fdd21c4f138c8277d7dc2623fee4d07e141f4ce32a0a" +dependencies = [ + "display_full_error", + "flate2", + "glob", + "mime_guess", + "proc-macro2", + "quote", + "sha2", + "syn 3.0.4", + "thiserror", + "zstd", +] + [[package]] name = "syn" version = "2.0.119" @@ -647,6 +928,36 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "time" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tokio" version = "1.53.1" @@ -772,6 +1083,18 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" @@ -795,6 +1118,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" @@ -861,8 +1190,42 @@ dependencies = [ "windows-link", ] +[[package]] +name = "zlib-rs" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12" + [[package]] name = "zmij" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 4b0be25..9ce4c55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,11 +6,15 @@ edition = "2024" [dependencies] argh = "0.1.19" axum = { version = "0.8.9", optional = true } +axum-extra = { version = "0.12.6", features = ["cookie"], optional = true } camino = "1.2.5" dn_escape = { path = "vendor/dn_escape" } futures-util = { version = "0.3.34", features = ["sink"] } +http = { version = "1.5.0", optional = true } ldap3_proto = "0.8.1" +minijinja = { version = "2.24.0", optional = true } serde = { version = "1.0.229", features = ["derive"] } +static-serve = { version = "0.6.3", optional = true } tokio = { version = "1.53.1", features = ["macros", "net", "rt", "time", "sync"] } tokio-util = { version = "0.7.19", features = ["codec"] } tower = "0.5.3" @@ -27,4 +31,4 @@ style = "deny" [features] default = [ "http" ] -http = ["dep:axum"] +http = ["dep:axum", "dep:axum-extra", "dep:http", "dep:minijinja", "dep:static-serve"] diff --git a/assets/css/main.css b/assets/css/main.css new file mode 100644 index 0000000..5660244 --- /dev/null +++ b/assets/css/main.css @@ -0,0 +1,81 @@ +body { + margin: 0; +} + +#logo { + display: block; + max-height: 5rem; + max-width: 5rem; + height: auto; + width: auto; + margin: 0 auto; +} + +#center { + width: 100vw; + height: 100vh; + display: flex; + justify-content: center; + align-items: space-around; + flex-direction: column; + max-width: 250px; + margin: auto; +} + +#login-form { + max-width: 200px; + margin: 0 auto; +} + +#login-line { + margin-top: 1rem; + display: flex; + justify-content: center; + font-size: 2rem; + text-align: center; + height: 2.5rem; + gap: 0.5rem; +} + +#login-line > img { + width: 1.75rem; + height: auto; +} + +#login-line > input { + border-color: rgba(31, 41, 55, 0.2); + border-radius: 8px; + padding: 0.5rem 1rem; +} + +#submit-login { + width: 100%; + background-color: rgb(87, 13, 248); + cursor: pointer; + box-sizing: border-box; + border: none; + color: rgb(240, 240, 240); + font-weight: bolder; + text-transform: uppercase; + margin: auto; +} + +.banner { + margin: 1rem auto; + width: 100%; + border-radius: 5px; +} + +.banner > p { + text-align: center; +} + +.is-success { + background-color: #28cb28; + color: #efefef; +} + +.is-error { + background-color: #d42929; + color: #efefef; +} diff --git a/assets/img/logo.png b/assets/img/logo.png new file mode 100644 index 0000000..9278cb6 Binary files /dev/null and b/assets/img/logo.png differ diff --git a/assets/img/password.svg b/assets/img/password.svg new file mode 100644 index 0000000..db7eb11 --- /dev/null +++ b/assets/img/password.svg @@ -0,0 +1 @@ + diff --git a/assets/img/user.svg b/assets/img/user.svg new file mode 100644 index 0000000..74ce0d9 --- /dev/null +++ b/assets/img/user.svg @@ -0,0 +1 @@ + diff --git a/src/http/login.rs b/src/http/login.rs new file mode 100644 index 0000000..2ffd75f --- /dev/null +++ b/src/http/login.rs @@ -0,0 +1,52 @@ +use axum::extract::{Form, State}; +use axum::response::{Html, IntoResponse, Redirect, Response}; +use axum_extra::extract::cookie::CookieJar; +use http::StatusCode; +use minijinja::context; +use serde::{Deserialize, Serialize}; + +use crate::db::DatabaseInterface; +use crate::http::HttpState; + +#[derive(Debug, Deserialize)] +pub struct LoginForm { + username: String, + password: String, +} + +#[derive(Debug, Serialize)] +pub enum LoginError { + InvalidCredentials, + SessionInvalidated, +} + +pub async fn login_page( + State(state): State>, + login_error: Option, +) -> (StatusCode, Html) { + let page = state + .templates + .get_template("login.html") + .unwrap() + .render(context! {login_error => login_error}) + .unwrap(); + (StatusCode::OK, Html(page)) +} + +pub async fn post_login( + State(state): State>, + cookies: CookieJar, + Form(form): Form, +) -> Response { + if let Some(_session) = state.sessions.get_session(&cookies) { + // Already logged in + (cookies, Redirect::to("/")).into_response() + } else if form.username == "admin" && form.password == "adminadmin" { + let cookies = state.sessions.add_session("admin", true, cookies); + (cookies, Redirect::to("/")).into_response() + } else { + login_page(State(state), Some(LoginError::InvalidCredentials)) + .await + .into_response() + } +} diff --git a/src/http/logout.rs b/src/http/logout.rs new file mode 100644 index 0000000..5e38309 --- /dev/null +++ b/src/http/logout.rs @@ -0,0 +1,23 @@ +use axum::extract::State; +use axum::response::{IntoResponse, Redirect, Response}; +use axum_extra::extract::cookie::CookieJar; + +use crate::db::DatabaseInterface; +use crate::http::HttpState; +use crate::http::login::{LoginError, login_page}; + +pub async fn logout( + State(state): State>, + cookies: CookieJar, +) -> Response { + let Some(session) = state.sessions.get_session(&cookies) else { + return (cookies, Redirect::to("/")).into_response(); + }; + + let cookies = state.sessions.remove_session(&session, cookies); + ( + cookies, + login_page(State(state), Some(LoginError::SessionInvalidated)).await, + ) + .into_response() +} diff --git a/src/http/mod.rs b/src/http/mod.rs index cba0164..2f6070c 100644 --- a/src/http/mod.rs +++ b/src/http/mod.rs @@ -1,11 +1,22 @@ use axum::Router; -use axum::routing::get; +use axum::extract::State; +use axum::response::Html; +use axum::routing::{get, post}; use axum::serve::Listener as AxumListener; +use axum_extra::extract::cookie::CookieJar; +use http::StatusCode; +use minijinja::{Environment, context, path_loader}; +use static_serve::embed_assets; use crate::db::{Database, DatabaseInterface}; use crate::listener::{Listener, ListenerKind}; use crate::stream::{AbstractSocketAddr, AbstractStreamKind}; +mod login; +mod logout; +mod session; +use session::HttpSessionManager; + impl AxumListener for Listener { type Io = AbstractStreamKind; type Addr = AbstractSocketAddr; @@ -46,12 +57,51 @@ impl AxumListener for Listener { } } +#[derive(Clone)] +pub struct HttpState { + pub db: Database, + pub sessions: HttpSessionManager, + pub templates: Environment<'static>, +} + +impl HttpState { + pub fn new(db: Database) -> Self { + let mut templates = Environment::new(); + templates.set_loader(path_loader("templates")); + Self { + db, + sessions: HttpSessionManager::new(), + templates, + } + } +} + pub async fn http_listen(listener: Listener, db: Database) { - let app = Router::new().route("/", get(handler)).with_state(db); + embed_assets!("assets"); + let app = Router::new() + .nest("/assets", static_router()) + .route("/", get(home)) + .route("/login", get(home)) + .route("/login", post(login::post_login)) + .route("/logout", get(logout::logout)) + .with_state(HttpState::new(db)); axum::serve(listener, app).await.unwrap(); } -pub async fn handler() -> &'static str { - "hello world" +pub async fn home( + State(state): State>, + cookies: CookieJar, +) -> (StatusCode, Html) { + if let Some(session) = state.sessions.get_session(&cookies) { + let page = state + .templates + .get_template("home.html") + .unwrap() + .render(context! {username => session.username}) + .unwrap(); + (StatusCode::OK, Html(page)) + } else { + login::login_page(State(state), None).await + } } diff --git a/src/http/session.rs b/src/http/session.rs new file mode 100644 index 0000000..f1e951c --- /dev/null +++ b/src/http/session.rs @@ -0,0 +1,75 @@ +use axum_extra::extract::cookie::{Cookie, CookieJar}; +use uuid::Uuid; + +use std::sync::{Arc, RwLock}; + +pub const COOKIE_NAME: &str = "lldap_session"; + +#[derive(Clone, Debug)] +pub struct HttpSessionManager { + inner: Arc>>, +} + +impl HttpSessionManager { + pub fn new() -> Self { + Self { + inner: Arc::new(RwLock::new(vec![])), + } + } + + pub fn add_session(&self, username: &str, is_admin: bool, cookies: CookieJar) -> CookieJar { + let uuid = Uuid::new_v4(); + + let mut cookie = Cookie::new(COOKIE_NAME, uuid.to_string()); + cookie.set_path("/"); + + let session = HttpSession { + username: username.to_string(), + is_admin, + uuid, + }; + + { + self.inner.write().unwrap().push(session); + } + + cookies.add(cookie) + } + + pub fn get_session(&self, cookies: &CookieJar) -> Option { + let cookie = cookies.get(COOKIE_NAME)?; + let previous_uuid = Uuid::parse_str(cookie.value()).ok()?; + + self.inner + .read() + .unwrap() + .iter() + .find(|session| session.uuid == previous_uuid) + .cloned() + } + + pub fn remove_session(&self, session: &HttpSession, cookies: CookieJar) -> CookieJar { + let mut cookie = Cookie::new(COOKIE_NAME, String::new()); + cookie.set_path("/"); + + let idx = { + let Some(idx) = self.inner.read().unwrap().iter().position(|s| s == session) else { + return cookies; + }; + idx + }; + + { + self.inner.write().unwrap().remove(idx); + } + + cookies.remove(cookie) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct HttpSession { + pub username: String, + pub is_admin: bool, + pub uuid: Uuid, +} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..b92b6da --- /dev/null +++ b/templates/base.html @@ -0,0 +1,11 @@ + + + + LLLDAP + + + +{% block main %} +{% endblock %} + + diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..a142aee --- /dev/null +++ b/templates/home.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% block main %} + + + You are logged in as {{ username }} + + Logout + + +{% endblock %} + diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..11ae59a --- /dev/null +++ b/templates/login.html @@ -0,0 +1,29 @@ +{% extends 'base.html' %} +{% block main %} + + + {% if login_error == "InvalidCredentials" %} + + Bad login/password + + {% elif login_error == "SessionInvalidated" %} + + Logout successful + + {% endif %} + + + + + + + + + + + + + + +{% endblock %} +
You are logged in as {{ username }}
Bad login/password
Logout successful