diff --git a/backend/ee-repo-ref.txt b/backend/ee-repo-ref.txt index c4b585b87a..57d4069a3b 100644 --- a/backend/ee-repo-ref.txt +++ b/backend/ee-repo-ref.txt @@ -1 +1 @@ -75e2aa1b2a8cff3cdcd64bab33eadab036e0c3fe +ba9196530fafbfe017d2592af2830a60b6fa62a0 \ No newline at end of file diff --git a/backend/windmill-api/src/ee.rs b/backend/windmill-api/src/ee.rs index 7e80797eb8..b20521e8b4 100644 --- a/backend/windmill-api/src/ee.rs +++ b/backend/windmill-api/src/ee.rs @@ -1,4 +1,8 @@ use anyhow::anyhow; +#[cfg(feature = "enterprise")] +use std::sync::Arc; +#[cfg(feature = "enterprise")] +use tokio::sync::RwLock; pub async fn validate_license_key(_license_key: String) -> anyhow::Result { // Implementation is not open source @@ -9,8 +13,20 @@ pub async fn validate_license_key(_license_key: String) -> anyhow::Result, _token: &str, -) -> Option<(crate::db::ApiAuthed, usize)> { + _external_jwks: Option>>, +) -> anyhow::Result<(crate::db::ApiAuthed, usize)> { // Implementation is not open source - None + Err(anyhow!("External JWT auth is not open source")) +} + +#[cfg(feature = "enterprise")] +pub struct ExternalJwks; + +#[cfg(feature = "enterprise")] +impl ExternalJwks { + pub async fn load() -> Option>> { + // Implementation is not open source + None + } } diff --git a/backend/windmill-api/src/lib.rs b/backend/windmill-api/src/lib.rs index 212738c7e2..f0ea523fc1 100644 --- a/backend/windmill-api/src/lib.rs +++ b/backend/windmill-api/src/lib.rs @@ -7,6 +7,8 @@ */ use crate::db::ApiAuthed; +#[cfg(feature = "enterprise")] +use crate::ee::ExternalJwks; #[cfg(feature = "embedding")] use crate::embeddings::load_embeddings_db; use crate::oauth2_ee::AllClients; @@ -164,9 +166,13 @@ pub async fn run_server( } let user_db = UserDB::new(db.clone()); + #[cfg(feature = "enterprise")] + let ext_jwks = ExternalJwks::load().await; let auth_cache = Arc::new(users::AuthCache::new( db.clone(), std::env::var("SUPERADMIN_SECRET").ok(), + #[cfg(feature = "enterprise")] + ext_jwks, )); let argon2 = Arc::new(Argon2::default()); diff --git a/backend/windmill-api/src/users.rs b/backend/windmill-api/src/users.rs index 8e6e48dea7..523d09fb94 100644 --- a/backend/windmill-api/src/users.rs +++ b/backend/windmill-api/src/users.rs @@ -12,6 +12,8 @@ use std::sync::Arc; use crate::db::ApiAuthed; +#[cfg(feature = "enterprise")] +use crate::ee::ExternalJwks; use crate::oauth2_ee::{check_nb_of_user, InstanceEvent}; use crate::utils::{ generate_instance_wide_unique_username, get_and_delete_pending_username_or_generate, @@ -39,6 +41,8 @@ use regex::Regex; use serde::{Deserialize, Serialize}; use sqlx::FromRow; use time::OffsetDateTime; +#[cfg(feature = "enterprise")] +use tokio::sync::RwLock; use tower_cookies::{Cookie, Cookies}; use tracing::{Instrument, Span}; use windmill_audit::audit_ee::{audit_log, AuditAuthor}; @@ -145,11 +149,23 @@ pub struct AuthCache { cache: Cache<(String, String), ExpiringAuthCache>, db: DB, superadmin_secret: Option, + #[cfg(feature = "enterprise")] + ext_jwks: Option>>, } impl AuthCache { - pub fn new(db: DB, superadmin_secret: Option) -> Self { - AuthCache { cache: Cache::new(300), db, superadmin_secret } + pub fn new( + db: DB, + superadmin_secret: Option, + #[cfg(feature = "enterprise")] ext_jwks: Option>>, + ) -> Self { + AuthCache { + cache: Cache::new(300), + db, + superadmin_secret, + #[cfg(feature = "enterprise")] + ext_jwks, + } } pub async fn invalidate(&self, w_id: &str, token: String) { @@ -168,9 +184,19 @@ impl AuthCache { } #[cfg(feature = "enterprise")] _ if token.starts_with("jwt_ext_") => { - let authed_and_exp = - crate::ee::jwt_ext_auth(w_id.as_ref(), token.trim_start_matches("jwt_ext_")) - .await; + let authed_and_exp = match crate::ee::jwt_ext_auth( + w_id.as_ref(), + token.trim_start_matches("jwt_ext_"), + self.ext_jwks.clone(), + ) + .await + { + Ok(r) => Some(r), + Err(e) => { + tracing::error!("JWT_EXT auth error: {:?}", e); + None + } + }; if let Some((authed, exp)) = authed_and_exp.clone() { self.cache.insert(