Admin room alias commands
- room alias set - room alias remove - room alias which - room alias list
This commit is contained in:
parent
6fdeec1108
commit
ebb94341c8
4 changed files with 245 additions and 7 deletions
|
@ -57,4 +57,28 @@ impl service::rooms::alias::Data for KeyValueDatabase {
|
||||||
.map_err(|_| Error::bad_database("Invalid alias in aliasid_alias."))
|
.map_err(|_| Error::bad_database("Invalid alias in aliasid_alias."))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn all_local_aliases<'a>(
|
||||||
|
&'a self,
|
||||||
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, String)>> + 'a> {
|
||||||
|
Box::new(
|
||||||
|
self.alias_roomid
|
||||||
|
.iter()
|
||||||
|
.map(|(room_alias_bytes, room_id_bytes)| {
|
||||||
|
let room_alias_localpart = utils::string_from_bytes(&room_alias_bytes)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid alias bytes in aliasid_alias.")
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let room_id = utils::string_from_bytes(&room_id_bytes)
|
||||||
|
.map_err(|_| {
|
||||||
|
Error::bad_database("Invalid room_id bytes in aliasid_alias.")
|
||||||
|
})?
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| Error::bad_database("Invalid room_id in aliasid_alias."))?;
|
||||||
|
|
||||||
|
Ok((room_id, room_alias_localpart))
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,7 @@ use super::pdu::PduBuilder;
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[cfg_attr(test, derive(Debug))]
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
|
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
|
||||||
|
// TODO: bikeshedding - should command names be singular or plural
|
||||||
enum AdminCommand {
|
enum AdminCommand {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
/// Commands for managing appservices
|
/// Commands for managing appservices
|
||||||
|
@ -168,6 +169,45 @@ enum UserCommand {
|
||||||
enum RoomCommand {
|
enum RoomCommand {
|
||||||
/// List all rooms the server knows about
|
/// List all rooms the server knows about
|
||||||
List,
|
List,
|
||||||
|
|
||||||
|
#[command(subcommand)]
|
||||||
|
/// Manage rooms' aliases
|
||||||
|
Alias(RoomAliasCommand),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
#[derive(Subcommand)]
|
||||||
|
enum RoomAliasCommand {
|
||||||
|
/// Make an alias point to a room.
|
||||||
|
Set {
|
||||||
|
#[arg(short, long)]
|
||||||
|
/// Set the alias even if a room is already using it
|
||||||
|
force: bool,
|
||||||
|
|
||||||
|
// The room id to set the alias on
|
||||||
|
room_id: Box<RoomId>,
|
||||||
|
|
||||||
|
// The alias localpart to use (`alias`, not `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: Box<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Remove an alias
|
||||||
|
Remove {
|
||||||
|
/// The alias localpart to remove (`alias`, not `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: Box<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Show which room is using an alias
|
||||||
|
Which {
|
||||||
|
/// The alias localpart to look up (`alias`, not `#alias:servername.tld`)
|
||||||
|
room_alias_localpart: Box<String>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// List aliases currently being used
|
||||||
|
List {
|
||||||
|
/// If set, only list the aliases for this room
|
||||||
|
room_id: Option<Box<RoomId>>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(test, derive(Debug))]
|
#[cfg_attr(test, derive(Debug))]
|
||||||
|
@ -444,17 +484,19 @@ impl Service {
|
||||||
"Failed to unregister appservice: {e}"
|
"Failed to unregister appservice: {e}"
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
AppserviceCommand::Show { appservice_identifier } => {
|
AppserviceCommand::Show {
|
||||||
|
appservice_identifier,
|
||||||
|
} => {
|
||||||
match services()
|
match services()
|
||||||
.appservice
|
.appservice
|
||||||
.get_registration(&appservice_identifier) {
|
.get_registration(&appservice_identifier)
|
||||||
|
{
|
||||||
Ok(Some(config)) => {
|
Ok(Some(config)) => {
|
||||||
let config_str = serde_yaml::to_string(&config)
|
let config_str = serde_yaml::to_string(&config)
|
||||||
.expect("config should've been validated on register");
|
.expect("config should've been validated on register");
|
||||||
let output = format!(
|
let output = format!(
|
||||||
"Config for {}:\n\n```yaml\n{}\n```",
|
"Config for {}:\n\n```yaml\n{}\n```",
|
||||||
appservice_identifier,
|
appservice_identifier, config_str,
|
||||||
config_str,
|
|
||||||
);
|
);
|
||||||
let output_html = format!(
|
let output_html = format!(
|
||||||
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
|
"Config for {}:\n\n<pre><code class=\"language-yaml\">{}</code></pre>",
|
||||||
|
@ -462,8 +504,10 @@ impl Service {
|
||||||
escape_html(&config_str),
|
escape_html(&config_str),
|
||||||
);
|
);
|
||||||
RoomMessageEventContent::text_html(output, output_html)
|
RoomMessageEventContent::text_html(output, output_html)
|
||||||
},
|
}
|
||||||
Ok(None) => RoomMessageEventContent::text_plain("Appservice does not exist."),
|
Ok(None) => {
|
||||||
|
RoomMessageEventContent::text_plain("Appservice does not exist.")
|
||||||
|
}
|
||||||
Err(_) => RoomMessageEventContent::text_plain("Failed to get appservice."),
|
Err(_) => RoomMessageEventContent::text_plain("Failed to get appservice."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -711,6 +755,162 @@ impl Service {
|
||||||
);
|
);
|
||||||
RoomMessageEventContent::text_plain(output)
|
RoomMessageEventContent::text_plain(output)
|
||||||
}
|
}
|
||||||
|
// TODO: clean up and deduplicate code
|
||||||
|
RoomCommand::Alias(command) => match command {
|
||||||
|
RoomAliasCommand::Set {
|
||||||
|
ref room_alias_localpart,
|
||||||
|
..
|
||||||
|
}
|
||||||
|
| RoomAliasCommand::Remove {
|
||||||
|
ref room_alias_localpart,
|
||||||
|
}
|
||||||
|
| RoomAliasCommand::Which {
|
||||||
|
ref room_alias_localpart,
|
||||||
|
} => {
|
||||||
|
let room_alias_str = format!(
|
||||||
|
"#{}:{}",
|
||||||
|
room_alias_localpart,
|
||||||
|
services().globals.server_name()
|
||||||
|
);
|
||||||
|
let room_alias = match RoomAliasId::parse_box(room_alias_str) {
|
||||||
|
Ok(alias) => alias,
|
||||||
|
Err(err) => {
|
||||||
|
return Ok(RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Failed to parse alias: {}",
|
||||||
|
err
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match command {
|
||||||
|
RoomAliasCommand::Set { force, room_id, .. } => {
|
||||||
|
match (force, services().rooms.alias.resolve_local_alias(&room_alias)) {
|
||||||
|
(true, Ok(Some(id))) => match services().rooms.alias.set_alias(&room_alias, &room_id) {
|
||||||
|
Ok(()) => RoomMessageEventContent::text_plain(format!("Successfully overwrote alias (formerly {})", id)),
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)),
|
||||||
|
}
|
||||||
|
(false, Ok(Some(id))) => {
|
||||||
|
RoomMessageEventContent::text_plain(format!("Refusing to overwrite in use alias for {}, use -f or --force to overwrite", id))
|
||||||
|
}
|
||||||
|
(_, Ok(None)) => match services().rooms.alias.set_alias(&room_alias, &room_id) {
|
||||||
|
Ok(()) => RoomMessageEventContent::text_plain("Successfully set alias"),
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(format!("Failed to remove alias: {}", err)),
|
||||||
|
}
|
||||||
|
(_, Err(err)) => RoomMessageEventContent::text_plain(format!("Unable to lookup alias: {}", err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomAliasCommand::Remove { .. } => {
|
||||||
|
match services().rooms.alias.resolve_local_alias(&room_alias) {
|
||||||
|
Ok(Some(id)) => {
|
||||||
|
match services().rooms.alias.remove_alias(&room_alias) {
|
||||||
|
Ok(()) => RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Removed alias from {}",
|
||||||
|
id
|
||||||
|
)),
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(
|
||||||
|
format!("Failed to remove alias: {}", err),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(None) => {
|
||||||
|
RoomMessageEventContent::text_plain("Alias isn't in use.")
|
||||||
|
}
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Unable to lookup alias: {}",
|
||||||
|
err
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomAliasCommand::Which { .. } => {
|
||||||
|
match services().rooms.alias.resolve_local_alias(&room_alias) {
|
||||||
|
Ok(Some(id)) => RoomMessageEventContent::text_plain(format!(
|
||||||
|
"Alias resolves to {}",
|
||||||
|
id
|
||||||
|
)),
|
||||||
|
Ok(None) => {
|
||||||
|
RoomMessageEventContent::text_plain("Alias isn't in use.")
|
||||||
|
}
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(&format!(
|
||||||
|
"Unable to lookup alias: {}",
|
||||||
|
err
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomAliasCommand::List { .. } => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RoomAliasCommand::List { room_id } => match room_id {
|
||||||
|
Some(room_id) => {
|
||||||
|
let aliases: Result<Vec<_>, _> = services()
|
||||||
|
.rooms
|
||||||
|
.alias
|
||||||
|
.local_aliases_for_room(&room_id)
|
||||||
|
.collect();
|
||||||
|
match aliases {
|
||||||
|
Ok(aliases) => {
|
||||||
|
let plain_list: String = aliases
|
||||||
|
.iter()
|
||||||
|
.map(|alias| format!("- {}\n", alias))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let html_list: String = aliases
|
||||||
|
.iter()
|
||||||
|
.map(|alias| {
|
||||||
|
format!(
|
||||||
|
"<li>{}</li>\n",
|
||||||
|
escape_html(&alias.to_string())
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let plain = format!("Aliases for {}:\n{}", room_id, plain_list);
|
||||||
|
let html =
|
||||||
|
format!("Aliases for {}:\n<ul>{}</ul>", room_id, html_list);
|
||||||
|
RoomMessageEventContent::text_html(plain, html)
|
||||||
|
}
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(&format!(
|
||||||
|
"Unable to list aliases: {}",
|
||||||
|
err
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let aliases: Result<Vec<_>, _> =
|
||||||
|
services().rooms.alias.all_local_aliases().collect();
|
||||||
|
match aliases {
|
||||||
|
Ok(aliases) => {
|
||||||
|
let server_name = services().globals.server_name();
|
||||||
|
let plain_list: String = aliases
|
||||||
|
.iter()
|
||||||
|
.map(|(id, alias)| {
|
||||||
|
format!("- #{}:{} -> {}\n", alias, server_name, id)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let html_list: String = aliases
|
||||||
|
.iter()
|
||||||
|
.map(|(id, alias)| {
|
||||||
|
format!(
|
||||||
|
"<li>#{}:{} -> {}</li>\n",
|
||||||
|
escape_html(&alias.to_string()),
|
||||||
|
server_name,
|
||||||
|
escape_html(&id.to_string())
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let plain = format!("Aliases:\n{}", plain_list);
|
||||||
|
let html = format!("Aliases:\n<ul>{}</ul>", html_list);
|
||||||
|
RoomMessageEventContent::text_html(plain, html)
|
||||||
|
}
|
||||||
|
Err(err) => RoomMessageEventContent::text_plain(&format!(
|
||||||
|
"Unable to list aliases: {}",
|
||||||
|
err
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
AdminCommand::Federation(command) => match command {
|
AdminCommand::Federation(command) => match command {
|
||||||
FederationCommand::DisableRoom { room_id } => {
|
FederationCommand::DisableRoom { room_id } => {
|
||||||
|
@ -1387,7 +1587,9 @@ impl Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_html(s: &str) -> String {
|
fn escape_html(s: &str) -> String {
|
||||||
s.replace('&', "&").replace('<', "<").replace('>', ">")
|
s.replace('&', "&")
|
||||||
|
.replace('<', "<")
|
||||||
|
.replace('>', ">")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -16,4 +16,9 @@ pub trait Data: Send + Sync {
|
||||||
&'a self,
|
&'a self,
|
||||||
room_id: &RoomId,
|
room_id: &RoomId,
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a>;
|
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a>;
|
||||||
|
|
||||||
|
/// Returns all local aliases on the server
|
||||||
|
fn all_local_aliases<'a>(
|
||||||
|
&'a self,
|
||||||
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, String)>> + 'a>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,11 @@ impl Service {
|
||||||
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a> {
|
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a> {
|
||||||
self.db.local_aliases_for_room(room_id)
|
self.db.local_aliases_for_room(room_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(self))]
|
||||||
|
pub fn all_local_aliases<'a>(
|
||||||
|
&'a self,
|
||||||
|
) -> Box<dyn Iterator<Item = Result<(OwnedRoomId, String)>> + 'a> {
|
||||||
|
self.db.all_local_aliases()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue