diff --git a/backend/.sqlx/query-d7e9b69fef8369117ce057d01d87288b39ea7c802007f112eb3d62230d07abb6.json b/backend/.sqlx/query-08c39f2d9e622313e670f869e23538ac8507fc8b1a3427a6b94ffb15fe43a1fa.json similarity index 62% rename from backend/.sqlx/query-d7e9b69fef8369117ce057d01d87288b39ea7c802007f112eb3d62230d07abb6.json rename to backend/.sqlx/query-08c39f2d9e622313e670f869e23538ac8507fc8b1a3427a6b94ffb15fe43a1fa.json index 015aa7b05a..97d9d27cf0 100644 --- a/backend/.sqlx/query-d7e9b69fef8369117ce057d01d87288b39ea7c802007f112eb3d62230d07abb6.json +++ b/backend/.sqlx/query-08c39f2d9e622313e670f869e23538ac8507fc8b1a3427a6b94ffb15fe43a1fa.json @@ -1,12 +1,12 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM token_expiry_notification n\n USING token t\n WHERE n.token = t.token\n AND n.expiration > now()\n AND n.expiration <= now() + interval '7 days'\n RETURNING substring(t.token for 10) as token_prefix, t.label, t.email, t.workspace_id", + "query": "DELETE FROM token_expiry_notification n\n USING token t\n WHERE n.token = t.token_hash\n AND n.expiration > now()\n AND n.expiration <= now() + interval '7 days'\n RETURNING t.token_prefix, t.label, t.email, t.workspace_id", "describe": { "columns": [ { "ordinal": 0, "name": "token_prefix", - "type_info": "Text" + "type_info": "Varchar" }, { "ordinal": 1, @@ -28,11 +28,11 @@ "Left": [] }, "nullable": [ - null, + true, true, true, true ] }, - "hash": "d7e9b69fef8369117ce057d01d87288b39ea7c802007f112eb3d62230d07abb6" + "hash": "08c39f2d9e622313e670f869e23538ac8507fc8b1a3427a6b94ffb15fe43a1fa" } diff --git a/backend/.sqlx/query-bb446cbb20166f274a7ee6e88abaa27e233e60e18b3d35545005eb680701241f.json b/backend/.sqlx/query-104fc7e5433abd7247323c5ef76b85f937776a6b47cd99c648bb4d819d3cfe57.json similarity index 75% rename from backend/.sqlx/query-bb446cbb20166f274a7ee6e88abaa27e233e60e18b3d35545005eb680701241f.json rename to backend/.sqlx/query-104fc7e5433abd7247323c5ef76b85f937776a6b47cd99c648bb4d819d3cfe57.json index 9085383617..a59afbf3ef 100644 --- a/backend/.sqlx/query-bb446cbb20166f274a7ee6e88abaa27e233e60e18b3d35545005eb680701241f.json +++ b/backend/.sqlx/query-104fc7e5433abd7247323c5ef76b85f937776a6b47cd99c648bb4d819d3cfe57.json @@ -1,12 +1,12 @@ { "db_name": "PostgreSQL", - "query": "DELETE FROM token WHERE expiration <= now()\n RETURNING substring(token for 10) as token_prefix, label, email, workspace_id", + "query": "DELETE FROM token WHERE expiration <= now()\n RETURNING token_prefix, label, email, workspace_id", "describe": { "columns": [ { "ordinal": 0, "name": "token_prefix", - "type_info": "Text" + "type_info": "Varchar" }, { "ordinal": 1, @@ -28,11 +28,11 @@ "Left": [] }, "nullable": [ - null, + false, true, true, true ] }, - "hash": "bb446cbb20166f274a7ee6e88abaa27e233e60e18b3d35545005eb680701241f" + "hash": "104fc7e5433abd7247323c5ef76b85f937776a6b47cd99c648bb4d819d3cfe57" } diff --git a/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json b/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json deleted file mode 100644 index b58e3bf8d8..0000000000 --- a/backend/.sqlx/query-1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "WITH email_lookup AS (\n SELECT email FROM token WHERE token = $1\n )\n DELETE FROM token\n WHERE email = (SELECT email FROM email_lookup) AND label = 'session'\n RETURNING email", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "email", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "1bdf186d3b99bbd913cbf95150105470cd5f1d4ddbb147cb8ce46f9d1da5dfaf" -} diff --git a/backend/.sqlx/query-55cf43cb9219b43f8e9f94b23b62846cd0b1ef5f64d20b0d975d0058730f427b.json b/backend/.sqlx/query-55cf43cb9219b43f8e9f94b23b62846cd0b1ef5f64d20b0d975d0058730f427b.json deleted file mode 100644 index e68f25d6ab..0000000000 --- a/backend/.sqlx/query-55cf43cb9219b43f8e9f94b23b62846cd0b1ef5f64d20b0d975d0058730f427b.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT job FROM token WHERE token = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "job", - "type_info": "Uuid" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "55cf43cb9219b43f8e9f94b23b62846cd0b1ef5f64d20b0d975d0058730f427b" -} diff --git a/backend/.sqlx/query-66e0968fe9f757755945a7010153821cf73ace9d6692750ccc4cca37701ed77a.json b/backend/.sqlx/query-66e0968fe9f757755945a7010153821cf73ace9d6692750ccc4cca37701ed77a.json deleted file mode 100644 index 3d63bbcfbf..0000000000 --- a/backend/.sqlx/query-66e0968fe9f757755945a7010153821cf73ace9d6692750ccc4cca37701ed77a.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM token WHERE token = $1", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [] - }, - "hash": "66e0968fe9f757755945a7010153821cf73ace9d6692750ccc4cca37701ed77a" -} diff --git a/backend/.sqlx/query-97e364c703bdcdfb5345810659cbe0477a28b8199ef0b297f9a22c88a43b6b5c.json b/backend/.sqlx/query-97e364c703bdcdfb5345810659cbe0477a28b8199ef0b297f9a22c88a43b6b5c.json deleted file mode 100644 index 34ff650daf..0000000000 --- a/backend/.sqlx/query-97e364c703bdcdfb5345810659cbe0477a28b8199ef0b297f9a22c88a43b6b5c.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "DELETE FROM token WHERE token = $1 RETURNING email", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "email", - "type_info": "Varchar" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - true - ] - }, - "hash": "97e364c703bdcdfb5345810659cbe0477a28b8199ef0b297f9a22c88a43b6b5c" -} diff --git a/backend/.sqlx/query-19a7ebb2e7e8e57b6e7c974da8eb7c6841a5c4ff12ba7c12c73d691c49dd99ed.json b/backend/.sqlx/query-983c21be4341a7ff9eb647041aa3642a89b16701e71d624c6adacb652e231a1a.json similarity index 59% rename from backend/.sqlx/query-19a7ebb2e7e8e57b6e7c974da8eb7c6841a5c4ff12ba7c12c73d691c49dd99ed.json rename to backend/.sqlx/query-983c21be4341a7ff9eb647041aa3642a89b16701e71d624c6adacb652e231a1a.json index 18ad13d90f..959438f582 100644 --- a/backend/.sqlx/query-19a7ebb2e7e8e57b6e7c974da8eb7c6841a5c4ff12ba7c12c73d691c49dd99ed.json +++ b/backend/.sqlx/query-983c21be4341a7ff9eb647041aa3642a89b16701e71d624c6adacb652e231a1a.json @@ -1,6 +1,6 @@ { "db_name": "PostgreSQL", - "query": "SELECT email FROM token WHERE token = $1 AND (expiration > NOW() OR expiration IS NULL)", + "query": "SELECT email FROM token WHERE token_hash = $1 AND (expiration > NOW() OR expiration IS NULL)", "describe": { "columns": [ { @@ -18,5 +18,5 @@ true ] }, - "hash": "19a7ebb2e7e8e57b6e7c974da8eb7c6841a5c4ff12ba7c12c73d691c49dd99ed" + "hash": "983c21be4341a7ff9eb647041aa3642a89b16701e71d624c6adacb652e231a1a" } diff --git a/backend/.sqlx/query-bfff3d8df18db198d6ebba8a049b00147fc8bcd42f3df37ef81b9ded80974bd0.json b/backend/.sqlx/query-bfff3d8df18db198d6ebba8a049b00147fc8bcd42f3df37ef81b9ded80974bd0.json deleted file mode 100644 index 4843d959c1..0000000000 --- a/backend/.sqlx/query-bfff3d8df18db198d6ebba8a049b00147fc8bcd42f3df37ef81b9ded80974bd0.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT true FROM token WHERE token = $1 and expiration IS NOT NULL and expiration > now() + $2::int * '1 sec'::interval", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "?column?", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text", - "Int4" - ] - }, - "nullable": [ - null - ] - }, - "hash": "bfff3d8df18db198d6ebba8a049b00147fc8bcd42f3df37ef81b9ded80974bd0" -} diff --git a/backend/src/monitor.rs b/backend/src/monitor.rs index 156f93b7c1..a430c0b31d 100644 --- a/backend/src/monitor.rs +++ b/backend/src/monitor.rs @@ -945,7 +945,7 @@ pub async fn delete_expired_items(db: &DB) -> () { let expired_tokens_r = sqlx::query_as!( TokenRow, "DELETE FROM token WHERE expiration <= now() - RETURNING concat(token_prefix, '*****')", + RETURNING token_prefix, label, email, workspace_id", ) .fetch_all(db) .await; @@ -1164,15 +1164,17 @@ pub async fn delete_expired_items(db: &DB) -> () { } pub async fn check_expiring_tokens(db: &DB) { - // Find tokens expiring within 7 days that still have a pending notification row + // Find tokens expiring within 7 days that still have a pending notification row. + // The notification table stores token_hash (not plaintext) so the join works + // even after the hash migration makes token.token nullable. let expiring_tokens_r = sqlx::query_as!( TokenRow, "DELETE FROM token_expiry_notification n USING token t - WHERE n.token = t.token + WHERE n.token = t.token_hash AND n.expiration > now() AND n.expiration <= now() + interval '7 days' - RETURNING substring(t.token for 10) as token_prefix, t.label, t.email, t.workspace_id", + RETURNING t.token_prefix, t.label, t.email, t.workspace_id", ) .fetch_all(db) .await; diff --git a/backend/windmill-api-auth/src/auth.rs b/backend/windmill-api-auth/src/auth.rs index 30a6b241b2..d8303ffeee 100644 --- a/backend/windmill-api-auth/src/auth.rs +++ b/backend/windmill-api-auth/src/auth.rs @@ -44,13 +44,14 @@ lazy_static::lazy_static! { /// Get email from a valid token, with caching. /// Used for WM_END_USER_EMAIL when user is authenticated but not a workspace member. async fn get_email_from_token(db: &DB, token: &str) -> Option { - if let Some(cached) = TOKEN_EMAIL_CACHE.get(token) { + let t_hash = hash_token(token); + if let Some(cached) = TOKEN_EMAIL_CACHE.get(&t_hash) { return cached; } let email = sqlx::query_scalar!( - "SELECT email FROM token WHERE token = $1 AND (expiration > NOW() OR expiration IS NULL)", - token + "SELECT email FROM token WHERE token_hash = $1 AND (expiration > NOW() OR expiration IS NULL)", + t_hash ) .fetch_optional(db) .await @@ -58,7 +59,7 @@ async fn get_email_from_token(db: &DB, token: &str) -> Option { .flatten() .flatten(); // email column is nullable, so we get Option> - TOKEN_EMAIL_CACHE.insert(token.to_string(), email.clone()); + TOKEN_EMAIL_CACHE.insert(t_hash, email.clone()); email } diff --git a/backend/windmill-api-auth/src/lib.rs b/backend/windmill-api-auth/src/lib.rs index 50175b8686..38ad683f00 100644 --- a/backend/windmill-api-auth/src/lib.rs +++ b/backend/windmill-api-auth/src/lib.rs @@ -575,7 +575,7 @@ pub async fn create_token_internal( register_token_expiry_notification( &mut *tx, - &token, + &t_hash, token_config.label.as_deref(), token_config.expiration, ) @@ -597,12 +597,14 @@ pub async fn create_token_internal( } /// Insert a pending expiry notification row for user tokens that have an expiration. +/// Stores the token_hash so the join in check_expiring_tokens works even when +/// the plaintext token column is NULL (after hash migration). /// When updating this filter, also update: /// - `is_user_token` in src/monitor.rs /// - `isUserToken` in frontend/src/lib/components/settings/TokensTable.svelte pub async fn register_token_expiry_notification( tx: &mut sqlx::PgConnection, - token: &str, + token_hash: &str, label: Option<&str>, expiration: Option>, ) { @@ -619,7 +621,7 @@ pub async fn register_token_expiry_notification( } if let Err(e) = sqlx::query!( "INSERT INTO token_expiry_notification (token, expiration) VALUES ($1, $2) ON CONFLICT DO NOTHING", - token, + token_hash, expiration, ) .execute(&mut *tx) diff --git a/backend/windmill-api-users/src/users.rs b/backend/windmill-api-users/src/users.rs index 3c5fa40026..22cd96ef90 100644 --- a/backend/windmill-api-users/src/users.rs +++ b/backend/windmill-api-users/src/users.rs @@ -1880,7 +1880,7 @@ async fn impersonate( windmill_api_auth::register_token_expiry_notification( &mut *tx, - &token, + &t_hash, new_token.label.as_deref(), new_token.expiration, )