Implement cookie session handling

This commit is contained in:
lemonsh 2022-04-09 17:30:20 +02:00
parent 98dc77d7ba
commit a7a574be5c
5 changed files with 280 additions and 50 deletions

189
Cargo.lock generated
View file

@ -199,6 +199,16 @@ dependencies = [
"memchr",
]
[[package]]
name = "buf_redux"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f"
dependencies = [
"memchr",
"safemem",
]
[[package]]
name = "bumpalo"
version = "3.9.1"
@ -251,6 +261,7 @@ dependencies = [
"num-integer",
"num-traits",
"serde",
"time",
"winapi",
]
@ -434,6 +445,15 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fastrand"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
dependencies = [
"instant",
]
[[package]]
name = "fnv"
version = "1.0.7"
@ -500,6 +520,7 @@ dependencies = [
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
@ -781,9 +802,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "js-sys"
version = "0.3.56"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
dependencies = [
"wasm-bindgen",
]
@ -796,9 +817,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.121"
version = "0.2.122"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
[[package]]
name = "lock_api"
@ -893,6 +914,24 @@ dependencies = [
"winapi",
]
[[package]]
name = "multipart"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
dependencies = [
"buf_redux",
"httparse",
"log",
"mime",
"mime_guess",
"quick-error",
"rand",
"safemem",
"tempfile",
"twoway",
]
[[package]]
name = "nom"
version = "7.1.1"
@ -1127,13 +1166,19 @@ checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "proc-macro2"
version = "1.0.36"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quick-error"
version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quote"
version = "1.0.17"
@ -1210,6 +1255,15 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "remove_dir_all"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
]
[[package]]
name = "ring"
version = "0.16.20"
@ -1244,6 +1298,12 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "safemem"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
[[package]]
name = "same-file"
version = "1.0.6"
@ -1549,15 +1609,29 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "syn"
version = "1.0.90"
version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "tempfile"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if 1.0.0",
"fastrand",
"libc",
"redox_syscall",
"remove_dir_all",
"winapi",
]
[[package]]
name = "tera"
version = "1.15.0"
@ -1609,6 +1683,16 @@ dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438"
dependencies = [
"libc",
"winapi",
]
[[package]]
name = "tinyvec"
version = "1.5.1"
@ -1630,6 +1714,7 @@ version = "0.1.0"
dependencies = [
"anyhow",
"async-sqlx-session",
"chrono",
"serde",
"sqlx",
"tera",
@ -1638,6 +1723,7 @@ dependencies = [
"tracing",
"tracing-subscriber",
"warp",
"warp-sessions",
]
[[package]]
@ -1692,6 +1778,19 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-tungstenite"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511de3f85caf1c98983545490c3d09685fa8eb634e57eec22bb4db271f46cbd8"
dependencies = [
"futures-util",
"log",
"pin-project",
"tokio",
"tungstenite",
]
[[package]]
name = "tokio-util"
version = "0.6.9"
@ -1800,6 +1899,34 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
name = "tungstenite"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0b2d8558abd2e276b0a8df5c05a2ec762609344191e5fd23e292c910e9165b5"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand",
"sha-1 0.9.8",
"thiserror",
"url",
"utf-8",
]
[[package]]
name = "twoway"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1"
dependencies = [
"memchr",
]
[[package]]
name = "typenum"
version = "1.15.0"
@ -1931,6 +2058,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "utf-8"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "valuable"
version = "0.1.0"
@ -1979,6 +2112,7 @@ dependencies = [
"log",
"mime",
"mime_guess",
"multipart",
"percent-encoding",
"pin-project",
"scoped-tls",
@ -1987,11 +2121,26 @@ dependencies = [
"serde_urlencoded",
"tokio",
"tokio-stream",
"tokio-tungstenite",
"tokio-util 0.6.9",
"tower-service",
"tracing",
]
[[package]]
name = "warp-sessions"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc73864084b8deb701cbd0eab8bf0ca239ee4ebc5e4b3de43f9c29065bf2035c"
dependencies = [
"async-session",
"async-trait",
"http",
"serde",
"tokio",
"warp",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
@ -2006,9 +2155,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
dependencies = [
"cfg-if 1.0.0",
"wasm-bindgen-macro",
@ -2016,9 +2165,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
dependencies = [
"bumpalo",
"lazy_static",
@ -2031,9 +2180,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@ -2041,9 +2190,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
dependencies = [
"proc-macro2",
"quote",
@ -2054,15 +2203,15 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.79"
version = "0.2.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
[[package]]
name = "web-sys"
version = "0.3.56"
version = "0.3.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
checksum = "7b17e741662c70c8bd24ac5c5b18de314a2c26c32bf8346ee1e6f53de919c283"
dependencies = [
"js-sys",
"wasm-bindgen",

View file

@ -3,6 +3,10 @@ name = "tmtd"
version = "0.1.0"
edition = "2021"
[profile.release]
strip = "debuginfo"
lto = true
[dependencies]
tokio = { version = "1", features = ["rt", "sync", "signal", "macros"] }
toml = "0.5"
@ -11,6 +15,8 @@ sqlx = { version = "0.5", default-features = false, features = ["runtime-tokio-r
tracing = "0.1"
tracing-subscriber = "0.3"
warp = { version = "0.3", default-features = false }
warp-sessions = "1.0"
tera = "1.15"
async-sqlx-session = { version = "0.4", default-features = false, features = ["pg"] }
anyhow = "1.0"
chrono = "0.4"

View file

@ -21,9 +21,7 @@ use sqlx::{ConnectOptions, PgPool};
use tracing::info;
use tracing::log::LevelFilter;
pub struct Database {
pool: PgPool,
}
pub struct Database(PgPool);
impl Database {
pub async fn connect(conn_string: &str) -> anyhow::Result<Self> {
@ -40,16 +38,14 @@ impl Database {
"Database connected, PostgreSQL version {}",
pgver.as_deref().unwrap_or("unknown")
);
Ok(Self {
pool
})
Ok(Self(pool))
}
pub fn pool(&self) -> PgPool {
self.pool.clone()
self.0.clone()
}
pub async fn close(&self) {
self.pool.close().await;
self.0.close().await;
}
}

View file

@ -16,16 +16,16 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::web::App;
use crate::{config::Config, database::Database};
use std::{env, sync::Arc};
use async_sqlx_session::PostgresSessionStore;
use std::str::FromStr;
use std::time::Duration;
use async_sqlx_session::PostgresSessionStore;
use std::{env, sync::Arc};
use tokio::sync::broadcast;
use tokio::task::JoinHandle;
use tokio::time::sleep;
use tracing::{debug, error, info, Level};
use crate::web::App;
mod config;
mod database;
@ -71,13 +71,15 @@ async fn main() -> anyhow::Result<()> {
info!(concat!("Initializing - tmtd ", env!("CARGO_PKG_VERSION")));
let (ctx, _) = broadcast::channel(1);
let database = Arc::new(Database::connect(&cfg.connection_string).await?);
let session_store = PostgresSessionStore::from_client(database.pool()).with_table_name("sessions");
let session_store =
PostgresSessionStore::from_client(database.pool()).with_table_name("sessions");
session_store.migrate().await?;
let cleanup_task = spawn_session_cleanup_task(&session_store, Duration::from_secs(600), ctx.subscribe());
let cleanup_task =
spawn_session_cleanup_task(&session_store, Duration::from_secs(600), ctx.subscribe());
info!("Started session cleanup task");
let web_app = App::new(cfg.clone(), database.clone())?;
let web_app = App::new(cfg.clone(), database.clone(), session_store)?;
let web_task = tokio::spawn(web_app.run(ctx.subscribe()));
info!("Started the web app at http://{}", cfg.listen_addr);
@ -95,7 +97,11 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}
fn spawn_session_cleanup_task(store: &PostgresSessionStore, period: Duration, mut cancel: broadcast::Receiver<()>) -> JoinHandle<()> {
fn spawn_session_cleanup_task(
store: &PostgresSessionStore,
period: Duration,
mut cancel: broadcast::Receiver<()>,
) -> JoinHandle<()> {
let store = store.clone();
tokio::spawn(async move {
loop {

View file

@ -16,43 +16,116 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::{Config, Database};
use std::sync::Arc;
use async_sqlx_session::PostgresSessionStore;
use chrono::Duration;
use sqlx::types::chrono::Utc;
use tera::Tera;
use tokio::sync::broadcast;
use warp::{Filter, reply};
use tera::{Context, Tera};
use tracing::info;
use warp::Filter;
use warp_sessions::{CookieOptions, SameSiteCookieOption, SessionWithStore};
use crate::{Config, Database};
macro_rules! warp_try {
($expr:expr) => {
match $expr {
Ok(o) => o,
Err(e) => {
warn!("Error in a request handler: {}", e);
return reply::with_status(
format!("Error: {}", e),
StatusCode::INTERNAL_SERVER_ERROR,
)
.into_response();
}
}
};
($store:expr, $expr:expr) => {
match $expr {
Ok(o) => o,
Err(e) => {
warn!("Error in a request handler: {}", e);
return (
reply::with_status(format!("Error: {}", e), StatusCode::INTERNAL_SERVER_ERROR)
.into_response(),
$store,
);
}
}
};
}
macro_rules! warp_session {
($filter:expr, $store:expr, $then:expr) => {
$filter
.and(
warp_sessions::request::with_session(
$store.clone(),
Some(CookieOptions {
cookie_name: "tmtd_sid",
cookie_value: None,
max_age: Some(86400*7),
domain: None,
path: None,
secure: true,
http_only: true,
same_site: Some(SameSiteCookieOption::Strict),
}),
)
.map(|mut s: SessionWithStore<PostgresSessionStore>| {
s.session.set_expiry(Utc::now() + Duration::days(7));
s
}),
)
.then($then)
.untuple_one()
.and_then(warp_sessions::reply::with_session)
};
}
pub struct App {
config: Arc<Config>,
db: Arc<Database>,
tera: Arc<Tera>
session_store: PostgresSessionStore,
tera: Arc<Tera>,
}
impl App {
pub fn new(config: Arc<Config>, db: Arc<Database>) -> Result<Self, tera::Error> {
pub fn new(
config: Arc<Config>,
db: Arc<Database>,
session_store: PostgresSessionStore,
) -> Result<Self, tera::Error> {
Ok(Self {
config,
db,
tera: Arc::new(Tera::new("templates/*.html")?)
session_store,
tera: Arc::new(Tera::new("templates/*.html")?),
})
}
pub async fn run(self, mut cancel: broadcast::Receiver<()>) {
let hello = warp::path!("hello" / String).map({
let tera = self.tera.clone();
move |name: String| {
let mut ctx = Context::new();
ctx.insert("name", &name);
reply::html(tera.render("hello.html", &ctx).unwrap())
let route = warp_session!(
warp::path::end(),
self.session_store,
move |mut s: SessionWithStore<PostgresSessionStore>| async move {
s.session.insert_raw("test", "something".into());
(
warp::reply::html(format!("session id: {}", s.session.id())),
s,
)
}
});
);
let (_, server) =
warp::serve(hello).bind_with_graceful_shutdown(self.config.listen_addr, async move {
warp::serve(route).bind_with_graceful_shutdown(self.config.listen_addr, async move {
cancel.recv().await.unwrap();
});
server.await;
info!("Web app has been shut down")
info!("Web app has been shut down");
}
}