mirror of
https://gitlab.com/famedly/conduit.git
synced 2025-06-27 16:35:59 +00:00
Merge branch 'redact-passwords-from-admin-commands' into 'next'
Redact passwords from admin commands Closes #432 See merge request famedly/conduit!632
This commit is contained in:
commit
a74747c5e7
5 changed files with 347 additions and 143 deletions
|
@ -249,11 +249,15 @@ pub async fn register_route(body: Ruma<register::v3::Request>) -> Result<registe
|
||||||
|
|
||||||
info!("New user {} registered on this server.", user_id);
|
info!("New user {} registered on this server.", user_id);
|
||||||
if !body.from_appservice && !is_guest {
|
if !body.from_appservice && !is_guest {
|
||||||
services()
|
if let Err(err) = services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(&RoomMessageEventContent::notice_plain(format!(
|
||||||
"New user {user_id} registered on this server."
|
"New user {user_id} registered on this server."
|
||||||
)));
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is the first real user, grant them admin privileges
|
// If this is the first real user, grant them admin privileges
|
||||||
|
@ -351,11 +355,15 @@ pub async fn change_password_route(
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("User {} changed their password.", sender_user);
|
info!("User {} changed their password.", sender_user);
|
||||||
services()
|
if let Err(err) = services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(&RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} changed their password."
|
"User {sender_user} changed their password."
|
||||||
)));
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(change_password::v3::Response {})
|
Ok(change_password::v3::Response {})
|
||||||
}
|
}
|
||||||
|
@ -428,11 +436,15 @@ pub async fn deactivate_route(
|
||||||
services().users.deactivate_account(sender_user)?;
|
services().users.deactivate_account(sender_user)?;
|
||||||
|
|
||||||
info!("User {} deactivated their account.", sender_user);
|
info!("User {} deactivated their account.", sender_user);
|
||||||
services()
|
if let Err(err) = services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::notice_plain(format!(
|
.send_message(&RoomMessageEventContent::notice_plain(format!(
|
||||||
"User {sender_user} deactivated their account."
|
"User {sender_user} deactivated their account."
|
||||||
)));
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(deactivate::v3::Response {
|
Ok(deactivate::v3::Response {
|
||||||
id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport,
|
id_server_unbind_result: ThirdPartyIdRemovalStatus::NoSupport,
|
||||||
|
|
|
@ -38,8 +38,8 @@ pub async fn report_event_route(
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
services().admin
|
if let Err(err) = services().admin
|
||||||
.send_message(message::RoomMessageEventContent::text_html(
|
.send_message(&message::RoomMessageEventContent::text_html(
|
||||||
format!(
|
format!(
|
||||||
"Report received from: {}\n\n\
|
"Report received from: {}\n\n\
|
||||||
Event ID: {:?}\n\
|
Event ID: {:?}\n\
|
||||||
|
@ -63,7 +63,9 @@ pub async fn report_event_route(
|
||||||
body.score,
|
body.score,
|
||||||
HtmlEscape(body.reason.as_deref().unwrap_or(""))
|
HtmlEscape(body.reason.as_deref().unwrap_or(""))
|
||||||
),
|
),
|
||||||
));
|
)).await {
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
}
|
||||||
|
|
||||||
Ok(report_content::v3::Response {})
|
Ok(report_content::v3::Response {})
|
||||||
}
|
}
|
||||||
|
|
|
@ -972,7 +972,11 @@ impl KeyValueDatabase {
|
||||||
Ok(pwd_set) => {
|
Ok(pwd_set) => {
|
||||||
if pwd_set {
|
if pwd_set {
|
||||||
warn!("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!");
|
warn!("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!");
|
||||||
services().admin.send_message(RoomMessageEventContent::text_plain("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!"));
|
if let Err(err) = services().admin.send_message(
|
||||||
|
&RoomMessageEventContent::text_plain("The Conduit account emergency password is set! Please unset it as soon as you finish admin account recovery!")
|
||||||
|
).await {
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
|
@ -1043,12 +1047,14 @@ impl KeyValueDatabase {
|
||||||
last_update_id = last_update_id.max(update.id);
|
last_update_id = last_update_id.max(update.id);
|
||||||
if update.id > services().globals.last_check_for_updates_id()? {
|
if update.id > services().globals.last_check_for_updates_id()? {
|
||||||
println!("{}", update.message);
|
println!("{}", update.message);
|
||||||
services()
|
if let Err(err) = services()
|
||||||
.admin
|
.admin
|
||||||
.send_message(RoomMessageEventContent::text_plain(format!(
|
.send_message(&RoomMessageEventContent::text_plain(format!(
|
||||||
"@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}",
|
"@room: The following is a message from the Conduit developers. It was sent on '{}':\n\n{}",
|
||||||
update.date, update.message
|
update.date, update.message
|
||||||
)))
|
))).await {
|
||||||
|
tracing::error!("Failed to send a message to admin room: {err}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
services()
|
services()
|
||||||
|
|
|
@ -20,6 +20,7 @@ use ruma::{
|
||||||
message::RoomMessageEventContent,
|
message::RoomMessageEventContent,
|
||||||
name::RoomNameEventContent,
|
name::RoomNameEventContent,
|
||||||
power_levels::RoomPowerLevelsEventContent,
|
power_levels::RoomPowerLevelsEventContent,
|
||||||
|
redaction::RoomRedactionEventContent,
|
||||||
topic::RoomTopicEventContent,
|
topic::RoomTopicEventContent,
|
||||||
},
|
},
|
||||||
TimelineEventType,
|
TimelineEventType,
|
||||||
|
@ -182,8 +183,7 @@ enum AdminCommand {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AdminRoomEvent {
|
pub enum AdminRoomEvent {
|
||||||
ProcessMessage(String),
|
ProcessMessage(String, ruma::OwnedEventId),
|
||||||
SendMessage(RoomMessageEventContent),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Service {
|
pub struct Service {
|
||||||
|
@ -220,58 +220,150 @@ impl Service {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
Some(event) = receiver.recv() => {
|
Some(event) = receiver.recv() => {
|
||||||
let message_content = match event {
|
let message_content = match event {
|
||||||
AdminRoomEvent::SendMessage(content) => content,
|
AdminRoomEvent::ProcessMessage(room_message, event_id) => self.process_admin_message(room_message, &event_id).await
|
||||||
AdminRoomEvent::ProcessMessage(room_message) => self.process_admin_message(room_message).await
|
|
||||||
};
|
};
|
||||||
|
if let Some(message_content) = message_content {
|
||||||
|
let mutex_state = Arc::clone(
|
||||||
|
services().globals
|
||||||
|
.roomid_mutex_state
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.entry(conduit_room.to_owned())
|
||||||
|
.or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
let mutex_state = Arc::clone(
|
let state_lock = mutex_state.lock().await;
|
||||||
services().globals
|
|
||||||
.roomid_mutex_state
|
|
||||||
.write()
|
|
||||||
.await
|
|
||||||
.entry(conduit_room.to_owned())
|
|
||||||
.or_default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let state_lock = mutex_state.lock().await;
|
services()
|
||||||
|
.rooms
|
||||||
services()
|
.timeline
|
||||||
.rooms
|
.build_and_append_pdu(
|
||||||
.timeline
|
PduBuilder {
|
||||||
.build_and_append_pdu(
|
event_type: TimelineEventType::RoomMessage,
|
||||||
PduBuilder {
|
content: to_raw_value(&message_content)
|
||||||
event_type: TimelineEventType::RoomMessage,
|
.expect("event is valid, we just created it"),
|
||||||
content: to_raw_value(&message_content)
|
unsigned: None,
|
||||||
.expect("event is valid, we just created it"),
|
state_key: None,
|
||||||
unsigned: None,
|
redacts: None,
|
||||||
state_key: None,
|
},
|
||||||
redacts: None,
|
&conduit_user,
|
||||||
},
|
&conduit_room,
|
||||||
&conduit_user,
|
&state_lock,
|
||||||
&conduit_room,
|
)
|
||||||
&state_lock,
|
.await.unwrap();
|
||||||
)
|
}
|
||||||
.await.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_message(&self, room_message: String) {
|
pub fn process_message(&self, room_message: String, event_id: &EventId) {
|
||||||
self.sender
|
self.sender
|
||||||
.send(AdminRoomEvent::ProcessMessage(room_message))
|
.send(AdminRoomEvent::ProcessMessage(
|
||||||
|
room_message,
|
||||||
|
event_id.into(),
|
||||||
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn send_message(&self, message_content: RoomMessageEventContent) {
|
/// Delete user message in the conduit admin room.
|
||||||
self.sender
|
pub async fn delete_user_message(
|
||||||
.send(AdminRoomEvent::SendMessage(message_content))
|
&self,
|
||||||
.unwrap();
|
event_id: &EventId,
|
||||||
|
reason: Option<impl Into<String>>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let conduit_user =
|
||||||
|
UserId::parse_with_server_name("conduit", services().globals.server_name())
|
||||||
|
.expect("@conduit:server_name is valid");
|
||||||
|
if let Some(room_id) = services().admin.get_admin_room()? {
|
||||||
|
let mutex_state = Arc::clone(
|
||||||
|
services()
|
||||||
|
.globals
|
||||||
|
.roomid_mutex_state
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.entry(room_id.clone())
|
||||||
|
.or_default(),
|
||||||
|
);
|
||||||
|
let state_lock = mutex_state.lock().await;
|
||||||
|
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.build_and_append_pdu(
|
||||||
|
PduBuilder {
|
||||||
|
event_type: TimelineEventType::RoomRedaction,
|
||||||
|
content: to_raw_value(&RoomRedactionEventContent {
|
||||||
|
redacts: Some(event_id.into()),
|
||||||
|
reason: reason.map(Into::into),
|
||||||
|
})
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
|
unsigned: None,
|
||||||
|
state_key: None,
|
||||||
|
redacts: Some(event_id.into()),
|
||||||
|
},
|
||||||
|
&conduit_user,
|
||||||
|
&room_id,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse and process a message from the admin room
|
/// Send the message content and return it's result
|
||||||
async fn process_admin_message(&self, room_message: String) -> RoomMessageEventContent {
|
///
|
||||||
|
/// Note: Will return Ok(None) if there is no admin room
|
||||||
|
pub async fn send_message(
|
||||||
|
&self,
|
||||||
|
message_content: &RoomMessageEventContent,
|
||||||
|
) -> Result<Option<Arc<EventId>>> {
|
||||||
|
let conduit_user =
|
||||||
|
UserId::parse_with_server_name("conduit", services().globals.server_name())
|
||||||
|
.expect("@conduit:server_name is valid");
|
||||||
|
if let Some(room_id) = services().admin.get_admin_room()? {
|
||||||
|
let mutex_state = Arc::clone(
|
||||||
|
services()
|
||||||
|
.globals
|
||||||
|
.roomid_mutex_state
|
||||||
|
.write()
|
||||||
|
.await
|
||||||
|
.entry(room_id.clone())
|
||||||
|
.or_default(),
|
||||||
|
);
|
||||||
|
let state_lock = mutex_state.lock().await;
|
||||||
|
services()
|
||||||
|
.rooms
|
||||||
|
.timeline
|
||||||
|
.build_and_append_pdu(
|
||||||
|
PduBuilder {
|
||||||
|
event_type: TimelineEventType::RoomMessage,
|
||||||
|
content: to_raw_value(message_content)
|
||||||
|
.expect("event is valid, we just created it"),
|
||||||
|
unsigned: None,
|
||||||
|
state_key: None,
|
||||||
|
redacts: None,
|
||||||
|
},
|
||||||
|
&conduit_user,
|
||||||
|
&room_id,
|
||||||
|
&state_lock,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.map(Some)
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parse and process a message from the admin room
|
||||||
|
///
|
||||||
|
/// May return `Option::None` if there is no process case for the message
|
||||||
|
async fn process_admin_message(
|
||||||
|
&self,
|
||||||
|
room_message: String,
|
||||||
|
event_id: &EventId,
|
||||||
|
) -> Option<RoomMessageEventContent> {
|
||||||
let mut lines = room_message.lines().filter(|l| !l.trim().is_empty());
|
let mut lines = room_message.lines().filter(|l| !l.trim().is_empty());
|
||||||
let command_line = lines.next().expect("each string has at least one line");
|
let command_line = lines.next().expect("each string has at least one line");
|
||||||
let body: Vec<_> = lines.collect();
|
let body: Vec<_> = lines.collect();
|
||||||
|
@ -283,12 +375,16 @@ impl Service {
|
||||||
let message = error.replace("server.name", server_name.as_str());
|
let message = error.replace("server.name", server_name.as_str());
|
||||||
let html_message = self.usage_to_html(&message, server_name);
|
let html_message = self.usage_to_html(&message, server_name);
|
||||||
|
|
||||||
return RoomMessageEventContent::text_html(message, html_message);
|
return Some(RoomMessageEventContent::text_html(message, html_message));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match self.process_admin_command(admin_command, body).await {
|
match self
|
||||||
Ok(reply_message) => reply_message,
|
.process_admin_command(admin_command, body, event_id)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(Some(reply_message)) => Some(reply_message),
|
||||||
|
Ok(None) => None,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let markdown_message = format!(
|
let markdown_message = format!(
|
||||||
"Encountered an error while handling the command:\n\
|
"Encountered an error while handling the command:\n\
|
||||||
|
@ -299,7 +395,10 @@ impl Service {
|
||||||
<pre>\n{error}\n</pre>",
|
<pre>\n{error}\n</pre>",
|
||||||
);
|
);
|
||||||
|
|
||||||
RoomMessageEventContent::text_html(markdown_message, html_message)
|
Some(RoomMessageEventContent::text_html(
|
||||||
|
markdown_message,
|
||||||
|
html_message,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -326,18 +425,22 @@ impl Service {
|
||||||
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Process the entered admin command
|
||||||
|
///
|
||||||
|
/// May return `Ok(Option::None)` if there is no process case for the message
|
||||||
async fn process_admin_command(
|
async fn process_admin_command(
|
||||||
&self,
|
&self,
|
||||||
command: AdminCommand,
|
command: AdminCommand,
|
||||||
body: Vec<&str>,
|
body: Vec<&str>,
|
||||||
) -> Result<RoomMessageEventContent> {
|
event_id: &EventId,
|
||||||
|
) -> Result<Option<RoomMessageEventContent>> {
|
||||||
let reply_message_content = match command {
|
let reply_message_content = match command {
|
||||||
AdminCommand::RegisterAppservice => {
|
AdminCommand::RegisterAppservice => {
|
||||||
if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```"
|
if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```"
|
||||||
{
|
{
|
||||||
let appservice_config = body[1..body.len() - 1].join("\n");
|
let appservice_config = body[1..body.len() - 1].join("\n");
|
||||||
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
let parsed_config = serde_yaml::from_str::<Registration>(&appservice_config);
|
||||||
match parsed_config {
|
Some(match parsed_config {
|
||||||
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
Ok(yaml) => match services().appservice.register_appservice(yaml).await {
|
||||||
Ok(id) => RoomMessageEventContent::text_plain(format!(
|
Ok(id) => RoomMessageEventContent::text_plain(format!(
|
||||||
"Appservice registered with ID: {id}."
|
"Appservice registered with ID: {id}."
|
||||||
|
@ -349,25 +452,27 @@ impl Service {
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
Err(e) => RoomMessageEventContent::text_plain(format!(
|
||||||
"Could not parse appservice config: {e}"
|
"Could not parse appservice config: {e}"
|
||||||
)),
|
)),
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain(
|
Some(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::UnregisterAppservice {
|
AdminCommand::UnregisterAppservice {
|
||||||
appservice_identifier,
|
appservice_identifier,
|
||||||
} => match services()
|
} => Some(
|
||||||
.appservice
|
match services()
|
||||||
.unregister_appservice(&appservice_identifier)
|
.appservice
|
||||||
.await
|
.unregister_appservice(&appservice_identifier)
|
||||||
{
|
.await
|
||||||
Ok(()) => RoomMessageEventContent::text_plain("Appservice unregistered."),
|
{
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
Ok(()) => RoomMessageEventContent::text_plain("Appservice unregistered."),
|
||||||
"Failed to unregister appservice: {e}"
|
Err(e) => RoomMessageEventContent::text_plain(format!(
|
||||||
)),
|
"Failed to unregister appservice: {e}"
|
||||||
},
|
)),
|
||||||
|
},
|
||||||
|
),
|
||||||
AdminCommand::ListAppservices => {
|
AdminCommand::ListAppservices => {
|
||||||
let appservices = services().appservice.iter_ids().await;
|
let appservices = services().appservice.iter_ids().await;
|
||||||
let output = format!(
|
let output = format!(
|
||||||
|
@ -375,7 +480,7 @@ impl Service {
|
||||||
appservices.len(),
|
appservices.len(),
|
||||||
appservices.join(", ")
|
appservices.join(", ")
|
||||||
);
|
);
|
||||||
RoomMessageEventContent::text_plain(output)
|
Some(RoomMessageEventContent::text_plain(output))
|
||||||
}
|
}
|
||||||
AdminCommand::ListRooms => {
|
AdminCommand::ListRooms => {
|
||||||
let room_ids = services().rooms.metadata.iter_ids();
|
let room_ids = services().rooms.metadata.iter_ids();
|
||||||
|
@ -396,16 +501,16 @@ impl Service {
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
);
|
);
|
||||||
RoomMessageEventContent::text_plain(output)
|
Some(RoomMessageEventContent::text_plain(output))
|
||||||
}
|
}
|
||||||
AdminCommand::ListLocalUsers => match services().users.list_local_users() {
|
AdminCommand::ListLocalUsers => Some(match services().users.list_local_users() {
|
||||||
Ok(users) => {
|
Ok(users) => {
|
||||||
let mut msg: String = format!("Found {} local user account(s):\n", users.len());
|
let mut msg: String = format!("Found {} local user account(s):\n", users.len());
|
||||||
msg += &users.join("\n");
|
msg += &users.join("\n");
|
||||||
RoomMessageEventContent::text_plain(&msg)
|
RoomMessageEventContent::text_plain(&msg)
|
||||||
}
|
}
|
||||||
Err(e) => RoomMessageEventContent::text_plain(e.to_string()),
|
Err(e) => RoomMessageEventContent::text_plain(e.to_string()),
|
||||||
},
|
}),
|
||||||
AdminCommand::IncomingFederation => {
|
AdminCommand::IncomingFederation => {
|
||||||
let map = services().globals.roomid_federationhandletime.read().await;
|
let map = services().globals.roomid_federationhandletime.read().await;
|
||||||
let mut msg: String = format!("Handling {} incoming pdus:\n", map.len());
|
let mut msg: String = format!("Handling {} incoming pdus:\n", map.len());
|
||||||
|
@ -420,7 +525,7 @@ impl Service {
|
||||||
elapsed.as_secs() % 60
|
elapsed.as_secs() % 60
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RoomMessageEventContent::text_plain(&msg)
|
Some(RoomMessageEventContent::text_plain(&msg))
|
||||||
}
|
}
|
||||||
AdminCommand::GetAuthChain { event_id } => {
|
AdminCommand::GetAuthChain { event_id } => {
|
||||||
let event_id = Arc::<EventId>::from(event_id);
|
let event_id = Arc::<EventId>::from(event_id);
|
||||||
|
@ -441,18 +546,18 @@ impl Service {
|
||||||
.await?
|
.await?
|
||||||
.count();
|
.count();
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Loaded auth chain with length {count} in {elapsed:?}"
|
"Loaded auth chain with length {count} in {elapsed:?}"
|
||||||
))
|
)))
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain("Event not found.")
|
Some(RoomMessageEventContent::text_plain("Event not found."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::ParsePdu => {
|
AdminCommand::ParsePdu => {
|
||||||
if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```"
|
if body.len() > 2 && body[0].trim() == "```" && body.last().unwrap().trim() == "```"
|
||||||
{
|
{
|
||||||
let string = body[1..body.len() - 1].join("\n");
|
let string = body[1..body.len() - 1].join("\n");
|
||||||
match serde_json::from_str(&string) {
|
Some(match serde_json::from_str(&string) {
|
||||||
Ok(value) => {
|
Ok(value) => {
|
||||||
match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
match ruma::signatures::reference_hash(&value, &RoomVersionId::V6) {
|
||||||
Ok(hash) => {
|
Ok(hash) => {
|
||||||
|
@ -477,9 +582,11 @@ impl Service {
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
Err(e) => RoomMessageEventContent::text_plain(format!(
|
||||||
"Invalid json in command body: {e}"
|
"Invalid json in command body: {e}"
|
||||||
)),
|
)),
|
||||||
}
|
})
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain("Expected code block in command body.")
|
Some(RoomMessageEventContent::text_plain(
|
||||||
|
"Expected code block in command body.",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::GetPdu { event_id } => {
|
AdminCommand::GetPdu { event_id } => {
|
||||||
|
@ -496,7 +603,7 @@ impl Service {
|
||||||
Some(json) => {
|
Some(json) => {
|
||||||
let json_text = serde_json::to_string_pretty(&json)
|
let json_text = serde_json::to_string_pretty(&json)
|
||||||
.expect("canonical json is valid json");
|
.expect("canonical json is valid json");
|
||||||
RoomMessageEventContent::text_html(
|
Some(RoomMessageEventContent::text_html(
|
||||||
format!(
|
format!(
|
||||||
"{}\n```json\n{}\n```",
|
"{}\n```json\n{}\n```",
|
||||||
if outlier {
|
if outlier {
|
||||||
|
@ -515,32 +622,35 @@ impl Service {
|
||||||
},
|
},
|
||||||
HtmlEscape(&json_text)
|
HtmlEscape(&json_text)
|
||||||
),
|
),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
None => RoomMessageEventContent::text_plain("PDU not found."),
|
None => Some(RoomMessageEventContent::text_plain("PDU not found.")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::MemoryUsage => {
|
AdminCommand::MemoryUsage => {
|
||||||
let response1 = services().memory_usage().await;
|
let response1 = services().memory_usage().await;
|
||||||
let response2 = services().globals.db.memory_usage();
|
let response2 = services().globals.db.memory_usage();
|
||||||
|
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Services:\n{response1}\n\nDatabase:\n{response2}"
|
"Services:\n{response1}\n\nDatabase:\n{response2}"
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
AdminCommand::ClearDatabaseCaches { amount } => {
|
AdminCommand::ClearDatabaseCaches { amount } => {
|
||||||
services().globals.db.clear_caches(amount);
|
services().globals.db.clear_caches(amount);
|
||||||
|
|
||||||
RoomMessageEventContent::text_plain("Done.")
|
Some(RoomMessageEventContent::text_plain("Done."))
|
||||||
}
|
}
|
||||||
AdminCommand::ClearServiceCaches { amount } => {
|
AdminCommand::ClearServiceCaches { amount } => {
|
||||||
services().clear_caches(amount).await;
|
services().clear_caches(amount).await;
|
||||||
|
|
||||||
RoomMessageEventContent::text_plain("Done.")
|
Some(RoomMessageEventContent::text_plain("Done."))
|
||||||
}
|
}
|
||||||
AdminCommand::ShowConfig => {
|
AdminCommand::ShowConfig => {
|
||||||
// Construct and send the response
|
// Construct and send the response
|
||||||
RoomMessageEventContent::text_plain(format!("{}", services().globals.config))
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"{}",
|
||||||
|
services().globals.config
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
AdminCommand::ResetPassword { username } => {
|
AdminCommand::ResetPassword { username } => {
|
||||||
let user_id = match UserId::parse_with_server_name(
|
let user_id = match UserId::parse_with_server_name(
|
||||||
|
@ -549,17 +659,17 @@ impl Service {
|
||||||
) {
|
) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"The supplied username is not a valid username: {e}"
|
"The supplied username is not a valid username: {e}"
|
||||||
)))
|
))))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checks if user is local
|
// Checks if user is local
|
||||||
if user_id.server_name() != services().globals.server_name() {
|
if user_id.server_name() != services().globals.server_name() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(Some(RoomMessageEventContent::text_plain(
|
||||||
"The specified user is not from this server!",
|
"The specified user is not from this server!",
|
||||||
));
|
)));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check if the specified user is valid
|
// Check if the specified user is valid
|
||||||
|
@ -571,26 +681,55 @@ impl Service {
|
||||||
)
|
)
|
||||||
.expect("conduit user exists")
|
.expect("conduit user exists")
|
||||||
{
|
{
|
||||||
return Ok(RoomMessageEventContent::text_plain(
|
return Ok(Some(RoomMessageEventContent::text_plain(
|
||||||
"The specified user does not exist!",
|
"The specified user does not exist!",
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
|
let new_password = utils::random_string(AUTO_GEN_PASSWORD_LENGTH);
|
||||||
|
|
||||||
match services()
|
if let Err(err) = services()
|
||||||
.users
|
.users
|
||||||
.set_password(&user_id, Some(new_password.as_str()))
|
.set_password(&user_id, Some(new_password.as_str()))
|
||||||
{
|
{
|
||||||
Ok(()) => RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Successfully reset the password for user {user_id}: {new_password}"
|
"Couldn't reset the password for user {user_id}: {err}"
|
||||||
)),
|
)))
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
} else {
|
||||||
"Couldn't reset the password for user {user_id}: {e}"
|
// Send the reset password message to the user, we
|
||||||
)),
|
// need it's event id to delete it after 60s
|
||||||
|
let Some(sended_message_event_id) = services()
|
||||||
|
.admin
|
||||||
|
.send_message(&RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Successfully reset the password for user {user_id}: {new_password} (This message will be deleted after 60s)"
|
||||||
|
)))
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete the message after 60s because it's contain a plain password
|
||||||
|
// and the admin room are not encrypted
|
||||||
|
tokio::spawn(async move {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
|
||||||
|
if let Err(err) = services()
|
||||||
|
.admin
|
||||||
|
.delete_user_message(
|
||||||
|
sended_message_event_id.as_ref(),
|
||||||
|
Some("Message contained a plaintext password"),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::warn!(
|
||||||
|
"Couldn't delete message containing a plaintext password {err}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::CreateUser { username, password } => {
|
AdminCommand::CreateUser { username, password } => {
|
||||||
|
let is_auto_generated_password = password.is_none();
|
||||||
let password =
|
let password =
|
||||||
password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
password.unwrap_or_else(|| utils::random_string(AUTO_GEN_PASSWORD_LENGTH));
|
||||||
// Validate user id
|
// Validate user id
|
||||||
|
@ -600,20 +739,20 @@ impl Service {
|
||||||
) {
|
) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"The supplied username is not a valid username: {e}"
|
"The supplied username is not a valid username: {e}"
|
||||||
)))
|
))))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if user_id.is_historical() {
|
if user_id.is_historical() {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Userid {user_id} is not allowed due to historical"
|
"Userid {user_id} is not allowed due to historical"
|
||||||
)));
|
))));
|
||||||
}
|
}
|
||||||
if services().users.exists(&user_id)? {
|
if services().users.exists(&user_id)? {
|
||||||
return Ok(RoomMessageEventContent::text_plain(format!(
|
return Ok(Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Userid {user_id} already exists"
|
"Userid {user_id} already exists"
|
||||||
)));
|
))));
|
||||||
}
|
}
|
||||||
// Create user
|
// Create user
|
||||||
services().users.create(&user_id, Some(password.as_str()))?;
|
services().users.create(&user_id, Some(password.as_str()))?;
|
||||||
|
@ -631,6 +770,7 @@ impl Service {
|
||||||
.set_displayname(&user_id, Some(displayname))?;
|
.set_displayname(&user_id, Some(displayname))?;
|
||||||
|
|
||||||
// Initial account data
|
// Initial account data
|
||||||
|
// we dont add a device since we're not the user, just the creator
|
||||||
services().account_data.update(
|
services().account_data.update(
|
||||||
None,
|
None,
|
||||||
&user_id,
|
&user_id,
|
||||||
|
@ -645,20 +785,57 @@ impl Service {
|
||||||
.expect("to json value always works"),
|
.expect("to json value always works"),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// we dont add a device since we're not the user, just the creator
|
// We'll delete the user message because it's contain a plain password
|
||||||
|
// and the admin room are not encrypted
|
||||||
|
if !is_auto_generated_password {
|
||||||
|
services()
|
||||||
|
.admin
|
||||||
|
.delete_user_message(
|
||||||
|
event_id,
|
||||||
|
Some("Message contained a plaintext password"),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// Inhibit login does not work for guests
|
// Send the created user message to the user, we
|
||||||
RoomMessageEventContent::text_plain(format!(
|
// need it's event id to delete it after 60s
|
||||||
"Created user with user_id: {user_id} and password: {password}"
|
let Some(sended_message_event_id) = services()
|
||||||
))
|
.admin
|
||||||
|
.send_message(&RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Created user with user_id: {user_id} and password: {password} (This message will be deleted after 60s)"
|
||||||
|
)))
|
||||||
|
.await?
|
||||||
|
else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Delete the message after 60s because it's contain a plain password
|
||||||
|
// and the admin room are not encrypted
|
||||||
|
tokio::spawn(async move {
|
||||||
|
tokio::time::sleep(tokio::time::Duration::from_secs(60)).await;
|
||||||
|
if let Err(err) = services()
|
||||||
|
.admin
|
||||||
|
.delete_user_message(
|
||||||
|
sended_message_event_id.as_ref(),
|
||||||
|
Some("Message contained a plaintext password"),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
tracing::warn!(
|
||||||
|
"Couldn't delete message containing a plaintext password {err}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
None
|
||||||
}
|
}
|
||||||
AdminCommand::DisableRoom { room_id } => {
|
AdminCommand::DisableRoom { room_id } => {
|
||||||
services().rooms.metadata.disable_room(&room_id, true)?;
|
services().rooms.metadata.disable_room(&room_id, true)?;
|
||||||
RoomMessageEventContent::text_plain("Room disabled.")
|
Some(RoomMessageEventContent::text_plain("Room disabled."))
|
||||||
}
|
}
|
||||||
AdminCommand::EnableRoom { room_id } => {
|
AdminCommand::EnableRoom { room_id } => {
|
||||||
services().rooms.metadata.disable_room(&room_id, false)?;
|
services().rooms.metadata.disable_room(&room_id, false)?;
|
||||||
RoomMessageEventContent::text_plain("Room enabled.")
|
Some(RoomMessageEventContent::text_plain("Room enabled."))
|
||||||
}
|
}
|
||||||
AdminCommand::DeactivateUser {
|
AdminCommand::DeactivateUser {
|
||||||
leave_rooms,
|
leave_rooms,
|
||||||
|
@ -666,14 +843,15 @@ impl Service {
|
||||||
} => {
|
} => {
|
||||||
let user_id = Arc::<UserId>::from(user_id);
|
let user_id = Arc::<UserId>::from(user_id);
|
||||||
if !services().users.exists(&user_id)? {
|
if !services().users.exists(&user_id)? {
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"User {user_id} doesn't exist on this server"
|
"User {user_id} doesn't exist on this server"
|
||||||
))
|
)))
|
||||||
} else if user_id.server_name() != services().globals.server_name() {
|
} else if user_id.server_name() != services().globals.server_name() {
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"User {user_id} is not from this server"
|
"User {user_id} is not from this server"
|
||||||
))
|
)))
|
||||||
} else {
|
} else {
|
||||||
|
// FIXME: Why this is here!
|
||||||
RoomMessageEventContent::text_plain(format!(
|
RoomMessageEventContent::text_plain(format!(
|
||||||
"Making {user_id} leave all rooms before deactivation..."
|
"Making {user_id} leave all rooms before deactivation..."
|
||||||
));
|
));
|
||||||
|
@ -684,9 +862,9 @@ impl Service {
|
||||||
leave_all_rooms(&user_id).await?;
|
leave_all_rooms(&user_id).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"User {user_id} has been deactivated"
|
"User {user_id} has been deactivated"
|
||||||
))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::DeactivateAll { leave_rooms, force } => {
|
AdminCommand::DeactivateAll { leave_rooms, force } => {
|
||||||
|
@ -751,10 +929,10 @@ impl Service {
|
||||||
html_message.push_str("</pre>\n\n");
|
html_message.push_str("</pre>\n\n");
|
||||||
}
|
}
|
||||||
if !markdown_message.is_empty() {
|
if !markdown_message.is_empty() {
|
||||||
return Ok(RoomMessageEventContent::text_html(
|
return Ok(Some(RoomMessageEventContent::text_html(
|
||||||
markdown_message,
|
markdown_message,
|
||||||
html_message,
|
html_message,
|
||||||
));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut deactivation_count = 0;
|
let mut deactivation_count = 0;
|
||||||
|
@ -786,16 +964,16 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
if admins.is_empty() {
|
if admins.is_empty() {
|
||||||
RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Deactivated {deactivation_count} accounts."
|
"Deactivated {deactivation_count} accounts."
|
||||||
))
|
)))
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain(format!("Deactivated {} accounts.\nSkipped admin accounts: {:?}. Use --force to deactivate admin accounts", deactivation_count, admins.join(", ")))
|
Some(RoomMessageEventContent::text_plain(format!("Deactivated {} accounts.\nSkipped admin accounts: {:?}. Use --force to deactivate admin accounts", deactivation_count, admins.join(", "))))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain(
|
Some(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::SignJson => {
|
AdminCommand::SignJson => {
|
||||||
|
@ -812,14 +990,16 @@ impl Service {
|
||||||
.expect("our request json is what ruma expects");
|
.expect("our request json is what ruma expects");
|
||||||
let json_text = serde_json::to_string_pretty(&value)
|
let json_text = serde_json::to_string_pretty(&value)
|
||||||
.expect("canonical json is valid json");
|
.expect("canonical json is valid json");
|
||||||
RoomMessageEventContent::text_plain(json_text)
|
Some(RoomMessageEventContent::text_plain(json_text))
|
||||||
}
|
}
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")),
|
Err(e) => Some(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Invalid json: {e}"
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain(
|
Some(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AdminCommand::VerifyJson => {
|
AdminCommand::VerifyJson => {
|
||||||
|
@ -838,18 +1018,22 @@ impl Service {
|
||||||
|
|
||||||
let pub_key_map = pub_key_map.read().await;
|
let pub_key_map = pub_key_map.read().await;
|
||||||
match ruma::signatures::verify_json(&pub_key_map, &value) {
|
match ruma::signatures::verify_json(&pub_key_map, &value) {
|
||||||
Ok(_) => RoomMessageEventContent::text_plain("Signature correct"),
|
Ok(_) => {
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!(
|
Some(RoomMessageEventContent::text_plain("Signature correct"))
|
||||||
|
}
|
||||||
|
Err(e) => Some(RoomMessageEventContent::text_plain(format!(
|
||||||
"Signature verification failed: {e}"
|
"Signature verification failed: {e}"
|
||||||
)),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => RoomMessageEventContent::text_plain(format!("Invalid json: {e}")),
|
Err(e) => Some(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Invalid json: {e}"
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
RoomMessageEventContent::text_plain(
|
Some(RoomMessageEventContent::text_plain(
|
||||||
"Expected code block in command body. Add --help for details.",
|
"Expected code block in command body. Add --help for details.",
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -499,7 +499,7 @@ impl Service {
|
||||||
|
|
||||||
if let Some(admin_room) = services().admin.get_admin_room()? {
|
if let Some(admin_room) = services().admin.get_admin_room()? {
|
||||||
if to_conduit && !from_conduit && admin_room == pdu.room_id {
|
if to_conduit && !from_conduit && admin_room == pdu.room_id {
|
||||||
services().admin.process_message(body);
|
services().admin.process_message(body, &pdu.event_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue