tuwunel_api/client/
admin.rs1use axum::extract::State;
2use futures::future::join3;
3use ruma::{
4 UserId,
5 api::client::admin::{is_user_locked, is_user_suspended, lock_user, suspend_user},
6};
7use tuwunel_core::{Err, Result};
8
9use crate::Ruma;
10
11async fn authorize(services: &crate::State, caller: &UserId, target: &UserId) -> Result {
18 if caller == target {
19 return Err!(Request(Forbidden("You cannot suspend or lock your own account")));
20 }
21
22 if !services.globals.user_is_local(target) {
23 return Err!(Request(InvalidParam("User is not local to this server")));
24 }
25
26 let (caller_admin, target_active, target_admin) = join3(
27 services.admin.user_is_admin(caller),
28 services.users.is_active(target),
29 services.admin.user_is_admin(target),
30 )
31 .await;
32
33 if !caller_admin {
34 return Err!(Request(Forbidden("Only server administrators can use this endpoint")));
35 }
36
37 if !target_active {
38 return Err!(Request(NotFound("Unknown user")));
39 }
40
41 if target_admin {
42 return Err!(Request(Forbidden(
43 "You cannot suspend or lock another server administrator"
44 )));
45 }
46
47 Ok(())
48}
49
50pub(crate) async fn is_user_suspended_route(
52 State(services): State<crate::State>,
53 body: Ruma<is_user_suspended::v1::Request>,
54) -> Result<is_user_suspended::v1::Response> {
55 let user_id = &body.user_id;
56
57 authorize(&services, body.sender_user(), user_id).await?;
58
59 Ok(is_user_suspended::v1::Response::new(services.users.is_suspended(user_id).await))
60}
61
62pub(crate) async fn suspend_user_route(
64 State(services): State<crate::State>,
65 body: Ruma<suspend_user::v1::Request>,
66) -> Result<suspend_user::v1::Response> {
67 let sender_user = body.sender_user();
68 let user_id = &body.user_id;
69
70 authorize(&services, sender_user, user_id).await?;
71
72 if services.users.is_suspended(user_id).await == body.suspended {
73 return Ok(suspend_user::v1::Response::new(body.suspended));
74 }
75
76 let action = match body.suspended {
77 | true => {
78 services.users.set_suspended(user_id, sender_user);
79 "suspended"
80 },
81 | false => {
82 services.users.clear_suspended(user_id);
83 "unsuspended"
84 },
85 };
86
87 if services.server.config.admin_room_notices {
88 services
89 .admin
90 .send_text(&format!("{user_id} has been {action} by {sender_user}."))
91 .await;
92 }
93
94 Ok(suspend_user::v1::Response::new(body.suspended))
95}
96
97pub(crate) async fn is_user_locked_route(
99 State(services): State<crate::State>,
100 body: Ruma<is_user_locked::v1::Request>,
101) -> Result<is_user_locked::v1::Response> {
102 let user_id = &body.user_id;
103
104 authorize(&services, body.sender_user(), user_id).await?;
105
106 Ok(is_user_locked::v1::Response::new(services.users.is_locked(user_id).await))
107}
108
109pub(crate) async fn lock_user_route(
111 State(services): State<crate::State>,
112 body: Ruma<lock_user::v1::Request>,
113) -> Result<lock_user::v1::Response> {
114 let sender_user = body.sender_user();
115 let user_id = &body.user_id;
116
117 authorize(&services, sender_user, user_id).await?;
118
119 if services.users.is_locked(user_id).await == body.locked {
120 return Ok(lock_user::v1::Response::new(body.locked));
121 }
122
123 let action = match body.locked {
124 | true => {
125 services.users.set_locked(user_id, sender_user);
126 "locked"
127 },
128 | false => {
129 services.users.clear_locked(user_id);
130 "unlocked"
131 },
132 };
133
134 if services.server.config.admin_room_notices {
135 services
136 .admin
137 .send_text(&format!("{user_id} has been {action} by {sender_user}."))
138 .await;
139 }
140
141 Ok(lock_user::v1::Response::new(body.locked))
142}