Admin room alias commands

- room alias set
- room alias remove
- room alias which
- room alias list
This commit is contained in:
tezlm 2023-10-03 20:42:31 -07:00
parent 99fb0a2953
commit 910d166b5d
No known key found for this signature in database
GPG key ID: 649733FCD94AFBBA
4 changed files with 158 additions and 0 deletions

View file

@ -57,4 +57,20 @@ impl service::rooms::alias::Data for KeyValueDatabase {
.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))
}))
}
}

View file

@ -40,6 +40,7 @@ use super::pdu::PduBuilder;
#[cfg_attr(test, derive(Debug))]
#[derive(Parser)]
#[command(name = "@conduit:server.name:", version = env!("CARGO_PKG_VERSION"))]
// TODO: bikeshedding - should command names be singular or plural
enum AdminCommand {
#[command(subcommand)]
/// Commands for managing appservices
@ -168,6 +169,45 @@ enum UserCommand {
enum RoomCommand {
/// List all rooms the server knows about
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))]
@ -709,6 +749,96 @@ impl Service {
);
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 {
FederationCommand::DisableRoom { room_id } => {

View file

@ -16,4 +16,9 @@ pub trait Data: Send + Sync {
&'a self,
room_id: &RoomId,
) -> 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>;
}

View file

@ -32,4 +32,11 @@ impl Service {
) -> Box<dyn Iterator<Item = Result<OwnedRoomAliasId>> + 'a> {
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()
}
}