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, matrix::room_version, 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_id = RoomVersionId::V11;
46
47	let room_version_rules = room_version::rules(&room_version_id)?;
48
49	let _short_id = services
50		.short
51		.get_or_create_shortroomid(&room_id)
52		.await;
53
54	let state_lock = services.state.mutex.lock(&room_id).await;
55
56	// Create a user for the server
57	let server_user = services.globals.server_user.as_ref();
58	if !services.users.exists(server_user).await {
59		create_server_user(services).await?;
60	}
61
62	let create_content = if !room_version_rules
63		.authorization
64		.use_room_create_sender
65	{
66		RoomCreateEventContent::new_v1(server_user.into())
67	} else {
68		RoomCreateEventContent::new_v11()
69	};
70
71	// 1. The room create event
72	services
73		.timeline
74		.build_and_append_pdu(
75			PduBuilder::state(String::new(), &RoomCreateEventContent {
76				federate: services.config.federate_admin_room,
77				predecessor: None,
78				room_version: room_version_id.clone(),
79				..create_content
80			}),
81			server_user,
82			&room_id,
83			&state_lock,
84		)
85		.boxed()
86		.await?;
87
88	// 2. Make server user/bot join
89	services
90		.timeline
91		.build_and_append_pdu(
92			PduBuilder::state(
93				String::from(server_user),
94				&RoomMemberEventContent::new(MembershipState::Join),
95			),
96			server_user,
97			&room_id,
98			&state_lock,
99		)
100		.boxed()
101		.await?;
102
103	// 3. Power levels
104	let users = BTreeMap::from_iter([(server_user.into(), 69420.into())]);
105
106	services
107		.timeline
108		.build_and_append_pdu(
109			PduBuilder::state(String::new(), &RoomPowerLevelsEventContent {
110				users,
111				..Default::default()
112			}),
113			server_user,
114			&room_id,
115			&state_lock,
116		)
117		.boxed()
118		.await?;
119
120	// 4.1 Join Rules
121	services
122		.timeline
123		.build_and_append_pdu(
124			PduBuilder::state(String::new(), &RoomJoinRulesEventContent::new(JoinRule::Invite)),
125			server_user,
126			&room_id,
127			&state_lock,
128		)
129		.boxed()
130		.await?;
131
132	// 4.2 History Visibility
133	services
134		.timeline
135		.build_and_append_pdu(
136			PduBuilder::state(
137				String::new(),
138				&RoomHistoryVisibilityEventContent::new(HistoryVisibility::Shared),
139			),
140			server_user,
141			&room_id,
142			&state_lock,
143		)
144		.boxed()
145		.await?;
146
147	// 4.3 Guest Access
148	services
149		.timeline
150		.build_and_append_pdu(
151			PduBuilder::state(
152				String::new(),
153				&RoomGuestAccessEventContent::new(GuestAccess::Forbidden),
154			),
155			server_user,
156			&room_id,
157			&state_lock,
158		)
159		.boxed()
160		.await?;
161
162	// 5. Events implied by name and topic
163	let room_name = format!("{} Admin Room", services.config.server_name);
164	services
165		.timeline
166		.build_and_append_pdu(
167			PduBuilder::state(String::new(), &RoomNameEventContent::new(room_name)),
168			server_user,
169			&room_id,
170			&state_lock,
171		)
172		.boxed()
173		.await?;
174
175	services
176		.timeline
177		.build_and_append_pdu(
178			PduBuilder::state(String::new(), &RoomTopicEventContent {
179				topic_block: TopicContentBlock::default(),
180				topic: format!("Manage {} | Run commands prefixed with `!admin` | Run `!admin -h` for help | Documentation: https://matrix-construct.github.io/tuwunel", services.config.server_name),
181			}),
182			server_user,
183			&room_id,
184			&state_lock,
185		)
186		.boxed()
187		.await?;
188
189	// 6. Room alias
190	let alias = &services.admin.admin_alias;
191
192	services
193		.timeline
194		.build_and_append_pdu(
195			PduBuilder::state(String::new(), &RoomCanonicalAliasEventContent {
196				alias: Some(alias.clone()),
197				alt_aliases: Vec::new(),
198			}),
199			server_user,
200			&room_id,
201			&state_lock,
202		)
203		.boxed()
204		.await?;
205
206	services.alias.set_alias(alias, &room_id)?;
207
208	// 7. (ad-hoc) Disable room URL previews for everyone by default
209	services
210		.timeline
211		.build_and_append_pdu(
212			PduBuilder::state(String::new(), &RoomPreviewUrlsEventContent { disabled: true }),
213			server_user,
214			&room_id,
215			&state_lock,
216		)
217		.boxed()
218		.await?;
219
220	Ok(())
221}