1
0
Fork 0
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:
Awiteb 2024-04-24 19:37:25 +00:00
commit a74747c5e7
5 changed files with 347 additions and 143 deletions

View file

@ -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,

View file

@ -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 {})
} }

View file

@ -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()

View file

@ -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.",
) ))
} }
} }
}; };

View file

@ -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);
} }
} }
} }