fix(appservice): Ensure appservice sender user is created on registration and authentication

Fixes #813 where appservice sender users were not being created, causing bridges
like mautrix-telegram to fail on startup.

This fix addresses the issue in two places:
1. During appservice registration - creates the sender_localpart user if it
   doesn't exist when the appservice is first registered
2. During authentication requests - only when authenticating AS the sender_localpart
   user, ensures it exists (for appservices registered before this fix)

This matches the expected Matrix appservice behaviour where the appservice's
sender user should be automatically created and available for use, while:
- Avoiding unnecessary checks when authenticating as puppet users
- Preventing inadvertent creation of puppet users during double-puppeting
- Minimizing performance impact by only checking when needed
This commit is contained in:
Tom Foster 2025-08-10 12:27:27 +01:00
commit 7a3493d1ac
2 changed files with 22 additions and 1 deletions

View file

@ -220,6 +220,13 @@ async fn auth_appservice(
return Err!(Request(Exclusive("User is not in namespace."))); return Err!(Request(Exclusive("User is not in namespace.")));
} }
// Only check/create the sender_localpart user if that's who is authenticating
if user_id.localpart() == info.registration.sender_localpart.as_str() {
if !services.users.exists(&user_id).await {
services.users.create(&user_id, None)?;
}
}
Ok(Auth { Ok(Auth {
origin: None, origin: None,
sender_user: Some(user_id), sender_user: Some(user_id),

View file

@ -11,7 +11,7 @@ use ruma::{RoomAliasId, RoomId, UserId, api::appservice::Registration};
use tokio::sync::{RwLock, RwLockReadGuard}; use tokio::sync::{RwLock, RwLockReadGuard};
pub use self::{namespace_regex::NamespaceRegex, registration_info::RegistrationInfo}; pub use self::{namespace_regex::NamespaceRegex, registration_info::RegistrationInfo};
use crate::{Dep, sending}; use crate::{Dep, globals, sending, users};
pub struct Service { pub struct Service {
registration_info: RwLock<Registrations>, registration_info: RwLock<Registrations>,
@ -20,7 +20,9 @@ pub struct Service {
} }
struct Services { struct Services {
globals: Dep<globals::Service>,
sending: Dep<sending::Service>, sending: Dep<sending::Service>,
users: Dep<users::Service>,
} }
struct Data { struct Data {
@ -35,7 +37,9 @@ impl crate::Service for Service {
Ok(Arc::new(Self { Ok(Arc::new(Self {
registration_info: RwLock::new(BTreeMap::new()), registration_info: RwLock::new(BTreeMap::new()),
services: Services { services: Services {
globals: args.depend::<globals::Service>("globals"),
sending: args.depend::<sending::Service>("sending"), sending: args.depend::<sending::Service>("sending"),
users: args.depend::<users::Service>("users"),
}, },
db: Data { db: Data {
id_appserviceregistrations: args.db["id_appserviceregistrations"].clone(), id_appserviceregistrations: args.db["id_appserviceregistrations"].clone(),
@ -68,6 +72,16 @@ impl Service {
appservice_config_body: &str, appservice_config_body: &str,
) -> Result { ) -> Result {
//TODO: Check for collisions between exclusive appservice namespaces //TODO: Check for collisions between exclusive appservice namespaces
let appservice_user_id = UserId::parse_with_server_name(
registration.sender_localpart.as_str(),
self.services.globals.server_name(),
)?;
if !self.services.users.exists(&appservice_user_id).await {
self.services.users.create(&appservice_user_id, None)?;
}
self.registration_info self.registration_info
.write() .write()
.await .await