mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 16:03:47 +00:00
feat: add LOGIN_DOMAIN env var to normalize emails during external login
Add LOGIN_DOMAIN environment variable that appends a domain to emails
missing one during external login (OAuth/SAML/SCIM). When set, emails
without '@' will have '@{LOGIN_DOMAIN}' appended.
Example: LOGIN_DOMAIN=example.com transforms "john" to "john@example.com"
Also includes a migration to lowercase existing emails in critical tables:
- password (primary user identity)
- usr (workspace users)
- email_to_igroup (instance group memberships)
- token (active sessions)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
2002e99a43
commit
89210fc736
@@ -0,0 +1,3 @@
|
||||
-- This migration cannot be reversed as the original email casing is not preserved
|
||||
-- Emails will remain lowercased
|
||||
SELECT 1;
|
||||
@@ -0,0 +1,14 @@
|
||||
-- Normalize emails to lowercase for consistency with external login normalization
|
||||
-- This migration lowercases email columns in critical authentication tables
|
||||
|
||||
-- Lowercase emails in password table (primary user identity)
|
||||
UPDATE password SET email = LOWER(email) WHERE email != LOWER(email);
|
||||
|
||||
-- Lowercase emails in usr table (workspace users)
|
||||
UPDATE usr SET email = LOWER(email) WHERE email != LOWER(email);
|
||||
|
||||
-- Lowercase emails in email_to_igroup table (instance group memberships)
|
||||
UPDATE email_to_igroup SET email = LOWER(email) WHERE email != LOWER(email);
|
||||
|
||||
-- Lowercase emails in token table (active sessions)
|
||||
UPDATE token SET email = LOWER(email) WHERE email != LOWER(email);
|
||||
@@ -28,6 +28,24 @@ lazy_static::lazy_static! {
|
||||
.and_then(|x| x.parse::<bool>().ok())
|
||||
.unwrap_or(false));
|
||||
|
||||
/// Domain to append to emails missing a domain during external login (OAuth/SAML).
|
||||
/// If set, emails without '@' will have '@{LOGIN_DOMAIN}' appended.
|
||||
/// Example: LOGIN_DOMAIN=example.com transforms "john" to "john@example.com"
|
||||
pub static ref LOGIN_DOMAIN: Option<String> = std::env::var("LOGIN_DOMAIN").ok();
|
||||
|
||||
}
|
||||
|
||||
/// Normalize an email from external login by appending LOGIN_DOMAIN if the email is missing a domain.
|
||||
/// Returns the email lowercased.
|
||||
pub fn normalize_external_email(email: &str) -> String {
|
||||
let email = email.trim();
|
||||
if email.contains('@') {
|
||||
email.to_lowercase()
|
||||
} else if let Some(domain) = LOGIN_DOMAIN.as_ref() {
|
||||
format!("{}@{}", email, domain).to_lowercase()
|
||||
} else {
|
||||
email.to_lowercase()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -41,3 +59,41 @@ pub enum InstanceEvent {
|
||||
UserInvitedWorkspace { workspace: String, email: String },
|
||||
UserJoinedWorkspace { workspace: String, email: String, username: String },
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_normalize_external_email_with_domain() {
|
||||
// Email already has domain - should just lowercase
|
||||
assert_eq!(
|
||||
normalize_external_email("John.Doe@Example.COM"),
|
||||
"john.doe@example.com"
|
||||
);
|
||||
assert_eq!(
|
||||
normalize_external_email("user@domain.org"),
|
||||
"user@domain.org"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_external_email_trims_whitespace() {
|
||||
assert_eq!(
|
||||
normalize_external_email(" user@example.com "),
|
||||
"user@example.com"
|
||||
);
|
||||
assert_eq!(normalize_external_email(" john "), "john");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normalize_external_email_without_domain_no_env() {
|
||||
// When LOGIN_DOMAIN is not set, email without @ stays as-is (lowercased)
|
||||
// Note: This test's behavior depends on whether LOGIN_DOMAIN env var is set
|
||||
// In the test environment, it's typically not set
|
||||
let result = normalize_external_email("JohnDoe");
|
||||
// Result will either be "johndoe" or "johndoe@{LOGIN_DOMAIN}" depending on env
|
||||
assert!(result.starts_with("johndoe"));
|
||||
assert_eq!(result, result.to_lowercase());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user