mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-18 16:02:10 +00:00
invalidate auth cache when logging out (#5869)
* invalidate auth cache when logging out * use proper sqlx migrate add * formatting * pg notify on token, not email
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
-- Remove token invalidation notification trigger
|
||||
|
||||
DROP TRIGGER IF EXISTS token_invalidation_trigger ON token;
|
||||
DROP FUNCTION IF EXISTS notify_token_invalidation();
|
||||
@@ -0,0 +1,17 @@
|
||||
-- Add token invalidation notification trigger
|
||||
|
||||
CREATE OR REPLACE FUNCTION notify_token_invalidation()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
-- Only notify for session token deletions when the invalidation settings are enabled
|
||||
IF OLD.label = 'session' AND OLD.email IS NOT NULL THEN
|
||||
PERFORM pg_notify('notify_token_invalidation', OLD.token);
|
||||
END IF;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
CREATE TRIGGER token_invalidation_trigger
|
||||
AFTER DELETE ON token
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION notify_token_invalidation();
|
||||
+7
-2
@@ -861,6 +861,11 @@ Windmill Community Edition {GIT_VERSION}
|
||||
}
|
||||
};
|
||||
},
|
||||
"notify_token_invalidation" => {
|
||||
let token = n.payload();
|
||||
tracing::info!("Token invalidation detected for token: {}...", &token[..token.len().min(8)]);
|
||||
windmill_api::auth::invalidate_token_from_cache(token);
|
||||
},
|
||||
"notify_global_setting_change" => {
|
||||
tracing::info!("Global setting change detected: {}", n.payload());
|
||||
match n.payload() {
|
||||
@@ -893,7 +898,7 @@ Windmill Community Edition {GIT_VERSION}
|
||||
if let Err(e) = load_tag_per_workspace_workspaces(&db).await {
|
||||
tracing::error!("Error loading default tag per workspace workspaces: {e:#}");
|
||||
}
|
||||
}
|
||||
},
|
||||
SMTP_SETTING => {
|
||||
reload_smtp_config(&db).await;
|
||||
},
|
||||
@@ -1010,7 +1015,6 @@ Windmill Community Edition {GIT_VERSION}
|
||||
tracing::error!(error = %e, "Could not reload critical alert UI setting");
|
||||
}
|
||||
},
|
||||
|
||||
a @_ => {
|
||||
tracing::info!("Unrecognized Global Setting Change Payload: {:?}", a);
|
||||
}
|
||||
@@ -1183,6 +1187,7 @@ async fn listen_pg(url: &str) -> Option<PgListener> {
|
||||
"notify_webhook_change",
|
||||
"notify_workspace_envs_change",
|
||||
"notify_runnable_version_change",
|
||||
"notify_token_invalidation",
|
||||
];
|
||||
|
||||
#[cfg(feature = "http_trigger")]
|
||||
|
||||
@@ -26,6 +26,20 @@ use windmill_common::{
|
||||
users::{COOKIE_NAME, SUPERADMIN_SECRET_EMAIL},
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
// Global auth cache accessible from main.rs for direct invalidation
|
||||
pub static ref AUTH_CACHE: Cache<(String, String), ExpiringAuthCache> = Cache::new(300);
|
||||
}
|
||||
|
||||
// Global function to invalidate a specific token from cache
|
||||
pub fn invalidate_token_from_cache(token: &str) {
|
||||
// Remove all cache entries for this token (across all workspaces)
|
||||
AUTH_CACHE.retain(|(_workspace_id, cached_token), _cached_value| {
|
||||
cached_token != token
|
||||
});
|
||||
tracing::info!("Invalidated token from auth cache: {}...", &token[..token.len().min(8)]);
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExpiringAuthCache {
|
||||
pub authed: ApiAuthed,
|
||||
@@ -33,7 +47,6 @@ pub struct ExpiringAuthCache {
|
||||
}
|
||||
|
||||
pub struct AuthCache {
|
||||
cache: Cache<(String, String), ExpiringAuthCache>,
|
||||
db: DB,
|
||||
superadmin_secret: Option<String>,
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -47,7 +60,6 @@ impl AuthCache {
|
||||
#[cfg(feature = "enterprise")] ext_jwks: Option<Arc<RwLock<ExternalJwks>>>,
|
||||
) -> Self {
|
||||
AuthCache {
|
||||
cache: Cache::new(300),
|
||||
db,
|
||||
superadmin_secret,
|
||||
#[cfg(feature = "enterprise")]
|
||||
@@ -56,7 +68,7 @@ impl AuthCache {
|
||||
}
|
||||
|
||||
pub async fn invalidate(&self, w_id: &str, token: String) {
|
||||
self.cache.remove(&(w_id.to_string(), token));
|
||||
AUTH_CACHE.remove(&(w_id.to_string(), token));
|
||||
}
|
||||
|
||||
pub async fn get_authed(&self, w_id: Option<String>, token: &str) -> Option<ApiAuthed> {
|
||||
@@ -64,7 +76,7 @@ impl AuthCache {
|
||||
w_id.as_ref().unwrap_or(&"".to_string()).to_string(),
|
||||
token.to_string(),
|
||||
);
|
||||
let s = self.cache.get(&key).map(|c| c.to_owned());
|
||||
let s = AUTH_CACHE.get(&key).map(|c| c.to_owned());
|
||||
match s {
|
||||
Some(ExpiringAuthCache { authed, expiry }) if expiry > chrono::Utc::now() => {
|
||||
Some(authed)
|
||||
@@ -86,7 +98,7 @@ impl AuthCache {
|
||||
};
|
||||
|
||||
if let Some((authed, exp)) = authed_and_exp.clone() {
|
||||
self.cache.insert(
|
||||
AUTH_CACHE.insert(
|
||||
key,
|
||||
ExpiringAuthCache {
|
||||
authed: authed.clone(),
|
||||
@@ -123,7 +135,7 @@ impl AuthCache {
|
||||
username_override,
|
||||
};
|
||||
|
||||
self.cache.insert(
|
||||
AUTH_CACHE.insert(
|
||||
key,
|
||||
ExpiringAuthCache {
|
||||
authed: authed.clone(),
|
||||
@@ -317,7 +329,7 @@ impl AuthCache {
|
||||
}
|
||||
};
|
||||
if let Some(authed) = authed_o.as_ref() {
|
||||
self.cache.insert(
|
||||
AUTH_CACHE.insert(
|
||||
key,
|
||||
ExpiringAuthCache {
|
||||
authed: authed.clone(),
|
||||
|
||||
@@ -69,7 +69,7 @@ mod ai;
|
||||
mod apps;
|
||||
pub mod args;
|
||||
mod audit;
|
||||
mod auth;
|
||||
pub mod auth;
|
||||
mod capture;
|
||||
mod concurrency_groups;
|
||||
mod configs;
|
||||
|
||||
Reference in New Issue
Block a user