From 228d13c5c9b7b7faab8fbc544734e2ba9110a5fe Mon Sep 17 00:00:00 2001 From: Maurus Decimus <11444311+mdecimus@users.noreply.github.com> Date: Thu, 30 Apr 2026 11:34:57 +0200 Subject: [PATCH] Fix impersonation when using external directories --- CHANGELOG.md | 5 ++++- crates/common/src/auth/authentication.rs | 19 ++++++++++++++++++- install.sh | 5 +++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d9b2583..5f65cef1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,10 +12,13 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If - Replaced `STALWART_HTTPS_PORT` with `STALWART_PUBLIC_URL`. ## Fixed -- Directory: Invalidate caches when group memberships change on an external directory. +- Directory: + - Invalidate caches when group memberships change on an external directory. + - User impersonation. - Log viewer: All events show as `INFO`. - Registry: Allow changing object variants. - Node id renewal. +- DNS Updater: Fix Route53 serialization format. ## [0.16.2] - 2026-04-28 diff --git a/crates/common/src/auth/authentication.rs b/crates/common/src/auth/authentication.rs index ab920307..829925e1 100644 --- a/crates/common/src/auth/authentication.rs +++ b/crates/common/src/auth/authentication.rs @@ -142,6 +142,13 @@ impl Server { // Authenticate app passwords if let Some(app_pass) = AppPassword::parse(secret) { + if username.is_master() { + return Err(trc::AuthEvent::Failed + .into_err() + .ctx(trc::Key::AccountName, auth_as_address.to_string()) + .ctx(trc::Key::SpanId, req.session_id) + .reason("App passwords cannot be used for impersonation")); + } return if let Some(account_id) = self.account_id_from_parts(auth_as_local, domain.id).await? { @@ -164,7 +171,17 @@ impl Server { // Obtain external directory, if any let mut is_alias_login = false; let token = if let Some(directory) = self.get_directory_for_cached_domain(&domain) { - let directory_account = directory.authenticate(&req.credentials).await?; + let directory_account = if username.is_master() { + directory + .authenticate(&Credentials::Basic { + username: auth_as_address.to_string(), + secret: secret.clone(), + mfa_token: mfa_token.clone(), + }) + .await? + } else { + directory.authenticate(&req.credentials).await? + }; is_alias_login = directory_account.email != auth_as_address; self.build_directory_token(directory_account, req.remote_ip) diff --git a/install.sh b/install.sh index a3b95801..bb518080 100644 --- a/install.sh +++ b/install.sh @@ -207,8 +207,9 @@ write_env_file() { # Override the hostname used in HTTP responses #STALWART_HOSTNAME=mail.example.com -# Override the HTTPS port used in HTTPS responses -#STALWART_HTTPS_PORT=8080 +# Override the public base URL published in OAuth, OIDC, and JMAP discovery +# documents. Accepts scheme, host, optional port, and optional path prefix. +#STALWART_PUBLIC_URL=https://mail.example.com # Enable bootstrap / recovery mode on startup. Accepted: 1, true. Default: false. #STALWART_RECOVERY_MODE=true