Skip to main content

tuwunel_admin/user/
set_profile_key.rs

1use ruma::{OwnedUserId, profile::ProfileFieldValue};
2use serde_json::Value;
3use tuwunel_core::{Err, Result, err};
4use tuwunel_service::{Services, profile::Propagation};
5
6use super::PropagateTo;
7use crate::{
8	admin_command,
9	utils::{check_known_remote_user, parse_active_local_user_id, parse_user_id},
10};
11
12#[admin_command]
13pub(super) async fn set_profile_key(
14	&self,
15	user_id: String,
16	key: String,
17	value: Vec<String>,
18	clear: bool,
19	propagate_to: Option<PropagateTo>,
20) -> Result {
21	let user_id = profile_owner(self.services, &user_id, clear, propagate_to.is_some()).await?;
22
23	let propagation = propagate_to
24		.map(Into::into)
25		.unwrap_or(Propagation::All);
26
27	let profile_value = if clear {
28		(key.as_str().into(), None)
29	} else {
30		let value = value.join(" ");
31
32		let value = serde_json::from_str(&value).unwrap_or(Value::String(value));
33
34		let profile_value = ProfileFieldValue::new(&key, value)
35			.map_err(|e| err!("Invalid value for profile key {key:?}: {e}"))?;
36
37		(profile_value.field_name(), Some(profile_value.value().into_owned()))
38	};
39
40	self.services
41		.profile
42		.set_profile_keys(&user_id, &[profile_value], Some(propagation))
43		.await?;
44
45	if clear {
46		write!(self, "Cleared profile key {key:?} for {user_id}").await
47	} else {
48		write!(self, "Set profile key {key:?} for {user_id}").await
49	}
50}
51
52/// Resolves the command's target: an active local user, or with `--clear` and
53/// no propagation a remote user this server has cached.
54///
55/// Locality is judged on the lowercased form, as `parse_active_local_user_id`
56/// does, so a mixed-case spelling of a local user never reaches the remote
57/// branch.
58async fn profile_owner(
59	services: &Services,
60	user_id: &str,
61	clear: bool,
62	propagate: bool,
63) -> Result<OwnedUserId> {
64	let local = parse_user_id(services, user_id)
65		.is_ok_and(|user_id| services.globals.user_is_local(&user_id));
66
67	let remote = OwnedUserId::parse(user_id)
68		.ok()
69		.filter(|_| !local);
70
71	match remote {
72		| None => parse_active_local_user_id(services, user_id).await,
73		| Some(_) if !clear => Err!("A remote user's profile key can only be cleared."),
74		| Some(_) if propagate => Err!("Propagation applies only to a local user's profile."),
75		| Some(user_id) => check_known_remote_user(services, &user_id)
76			.await
77			.map(|()| user_id),
78	}
79}