Skip to main content

tuwunel_service/emergency/
mod.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use ruma::{
5	events::{
6		GlobalAccountDataEvent, GlobalAccountDataEventType, push_rules::PushRulesEventContent,
7	},
8	push::Ruleset,
9};
10use tuwunel_core::{Result, debug_warn, error, warn};
11
12pub struct Service {
13	services: Arc<crate::services::OnceServices>,
14}
15
16#[async_trait]
17impl crate::Service for Service {
18	fn build(args: &crate::Args<'_>) -> Result<Arc<Self>> {
19		Ok(Arc::new(Self { services: args.services.clone() }))
20	}
21
22	async fn worker(self: Arc<Self>) -> Result {
23		if self
24			.services
25			.config
26			.emergency_password
27			.as_ref()
28			.is_none_or(String::is_empty)
29		{
30			return Ok(());
31		}
32
33		if self.services.globals.is_read_only() {
34			debug_warn!("emergency password feature ignored in read_only mode.");
35			return Ok(());
36		}
37
38		if self.services.config.ldap.enable {
39			warn!("emergency password feature not available with LDAP enabled.");
40			return Ok(());
41		}
42
43		self.set_emergency_access()
44			.await
45			.inspect_err(|e| {
46				error!("Failed to set the emergency password for the server user: {e}");
47			})
48	}
49
50	fn name(&self) -> &str { crate::service::make_name(std::module_path!()) }
51}
52
53impl Service {
54	/// Sets the emergency password and push rules for the server user account
55	/// in case emergency password is set
56	async fn set_emergency_access(&self) -> Result {
57		let server_user = &self.services.globals.server_user;
58
59		self.services
60			.users
61			.set_password(server_user, self.services.config.emergency_password.as_deref())
62			.await?;
63
64		let (ruleset, pwd_set) = match self.services.config.emergency_password {
65			| Some(_) => (Ruleset::server_default(server_user), true),
66			| None => (Ruleset::new(), false),
67		};
68
69		self.services
70			.account_data
71			.update(
72				None,
73				server_user,
74				GlobalAccountDataEventType::PushRules
75					.to_string()
76					.into(),
77				&serde_json::to_value(&GlobalAccountDataEvent {
78					content: PushRulesEventContent { global: ruleset },
79				})
80				.expect("to json value always works"),
81			)
82			.await?;
83
84		if pwd_set {
85			warn!(
86				"The server account emergency password is set! Please unset it as soon as you \
87				 finish admin account recovery! You will be logged out of the server service \
88				 account when you finish."
89			);
90			Ok(())
91		} else {
92			// logs out any users still in the server service account and removes sessions
93			self.services
94				.users
95				.deactivate_account(server_user)
96				.await
97		}
98	}
99}