Skip to main content

tuwunel_service/admin/
create.rs

1use std::collections::BTreeMap;
2
3use futures::FutureExt;
4use ruma::{
5	RoomId, RoomVersionId,
6	events::room::{
7		canonical_alias::RoomCanonicalAliasEventContent,
8		create::RoomCreateEventContent,
9		guest_access::{GuestAccess, RoomGuestAccessEventContent},
10		history_visibility::{HistoryVisibility, RoomHistoryVisibilityEventContent},
11		join_rules::{JoinRule, RoomJoinRulesEventContent},
12		member::{MembershipState, RoomMemberEventContent},
13		name::RoomNameEventContent,
14		power_levels::RoomPowerLevelsEventContent,
15		preview_url::RoomPreviewUrlsEventContent,
16		topic::{RoomTopicEventContent, TopicContentBlock},
17	},
18};
19use tuwunel_core::{Result, pdu::PduBuilder};
20
21use crate::Services;
22
23/// Create the server user.
24///
25/// This should be the first user on the server and created prior to the
26/// admin room.
27pub async fn create_server_user(services: &Services) -> Result {
28	let server_user = services.globals.server_user.as_ref();
29
30	// Create a user for the server
31	services
32		.users
33		.create(server_user, None, None)
34		.await?;
35
36	Ok(())
37}
38
39/// Create the admin room.
40///
41/// Users in this room are considered admins by tuwunel, and the room can be
42/// used to issue admin commands by talking to the server user inside it.
43pub async fn create_admin_room(services: &Services) -> Result {
44	let room_id = RoomId::new_v1(services.globals.server_name());
45	let room_version = RoomVersionId::V11;
46
47	let _short_id = services
48		.short
49		.get_or_create_shortroomid(&room_id)
50		.await;
51
52	let state_lock = services.state.mutex.lock(&room_id).await;
53
54	// Create a user for the server
55	let server_user = services.globals.server_user.as_ref();
56	if !services.users.exists(server_user).await {
57		create_server_user(services).await?;
58	}
59
60	let create_content = {
61		use RoomVersionId::*;
62		match room_version {
63			| V1 | V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 =>
64				RoomCreateEventContent::new_v1(server_user.into()),
65			| _ => RoomCreateEventContent::new_v11(),
66		}
67	};
68
69	// 1. The room create event
70	services
71		.timeline
72		.build_and_append_pdu(
73			PduBuilder::state(String::new(), &RoomCreateEventContent {
74				federate: services.config.federate_admin_room,
75				predecessor: None,
76				room_version: room_version.clone(),
77				..create_content
78			}),
79			server_user,
80			&room_id,
81			&state_lock,
82		)
83		.boxed()
84		.await?;
85
86	// 2. Make server user/bot join
87	services
88		.timeline
89		.build_and_append_pdu(
90			PduBuilder::state(
91				String::from(server_user),
92				&RoomMemberEventContent::new(MembershipState::Join),
93			),
94			server_user,
95			&room_id,
96			&state_lock,
97		)
98		.boxed()
99		.await?;
100
101	// 3. Power levels
102	let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
103
104	services
105		.timeline
106		.build_and_append_pdu(
107			PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
108				users,
109				..Default::default()
110			}),
111			server_user,
112			&room_id,
113			&state_lock,
114		)
115		.boxed()
116		.await?;
117
118	// 4.1 Join Rules
119	services
120		.timeline
121		.build_and_append_pdu(
122			PduBuilder::state(String::new(), &RoomJoinRulesEventContent::new(JoinRule::Invite)),
123			server_user,
124			&room_id,
125			&state_lock,
126		)
127		.boxed()
128		.await?;
129
130	// 4.2 History Visibility
131	services
132		.timeline
133		.build_and_append_pdu(
134			PduBuilder::state(
135				String::new(),
136				&RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
137			),
138			server_user,
139			&room_id,
140			&state_lock,
141		)
142		.boxed()
143		.await?;
144
145	// 4.3 Guest Access
146	services
147		.timeline
148		.build_and_append_pdu(
149			PduBuilder::state(
150				String::new(),
151				&RoomGuestAccessEventContent::new(GuestAccess::Forbidden),
152			),
153			server_user,
154			&room_id,
155			&state_lock,
156		)
157		.boxed()
158		.await?;
159
160	// 5. Events implied by name and topic
161	let room_name = format!("{} Admin Room", services.config.server_name);
162	services
163		.timeline
164		.build_and_append_pdu(
165			PduBuilder::state(String::new(), &RoomNameEventContent::new(room_name)),
166			server_user,
167			&room_id,
168			&state_lock,
169		)
170		.boxed()
171		.await?;
172
173	services
174		.timeline
175		.build_and_append_pdu(
176			PduBuilder::state(String::new(), &RoomTopicEventContent {
177				topic_block: TopicContentBlock::default(),
178				topic: format!("Manage {} | Run commands prefixed with `!admin` | Run `!admin -h` for help | Documentation: https://matrix-construct.github.io/tuwunel", services.config.server_name),
179			}),
180			server_user,
181			&room_id,
182			&state_lock,
183		)
184		.boxed()
185		.await?;
186
187	// 6. Room alias
188	let alias = &services.admin.admin_alias;
189
190	services
191		.timeline
192		.build_and_append_pdu(
193			PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
194				alias: Some(alias.clone()),
195				alt_aliases: Vec::new(),
196			}),
197			server_user,
198			&room_id,
199			&state_lock,
200		)
201		.boxed()
202		.await?;
203
204	services.alias.set_alias(alias, &room_id)?;
205
206	// 7. (ad-hoc) Disable room URL previews for everyone by default
207	services
208		.timeline
209		.build_and_append_pdu(
210			PduBuilder::state(String::new(), &RoomPreviewUrlsEventContent { disabled: true }),
211			server_user,
212			&room_id,
213			&state_lock,
214		)
215		.boxed()
216		.await?;
217
218	Ok(())
219}