feat: email blacklist (#6779)

* feat: email blacklist

* feat: wildcard support
This commit is contained in:
Michael H.
2026-07-20 14:02:55 +02:00
committed by GitHub
parent 6319832465
commit e52ae416a8
2 changed files with 34 additions and 0 deletions
+2
View File
@@ -238,6 +238,8 @@ vars! {
NEVERBOUNCE_API_KEY: String = "";
NEVERBOUNCE_BASE_URL: String = neverbounce::DEFAULT_API_URL;
EMAIL_DOMAIN_BLACKLIST: StringCsv = StringCsv(vec![]);
CLICKHOUSE_REPLICATED: bool = false;
CLICKHOUSE_URL: String = "http://localhost:8123";
CLICKHOUSE_USER: String = "default";
@@ -1816,7 +1816,39 @@ impl From<NewAccount> for AccountRegisterFlow {
}
}
/// Blacklist entries are matched literally, unless they begin with `*.`, in
/// which case they match any subdomain of the remaining suffix.
fn is_blacklisted_domain(domain: &str) -> bool {
let domain = domain.to_ascii_lowercase();
ENV.EMAIL_DOMAIN_BLACKLIST.iter().any(|entry| {
let entry = entry.trim().to_ascii_lowercase();
match entry.strip_prefix("*.") {
Some(suffix) => domain
.strip_suffix(suffix)
.is_some_and(|subdomain| subdomain.ends_with('.')),
None => entry == domain,
}
})
}
fn ensure_email_domain_is_allowed(email: &str) -> Result<(), ApiError> {
let Some((_, domain)) = email.rsplit_once('@') else {
return Err(ApiError::Request(email_check_error_generic()));
};
if is_blacklisted_domain(domain) {
info!(email.domain = domain, "blacklisted email domain, denying");
return Err(ApiError::Request(email_check_error_generic()));
}
Ok(())
}
async fn ensure_email_is_usable(email: &str) -> Result<(), ApiError> {
ensure_email_domain_is_allowed(email)?;
let result = check_email(email).await.map_err(ApiError::Request)?;
if matches!(