From 13253b2d6ea82d9e424765eacab9441f5d70bb4e Mon Sep 17 00:00:00 2001 From: Alexander Petric Date: Tue, 4 Mar 2025 20:08:42 +0100 Subject: [PATCH] feat: more controls on setting token duration (#5421) * allow setting max session length * more options for expiration * sqlx * option to invalidate all old sessions on new session * sqlx update script on mac * order * add audit log --------- Co-authored-by: Ruben Fiszel --- ...860292b704788827edfebc467a92486f9e14f.json | 14 +++++++ ...bf080df3f76ac3e6e6373a89c8d46388125d.json} | 4 +- backend/windmill-api/src/users.rs | 39 +++++++++++++++++-- .../src/lib/components/UserSettings.svelte | 13 ++++--- 4 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json rename backend/.sqlx/{query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json => query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json} (75%) diff --git a/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json new file mode 100644 index 0000000000..93ed95dd5e --- /dev/null +++ b/backend/.sqlx/query-7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f.json @@ -0,0 +1,14 @@ +{ + "db_name": "PostgreSQL", + "query": "DELETE FROM token WHERE email = $1 AND label = 'session'", + "describe": { + "columns": [], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [] + }, + "hash": "7db681e86f8332c636d0f29b389860292b704788827edfebc467a92486f9e14f" +} diff --git a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json similarity index 75% rename from backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json rename to backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json index 75f58e98e3..90a2f78d2c 100644 --- a/backend/.sqlx/query-b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27.json +++ b/backend/.sqlx/query-8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + "query": "INSERT INTO token\n (token, email, label, expiration, super_admin)\n VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", "describe": { "columns": [], "parameters": { @@ -14,5 +14,5 @@ }, "nullable": [] }, - "hash": "b05c5f62ef4aa21d33369130cced0e9d7d128727eb58a9be7ae69cbb16bcbb27" + "hash": "8aebd7f7fd1374f1c3d5389e953ebf080df3f76ac3e6e6373a89c8d46388125d" } diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index b2b5848c32..7661f09f69 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -55,7 +55,6 @@ use windmill_common::{ utils::{not_found_if_none, rd_string, require_admin, Pagination, StripPath}, }; use windmill_git_sync::handle_deployment_metadata; -pub const TTL_TOKEN_DB_H: u32 = 72; const COOKIE_PATH: &str = "/"; @@ -1696,6 +1695,11 @@ async fn refresh_token( Ok("token refreshed".to_string()) } +lazy_static::lazy_static! { + static ref MAX_SESSION_VALIDITY_SECONDS: i64 = std::env::var("MAX_SESSION_VALIDITY_SECONDS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(3 * 24 * 60 * 60); + static ref INVALIDATE_OLD_SESSIONS: bool = std::env::var("INVALIDATE_OLD_SESSIONS").ok().unwrap_or_else(|| String::new()).parse::().unwrap_or(false); +} + pub async fn create_session_token<'c>( email: &str, super_admin: bool, @@ -1703,18 +1707,45 @@ pub async fn create_session_token<'c>( cookies: Cookies, ) -> Result { let token = rd_string(32); + + if *INVALIDATE_OLD_SESSIONS { + sqlx::query!( + "DELETE FROM token WHERE email = $1 AND label = 'session'", + email + ) + .execute(&mut **tx) + .await?; + + audit_log( + &mut **tx, + &AuditAuthor { + email: email.to_string(), + username: email.to_string(), + username_override: None, + }, + "users.token.invalidate_old_sessions", + ActionKind::Delete, + &"global", + None, + None, + ) + .instrument(tracing::info_span!("token", email)) + .await?; + } + sqlx::query!( "INSERT INTO token (token, email, label, expiration, super_admin) - VALUES ($1, $2, $3, now() + ($4 || ' hours')::interval, $5)", + VALUES ($1, $2, $3, now() + ($4 || ' seconds')::interval, $5)", token, email, "session", - TTL_TOKEN_DB_H.to_string(), + &MAX_SESSION_VALIDITY_SECONDS.to_string(), super_admin ) .execute(&mut **tx) .await?; + let mut cookie = Cookie::new(COOKIE_NAME, token.clone()); cookie.set_secure(IS_SECURE.read().await.clone()); cookie.set_same_site(Some(tower_cookies::cookie::SameSite::Lax)); @@ -1725,7 +1756,7 @@ pub async fn create_session_token<'c>( } let mut expire: OffsetDateTime = time::OffsetDateTime::now_utc(); - expire += time::Duration::days(3); + expire += time::Duration::seconds(*MAX_SESSION_VALIDITY_SECONDS); cookie.set_expires(expire); cookies.add(cookie); Ok(token) diff --git a/frontend/src/lib/components/UserSettings.svelte b/frontend/src/lib/components/UserSettings.svelte index 6baf6ad222..7abff06238 100644 --- a/frontend/src/lib/components/UserSettings.svelte +++ b/frontend/src/lib/components/UserSettings.svelte @@ -72,8 +72,7 @@ newToken = undefined let date: Date | undefined if (newTokenExpiration) { - date = new Date() - date.setDate(date.getDate() + newTokenExpiration) + date = new Date(new Date().getTime() + newTokenExpiration * 1000) } newToken = await UserService.createToken({ requestBody: { @@ -299,9 +298,13 @@