fix: Forbid removing emails if they're required to register

This commit is contained in:
Ginger
2026-04-23 13:17:48 -04:00
parent 8171e3d614
commit 567d809efe
5 changed files with 30 additions and 4 deletions
+3
View File
@@ -2104,6 +2104,9 @@
# Whether to require that users provide an email address when they # Whether to require that users provide an email address when they
# register. # register.
# #
# If either this option or `require_email_for_token_registration` are set,
# users will not be allowed to remove their email address.
#
#require_email_for_registration = false #require_email_for_registration = false
# Whether to require that users who register with a registration token # Whether to require that users who register with a registration token
+12
View File
@@ -53,6 +53,10 @@ pub(crate) async fn request_3pid_management_token_via_email_route(
State(services): State<crate::State>, State(services): State<crate::State>,
body: Ruma<request_3pid_management_token_via_email::v3::Request>, body: Ruma<request_3pid_management_token_via_email::v3::Request>,
) -> Result<request_3pid_management_token_via_email::v3::Response> { ) -> Result<request_3pid_management_token_via_email::v3::Response> {
if !services.threepid.email_requirement().may_change() {
return Err!(Request(Forbidden("You may not change your email address.")));
}
let Ok(email) = Address::try_from(body.email.clone()) else { let Ok(email) = Address::try_from(body.email.clone()) else {
return Err!(Request(InvalidParam("Invalid email address."))); return Err!(Request(InvalidParam("Invalid email address.")));
}; };
@@ -105,6 +109,10 @@ pub(crate) async fn add_3pid_route(
) -> Result<add_3pid::v3::Response> { ) -> Result<add_3pid::v3::Response> {
let sender_user = body.sender_user(); let sender_user = body.sender_user();
if !services.threepid.email_requirement().may_change() {
return Err!(Request(Forbidden("You may not change your email address.")));
}
// Require password auth to add an email // Require password auth to add an email
let _ = services let _ = services
.uiaa .uiaa
@@ -138,6 +146,10 @@ pub(crate) async fn delete_3pid_route(
}); });
} }
if !services.threepid.email_requirement().may_remove() {
return Err!(Request(Forbidden("You may not remove your email address.")));
}
if services if services
.threepid .threepid
.disassociate_localpart_email(sender_user.localpart()) .disassociate_localpart_email(sender_user.localpart())
+1 -1
View File
@@ -32,7 +32,7 @@ pub(crate) async fn get_capabilities_route(
// Only allow 3pid changes if SMTP is configured // Only allow 3pid changes if SMTP is configured
capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability { capabilities.thirdparty_id_changes = ThirdPartyIdChangesCapability {
enabled: services.mailer.mailer().is_some(), enabled: services.threepid.email_requirement().may_change(),
}; };
capabilities.get_login_token = GetLoginTokenCapability { capabilities.get_login_token = GetLoginTokenCapability {
+3
View File
@@ -2516,6 +2516,9 @@ pub struct SmtpConfig {
/// Whether to require that users provide an email address when they /// Whether to require that users provide an email address when they
/// register. /// register.
/// ///
/// If either this option or `require_email_for_token_registration` are set,
/// users will not be allowed to remove their email address.
///
/// default: false /// default: false
#[serde(default)] #[serde(default)]
pub require_email_for_registration: bool, pub require_email_for_registration: bool,
+11 -3
View File
@@ -27,13 +27,21 @@ pub struct Service {
pub enum EmailRequirement { pub enum EmailRequirement {
/// Users may change their email, but cannot remove it entirely. /// Users may change their email, but cannot remove it entirely.
Always, Required,
/// Users may change or remove their email. /// Users may change or remove their email.
Optional, Optional,
/// SMTP is not configured. /// Users may not change their email at all.
Unavailable, Unavailable,
} }
impl EmailRequirement {
#[must_use]
pub fn may_change(&self) -> bool { matches!(self, Self::Required | Self::Optional) }
#[must_use]
pub fn may_remove(&self) -> bool { matches!(self, Self::Optional) }
}
struct Data { struct Data {
localpart_email: Arc<Map>, localpart_email: Arc<Map>,
email_localpart: Arc<Map>, email_localpart: Arc<Map>,
@@ -77,7 +85,7 @@ impl Service {
pub fn email_requirement(&self) -> EmailRequirement { pub fn email_requirement(&self) -> EmailRequirement {
if let Some(smtp) = &self.services.config.smtp { if let Some(smtp) = &self.services.config.smtp {
if smtp.require_email_for_registration || smtp.require_email_for_token_registration { if smtp.require_email_for_registration || smtp.require_email_for_token_registration {
EmailRequirement::Always EmailRequirement::Required
} else { } else {
EmailRequirement::Optional EmailRequirement::Optional
} }