Skip to main content

tuwunel_api/client/
account.rs

1use axum::extract::State;
2use futures::{FutureExt, StreamExt};
3use ruma::api::client::account::{
4	ThirdPartyIdRemovalStatus, change_password, deactivate, get_3pids,
5	request_3pid_management_token_via_email, request_3pid_management_token_via_msisdn, whoami,
6};
7use tuwunel_core::{Err, Result, err, info, utils::ReadyExt};
8
9use crate::{ClientIp, Ruma, router::auth_uiaa};
10
11/// # `POST /_matrix/client/r0/account/password`
12///
13/// Changes the password of this account.
14///
15/// - Requires UIAA to verify user password
16/// - Changes the password of the sender user
17/// - The password hash is calculated using argon2 with 32 character salt, the
18///   plain password is
19/// not saved
20///
21/// If logout_devices is true it does the following for each device except the
22/// sender device:
23/// - Invalidates access token
24/// - Deletes device metadata (device id, device display name, last seen ip,
25///   last seen ts)
26/// - Forgets to-device events
27/// - Triggers device list updates
28#[tracing::instrument(skip_all, fields(%client), name = "change_password")]
29pub(crate) async fn change_password_route(
30	State(services): State<crate::State>,
31	ClientIp(client): ClientIp,
32	body: Ruma<change_password::v3::Request>,
33) -> Result<change_password::v3::Response> {
34	let ref sender_user = auth_uiaa(&services, &body).await?;
35
36	services
37		.users
38		.set_password(sender_user, Some(&body.new_password))
39		.await?;
40
41	if body.logout_devices {
42		// Logout all devices except the current one
43		services
44			.users
45			.all_device_ids(sender_user)
46			.ready_filter(|&id| Some(id) != body.sender_device.as_deref())
47			.for_each(|id| services.users.remove_device(sender_user, id))
48			.await;
49	}
50
51	info!("User {sender_user} changed their password.");
52
53	if services.server.config.admin_room_notices {
54		services
55			.admin
56			.notice(&format!("User {sender_user} changed their password."))
57			.await;
58	}
59
60	Ok(change_password::v3::Response {})
61}
62
63/// # `GET _matrix/client/r0/account/whoami`
64///
65/// Get `user_id` of the sender user.
66///
67/// Note: Also works for Application Services
68pub(crate) async fn whoami_route(
69	State(services): State<crate::State>,
70	body: Ruma<whoami::v3::Request>,
71) -> Result<whoami::v3::Response> {
72	Ok(whoami::v3::Response {
73		user_id: body.sender_user().to_owned(),
74		device_id: body.sender_device.clone(),
75		is_guest: body.appservice_info.is_none()
76			&& services
77				.users
78				.is_deactivated(body.sender_user())
79				.await
80				.map_err(|_| err!(Request(Forbidden("User does not exist."))))?,
81	})
82}
83
84/// # `POST /_matrix/client/r0/account/deactivate`
85///
86/// Deactivate sender user account.
87///
88/// - Leaves all rooms and rejects all invitations
89/// - Invalidates all access tokens
90/// - Deletes all device metadata (device id, device display name, last seen ip,
91///   last seen ts)
92/// - Forgets all to-device events
93/// - Triggers device list updates
94/// - Removes ability to log in again
95#[tracing::instrument(skip_all, fields(%client), name = "deactivate")]
96pub(crate) async fn deactivate_route(
97	State(services): State<crate::State>,
98	ClientIp(client): ClientIp,
99	body: Ruma<deactivate::v3::Request>,
100) -> Result<deactivate::v3::Response> {
101	let ref sender_user = auth_uiaa(&services, &body).await?;
102
103	services
104		.deactivate
105		.full_deactivate(sender_user, body.erase)
106		.boxed()
107		.await?;
108
109	info!("User {sender_user} deactivated their account.");
110	if services.server.config.admin_room_notices {
111		services
112			.admin
113			.notice(&format!("User {sender_user} deactivated their account."))
114			.await;
115	}
116
117	Ok(deactivate::v3::Response {
118		id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport,
119	})
120}
121
122/// # `GET _matrix/client/v3/account/3pid`
123///
124/// Get a list of third party identifiers associated with this account.
125///
126/// - Currently always returns empty list
127pub(crate) async fn third_party_route(
128	body: Ruma<get_3pids::v3::Request>,
129) -> Result<get_3pids::v3::Response> {
130	let _sender_user = body
131		.sender_user
132		.as_ref()
133		.expect("user is authenticated");
134
135	Ok(get_3pids::v3::Response::new(Vec::new()))
136}
137
138/// # `POST /_matrix/client/v3/account/3pid/email/requestToken`
139///
140/// "This API should be used to request validation tokens when adding an email
141/// address to an account"
142///
143/// - 403 signals that The homeserver does not allow the third party identifier
144///   as a contact option.
145pub(crate) async fn request_3pid_management_token_via_email_route(
146	_body: Ruma<request_3pid_management_token_via_email::v3::Request>,
147) -> Result<request_3pid_management_token_via_email::v3::Response> {
148	Err!(Request(ThreepidDenied("Third party identifiers are not implemented")))
149}
150
151/// # `POST /_matrix/client/v3/account/3pid/msisdn/requestToken`
152///
153/// "This API should be used to request validation tokens when adding an phone
154/// number to an account"
155///
156/// - 403 signals that The homeserver does not allow the third party identifier
157///   as a contact option.
158pub(crate) async fn request_3pid_management_token_via_msisdn_route(
159	_body: Ruma<request_3pid_management_token_via_msisdn::v3::Request>,
160) -> Result<request_3pid_management_token_via_msisdn::v3::Response> {
161	Err!(Request(ThreepidDenied("Third party identifiers are not implemented")))
162}