From 455bf7b88d1115efff2ea0e4a1944abf7c8edc4d Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Wed, 4 Jun 2025 23:43:41 +0100 Subject: [PATCH] Add redis session store Signed-off-by: Nikolaos Karaolidis --- Cargo.lock | 411 ++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/config.rs | 9 + src/routes/auth.rs | 29 +-- src/state.rs | 93 ++++------ src/utils/authelia.rs | 40 ++++ src/utils/mod.rs | 1 + support/manifest.yaml | 7 + 8 files changed, 507 insertions(+), 85 deletions(-) create mode 100644 src/utils/authelia.rs diff --git a/Cargo.lock b/Cargo.lock index beb8766..9179ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,13 +118,105 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener 2.5.3", + "futures-core", +] + +[[package]] +name = "async-channel" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" +dependencies = [ + "async-channel 2.3.1", + "async-executor", + "async-io", + "async-lock 3.4.0", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +dependencies = [ + "async-lock 3.4.0", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ - "event-listener", + "event-listener 2.5.3", +] + +[[package]] +name = "async-lock" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +dependencies = [ + "event-listener 5.4.0", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-redis-session" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba82ce101e6cde598074604ef4a882bdd6b3a283baff446ae73ae2727c242452" +dependencies = [ + "async-session", + "redis 0.20.2", ] [[package]] @@ -134,7 +226,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07da4ce523b4e2ebaaf330746761df23a465b951a83d84bbce4233dabedae630" dependencies = [ "anyhow", - "async-lock", + "async-lock 2.8.0", "async-trait", "base64 0.13.1", "bincode", @@ -148,6 +240,38 @@ dependencies = [ "sha2 0.9.9", ] +[[package]] +name = "async-std" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "730294c1c08c2e0f85759590518f6333f0d5a0a766a27d519c1b244c3dfd8a24" +dependencies = [ + "async-channel 1.9.0", + "async-global-executor", + "async-io", + "async-lock 3.4.0", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.88" @@ -159,6 +283,12 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -181,7 +311,7 @@ dependencies = [ "http-body-util", "hyper", "hyper-util", - "itoa", + "itoa 1.0.15", "matchit", "memchr", "mime", @@ -356,6 +486,19 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel 2.3.1", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "bumpalo" version = "3.17.0" @@ -473,7 +616,16 @@ dependencies = [ "memchr", "pin-project-lite", "tokio", - "tokio-util", + "tokio-util 0.7.15", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", ] [[package]] @@ -503,6 +655,12 @@ dependencies = [ "libc", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-bigint" version = "0.5.5" @@ -688,6 +846,12 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + [[package]] name = "dyn-clone" version = "1.0.19" @@ -765,12 +929,49 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener 5.4.0", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "ff" version = "0.13.1" @@ -817,6 +1018,25 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + [[package]] name = "futures-sink" version = "0.3.31" @@ -887,11 +1107,24 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "gloo-timers" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "glyph" version = "0.1.0" dependencies = [ "argon2", + "async-redis-session", "async-session", "axum", "axum-extra", @@ -901,7 +1134,8 @@ dependencies = [ "non-empty-string", "openidconnect", "passwords", - "redis", + "redis 0.31.0", + "redis-macros", "serde", "serde_json", "serde_yaml", @@ -943,7 +1177,7 @@ dependencies = [ "http", "httpdate", "mime", - "sha1", + "sha1 0.10.6", ] [[package]] @@ -961,6 +1195,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + [[package]] name = "hex" version = "0.4.3" @@ -1003,7 +1243,7 @@ checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", - "itoa", + "itoa 1.0.15", ] [[package]] @@ -1060,7 +1300,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa", + "itoa 1.0.15", "pin-project-lite", "smallvec", "tokio", @@ -1298,6 +1538,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.15" @@ -1314,6 +1560,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1335,6 +1590,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + [[package]] name = "litemap" version = "0.8.0" @@ -1358,6 +1619,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" dependencies = [ "serde", + "value-bag", ] [[package]] @@ -1622,6 +1884,12 @@ dependencies = [ "sha2 0.10.9", ] +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.4" @@ -1692,6 +1960,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -1713,6 +1992,21 @@ dependencies = [ "spki", ] +[[package]] +name = "polling" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + [[package]] name = "potential_utf" version = "0.1.2" @@ -1921,6 +2215,27 @@ dependencies = [ "random-number", ] +[[package]] +name = "redis" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4f0ceb2ec0dd769483ecd283f6615aa83dcd0be556d5294c6e659caefe7cc54" +dependencies = [ + "async-std", + "async-trait", + "bytes", + "combine", + "dtoa", + "futures-util", + "itoa 0.4.8", + "percent-encoding", + "pin-project-lite", + "sha1 0.6.1", + "tokio", + "tokio-util 0.6.10", + "url", +] + [[package]] name = "redis" version = "0.31.0" @@ -1931,7 +2246,7 @@ dependencies = [ "cfg-if 1.0.0", "combine", "futures-util", - "itoa", + "itoa 1.0.15", "num-bigint", "percent-encoding", "pin-project-lite", @@ -1939,10 +2254,34 @@ dependencies = [ "sha1_smol", "socket2", "tokio", - "tokio-util", + "tokio-util 0.7.15", "url", ] +[[package]] +name = "redis-macros" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35ccf385c8643426649ed7de6ce3ee094a0c3bd772893aa446674496312546d0" +dependencies = [ + "redis 0.31.0", + "redis-macros-derive", + "serde", + "serde_json", +] + +[[package]] +name = "redis-macros-derive" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e7a54acf9e5462a748a4babcfc212c5142b1389103808f1116c9f54ace75c" +dependencies = [ + "proc-macro2", + "quote", + "redis 0.31.0", + "syn 2.0.101", +] + [[package]] name = "redox_syscall" version = "0.5.12" @@ -2058,6 +2397,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "rustls" version = "0.23.27" @@ -2167,7 +2519,7 @@ version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ - "itoa", + "itoa 1.0.15", "memchr", "ryu", "serde", @@ -2179,7 +2531,7 @@ version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" dependencies = [ - "itoa", + "itoa 1.0.15", "serde", ] @@ -2199,7 +2551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa", + "itoa 1.0.15", "ryu", "serde", ] @@ -2241,12 +2593,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" dependencies = [ "indexmap 2.9.0", - "itoa", + "itoa 1.0.15", "ryu", "serde", "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +dependencies = [ + "sha1_smol", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2471,7 +2832,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", - "itoa", + "itoa 1.0.15", "num-conv", "powerfmt", "serde", @@ -2558,6 +2919,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.6.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.15" @@ -2709,6 +3084,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "value-bag" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" + [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index ff50975..51da271 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ codegen-units = 1 [dependencies] argon2 = "0.5.3" +async-redis-session = "0.2.2" async-session = "3.0.0" axum = { version = "0.8.4", features = ["macros"] } axum-extra = { version = "0.10.1", features = ["typed-header"] } @@ -25,6 +26,7 @@ non-empty-string = { version = "0.2.6", features = ["serde"] } openidconnect = { version = "4.0.0", features = ["reqwest"] } passwords = "3.1.16" redis = { version = "0.31.0", features = ["tokio-comp"] } +redis-macros = "0.5.4" serde = "1.0.219" serde_json = "1.0.140" serde_yaml = "0.9.34" diff --git a/src/config.rs b/src/config.rs index 10a2dc8..2acac86 100644 --- a/src/config.rs +++ b/src/config.rs @@ -40,11 +40,20 @@ pub struct AutheliaConfig { pub user_database: PathBuf, } +#[derive(Clone, Deserialize)] +pub struct RedisConfig { + pub host: String, + pub port: u16, + #[serde(default)] + pub database: u8, +} + #[derive(Clone, Deserialize)] pub struct Config { pub server: ServerConfig, pub oauth: OAuthConfig, pub authelia: AutheliaConfig, + pub redis: RedisConfig, } impl Config { diff --git a/src/routes/auth.rs b/src/routes/auth.rs index eb8ea65..b646b89 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,6 +1,7 @@ use std::{borrow::Cow, convert::Infallible}; -use async_session::{MemoryStore, Session, SessionStore}; +use async_redis_session::RedisSessionStore; +use async_session::{Session, SessionStore}; use axum::{ Json, RequestPartsExt, Router, extract::{self, FromRef, FromRequestParts, OptionalFromRequestParts}, @@ -35,7 +36,7 @@ pub struct User { async fn login( extract::State(oauth_client): extract::State, - extract::State(session_store): extract::State, + extract::State(session_store): extract::State, ) -> Result { let (pkce_challenge, pkce_verifier) = PkceCodeChallenge::new_random_sha256(); @@ -89,7 +90,7 @@ fn create_login_session( } async fn create_login_cookie( - session_store: &MemoryStore, + session_store: &RedisSessionStore, session: Session, ) -> Result { let cookie = session_store @@ -127,9 +128,9 @@ struct CallbackParams { async fn callback( extract::Query(params): extract::Query, - extract::State(http_client): extract::State, + extract::State(oauth_http_client): extract::State, extract::State(oauth_client): extract::State, - extract::State(session_store): extract::State, + extract::State(session_store): extract::State, extract::State(config): extract::State, TypedHeader(cookies): TypedHeader, ) -> Result { @@ -155,7 +156,7 @@ async fn callback( })?; let token_response = validate_claims( - &http_client, + &oauth_http_client, &oauth_client, params, csrf_token, @@ -170,7 +171,7 @@ async fn callback( error!("failed to create userinfo request: {e}"); StatusCode::INTERNAL_SERVER_ERROR })? - .request_async(&http_client) + .request_async(&oauth_http_client) .await .map_err(|e| { error!("failed to request user info: {e}"); @@ -207,7 +208,7 @@ fn retrieve_login_session( } async fn validate_claims( - http_client: &reqwest::Client, + oauth_http_client: &reqwest::Client, oauth_client: &OAuthClient, params: CallbackParams, csrf_token: CsrfToken, @@ -226,7 +227,7 @@ async fn validate_claims( StatusCode::INTERNAL_SERVER_ERROR })? .set_pkce_verifier(pkce_verifier) - .request_async(http_client) + .request_async(oauth_http_client) .await .map_err(|e| { error!("failed to request token: {e}"); @@ -313,7 +314,7 @@ fn create_user_session( } async fn create_user_cookie( - session_store: &MemoryStore, + session_store: &RedisSessionStore, session: Session, ) -> Result { let cookie = session_store @@ -343,7 +344,7 @@ async fn create_user_cookie( } async fn logout( - extract::State(session_store): extract::State, + extract::State(session_store): extract::State, TypedHeader(cookies): TypedHeader, ) -> Result { let cookie = cookies.get(COOKIE_NAME).ok_or(StatusCode::UNAUTHORIZED)?; @@ -382,13 +383,13 @@ pub fn routes(state: State) -> Router { impl FromRequestParts for User where - MemoryStore: FromRef, + RedisSessionStore: FromRef, S: Send + Sync, { type Rejection = Response; async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { - let store = MemoryStore::from_ref(state); + let store = RedisSessionStore::from_ref(state); let cookies = parts.extract::>().await.map_err(|e| { if *e.name() == header::COOKIE { @@ -427,7 +428,7 @@ where impl OptionalFromRequestParts for User where - MemoryStore: FromRef, + RedisSessionStore: FromRef, S: Send + Sync, { type Rejection = Infallible; diff --git a/src/state.rs b/src/state.rs index 8bf00b1..3b14043 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,8 +1,7 @@ use std::error::Error; -use async_session::MemoryStore; +use async_redis_session::RedisSessionStore; use axum::extract::FromRef; -use log::error; use openidconnect::{ ClientId, ClientSecret, EmptyAdditionalClaims, EndpointMaybeSet, EndpointNotSet, EndpointSet, IssuerUrl, RedirectUrl, StandardErrorResponse, @@ -13,12 +12,9 @@ use openidconnect::{ }, reqwest, }; -use tokio::{ - spawn, - time::{Duration, sleep}, -}; +use redis::aio::MultiplexedConnection; -use crate::{config::Config, models::authelia}; +use crate::config::Config; pub type OAuthClient< HasAuthUrl = EndpointSet, @@ -52,52 +48,24 @@ pub struct State { pub config: Config, pub oauth_http_client: reqwest::Client, pub oauth_client: OAuthClient, - pub session_store: MemoryStore, + pub redis_client: MultiplexedConnection, + pub session_store: RedisSessionStore, } impl State { pub async fn from_config(config: Config) -> Result> { - let (oauth_http_client, oauth_client) = oauth(&config).await?; - let session_store = session_store(); + let (oauth_http_client, oauth_client) = oauth_client(&config).await?; + let redis_client = redis_client(&config).await?; + let session_store = session_store(&config)?; Ok(Self { config, oauth_http_client, oauth_client, + redis_client, session_store, }) } - - pub fn load_users(&self) -> Result> { - let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; - let users_file: authelia::UsersFile = serde_yaml::from_str(&file_contents)?; - let users = authelia::Users::from(users_file); - Ok(users) - } - - pub fn load_groups(&self) -> Result> { - let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; - let users_file = serde_yaml::from_str::(&file_contents)?; - let groups = authelia::Groups::from(users_file); - Ok(groups) - } - - pub fn load_users_and_groups( - &self, - ) -> Result<(authelia::Users, authelia::Groups), Box> { - let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; - let users_file = serde_yaml::from_str::(&file_contents)?; - let users = authelia::Users::from(users_file.clone()); - let groups = authelia::Groups::from(users_file); - Ok((users, groups)) - } - - pub fn save_users(&self, users: authelia::Users) -> Result<(), Box> { - let users_file = authelia::UsersFile::from(users); - let file_contents = serde_yaml::to_string(&users_file)?; - std::fs::write(&self.config.authelia.user_database, file_contents)?; - Ok(()) - } } impl FromRef for Config { @@ -118,13 +86,19 @@ impl FromRef for OAuthClient { } } -impl FromRef for MemoryStore { +impl FromRef for MultiplexedConnection { + fn from_ref(state: &State) -> Self { + state.redis_client.clone() + } +} + +impl FromRef for RedisSessionStore { fn from_ref(state: &State) -> Self { state.session_store.clone() } } -async fn oauth( +async fn oauth_client( config: &Config, ) -> Result<(reqwest::Client, OAuthClient), Box> { let oauth_http_client = reqwest::ClientBuilder::new() @@ -151,19 +125,26 @@ async fn oauth( Ok((oauth_http_client, oauth_client)) } -fn session_store() -> MemoryStore { - let session_store = MemoryStore::new(); +async fn redis_client( + config: &Config, +) -> Result> { + let url = format!( + "redis://{}:{}/{}", + config.redis.host, config.redis.port, config.redis.database + ); - let session_store_clone = session_store.clone(); - spawn(async move { - loop { - match session_store_clone.cleanup().await { - Ok(()) => {} - Err(e) => error!("Failed to clean up session store: {e}"), - } - sleep(Duration::from_secs(60)).await; - } - }); + let client = redis::Client::open(url)?; + let connection = client.get_multiplexed_async_connection().await?; - session_store + Ok(connection) +} + +fn session_store(config: &Config) -> Result> { + let url = format!( + "redis://{}:{}/{}", + config.redis.host, config.redis.port, config.redis.database + ); + let session_store = RedisSessionStore::new(url)?.with_prefix("session:"); + + Ok(session_store) } diff --git a/src/utils/authelia.rs b/src/utils/authelia.rs new file mode 100644 index 0000000..0022bd7 --- /dev/null +++ b/src/utils/authelia.rs @@ -0,0 +1,40 @@ +use std::error::Error; + +use crate::{models, state::State}; + +impl State { + pub fn load_users(&self) -> Result> { + let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; + let users_file: models::authelia::UsersFile = serde_yaml::from_str(&file_contents)?; + let users = models::authelia::Users::from(users_file); + Ok(users) + } + + pub fn load_groups(&self) -> Result> { + let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; + let users_file = serde_yaml::from_str::(&file_contents)?; + let groups = models::authelia::Groups::from(users_file); + Ok(groups) + } + + pub fn load_users_and_groups( + &self, + ) -> Result<(models::authelia::Users, models::authelia::Groups), Box> + { + let file_contents = std::fs::read_to_string(&self.config.authelia.user_database)?; + let users_file = serde_yaml::from_str::(&file_contents)?; + let users = models::authelia::Users::from(users_file.clone()); + let groups = models::authelia::Groups::from(users_file); + Ok((users, groups)) + } + + pub fn save_users( + &self, + users: models::authelia::Users, + ) -> Result<(), Box> { + let users_file = models::authelia::UsersFile::from(users); + let file_contents = serde_yaml::to_string(&users_file)?; + std::fs::write(&self.config.authelia.user_database, file_contents)?; + Ok(()) + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 274f0ed..b42b1ba 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1 +1,2 @@ +pub mod authelia; pub mod crypto; diff --git a/support/manifest.yaml b/support/manifest.yaml index f2713eb..44d651b 100644 --- a/support/manifest.yaml +++ b/support/manifest.yaml @@ -20,6 +20,9 @@ spec: "/etc/glyph/log4rs.yml", ] + - name: redis + image: docker.io/library/redis:latest + - name: authelia image: docker.io/authelia/authelia:latest volumeMounts: @@ -90,6 +93,10 @@ data: authelia: user_database: /etc/authelia/users/users.yml + redis: + host: redis + port: 6379 + log4rs.yml: | appenders: stdout: