Skip to main content

tuwunel_service/users/
register.rs

1use futures::FutureExt;
2use ruma::{UserId, events::GlobalAccountDataEventType, push};
3use tuwunel_core::{Err, Result, error, implement, info, is_equal_to, warn};
4
5use crate::profile::Propagation;
6
7#[derive(Debug, Default)]
8pub struct Register<'a> {
9	pub user_id: Option<&'a UserId>,
10	pub username: Option<&'a str>,
11	pub password: Option<&'a str>,
12	pub origin: Option<&'a str>,
13	pub is_appservice: bool,
14	pub is_guest: bool,
15	pub grant_first_user_admin: bool,
16	pub displayname: Option<&'a str>,
17	pub omit_displayname_suffix: bool,
18}
19
20/// Fully register a local user
21///
22/// Returns a device id and access token for the registered user
23#[implement(super::Service)]
24#[tracing::instrument(level = "info", skip(self, password))]
25pub async fn full_register(
26	&self,
27	Register {
28		username,
29		user_id,
30		password,
31		origin,
32		is_appservice,
33		is_guest,
34		grant_first_user_admin,
35		displayname,
36		omit_displayname_suffix,
37	}: Register<'_>,
38) -> Result {
39	let ref user_id = user_id
40		.map(ToOwned::to_owned)
41		.map(Ok)
42		.or_else(|| {
43			username.map(|username| {
44				UserId::parse_with_server_name(username, self.services.globals.server_name())
45			})
46		})
47		.transpose()?
48		.expect("Caller failed to supply either user_id or username parameter");
49
50	if !self.services.globals.user_is_local(user_id) {
51		return Err!("Cannot register remote user");
52	}
53
54	if self.services.users.exists(user_id).await {
55		return Err!(Request(UserInUse("User ID is not available.")));
56	}
57
58	// Create user
59	self.services
60		.users
61		.create(user_id, password, origin)
62		.await?;
63
64	let displayname_suffix = self
65		.services
66		.config
67		.new_user_displayname_suffix
68		.as_str();
69
70	let mut displayname = displayname.unwrap_or_else(|| user_id.localpart());
71
72	let displayname_with_suffix;
73	if !displayname_suffix.is_empty() && !omit_displayname_suffix {
74		displayname_with_suffix = format!("{displayname} {displayname_suffix}");
75		displayname = &displayname_with_suffix;
76	}
77
78	self.services
79		.profile
80		.set_displayname(user_id, Some(displayname), Some(Propagation::None))
81		.await?;
82
83	// Initial account data
84	self.services
85		.account_data
86		.update(
87			None,
88			user_id,
89			GlobalAccountDataEventType::PushRules
90				.to_string()
91				.into(),
92			&serde_json::to_value(ruma::events::push_rules::PushRulesEvent {
93				content: ruma::events::push_rules::PushRulesEventContent {
94					global: push::Ruleset::server_default(user_id),
95				},
96			})?,
97		)
98		.await?;
99
100	// If this is the first real user, grant them admin privileges except for guest
101	// users
102	// Note: the server user is generated first
103	if !is_guest
104		&& !is_appservice
105		&& grant_first_user_admin
106		&& self.services.config.grant_admin_to_first_user
107		&& let Ok(admin_room) = self.services.admin.get_admin_room().await
108		&& self
109			.services
110			.state_cache
111			.room_joined_count(&admin_room)
112			.await
113			.is_ok_and(is_equal_to!(1))
114	{
115		self.services
116			.admin
117			.make_user_admin(user_id)
118			.boxed()
119			.await?;
120		warn!("Granting {user_id} admin privileges as the first user");
121	}
122
123	if !is_appservice && (self.services.config.allow_guests_auto_join_rooms || !is_guest) {
124		for room in &self.services.server.config.auto_join_rooms {
125			let Ok(room_id) = self.services.alias.maybe_resolve(room).await else {
126				error!(
127					"Failed to resolve room alias to room ID when attempting to auto join \
128					 {room}, skipping"
129				);
130				continue;
131			};
132
133			if !self
134				.services
135				.state_cache
136				.server_in_room(self.services.globals.server_name(), &room_id)
137				.await
138			{
139				warn!(
140					"Skipping room {room} to automatically join as we have never joined before."
141				);
142				continue;
143			}
144
145			match self
146				.services
147				.membership
148				.join(
149					user_id,
150					&room_id,
151					Some(room),
152					Some("Automatically joining this room upon registration".to_owned()),
153					&[],
154					false,
155					None,
156				)
157				.boxed()
158				.await
159			{
160				| Err(e) => {
161					// don't return this error so we don't fail registrations
162					error!("Failed to automatically join room {room} for user {user_id}: {e}");
163				},
164				| _ => {
165					info!("Automatically joined room {room} for user {user_id}");
166				},
167			}
168		}
169	}
170
171	Ok(())
172}