Initial commit
This commit is contained in:
63
src/controller/access/determination/mod.rs
Normal file
63
src/controller/access/determination/mod.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
use super::super::ControllerType;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum AccessControllerType {
|
||||
Help,
|
||||
|
||||
GetUsers,
|
||||
SetUsers(Option<Vec<String>>),
|
||||
|
||||
GetRoomLocalAgentManagers,
|
||||
SetRoomLocalAgentManagers(Option<Vec<String>>),
|
||||
}
|
||||
|
||||
pub fn determine_controller(text: &str) -> ControllerType {
|
||||
if text.starts_with("users") {
|
||||
return ControllerType::Access(AccessControllerType::GetUsers);
|
||||
}
|
||||
|
||||
if let Some(patterns_string) = text.strip_prefix("set-users") {
|
||||
let patterns_string = patterns_string.trim().to_owned();
|
||||
|
||||
let patterns_option = if patterns_string.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let patterns_vector = patterns_string
|
||||
.split(" ")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
Some(patterns_vector)
|
||||
};
|
||||
|
||||
return ControllerType::Access(AccessControllerType::SetUsers(patterns_option));
|
||||
}
|
||||
|
||||
if text.starts_with("room-local-agent-managers") {
|
||||
return ControllerType::Access(AccessControllerType::GetRoomLocalAgentManagers);
|
||||
}
|
||||
|
||||
if let Some(patterns_string) = text.strip_prefix("set-room-local-agent-managers") {
|
||||
let patterns_string = patterns_string.trim().to_owned();
|
||||
|
||||
let patterns_option = if patterns_string.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let patterns_vector = patterns_string
|
||||
.split(" ")
|
||||
.map(|s| s.to_string())
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
Some(patterns_vector)
|
||||
};
|
||||
|
||||
return ControllerType::Access(AccessControllerType::SetRoomLocalAgentManagers(
|
||||
patterns_option,
|
||||
));
|
||||
}
|
||||
|
||||
ControllerType::Access(AccessControllerType::Help)
|
||||
}
|
||||
58
src/controller/access/determination/tests.rs
Normal file
58
src/controller/access/determination/tests.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
#[test]
|
||||
fn determine_controller() {
|
||||
struct TestCase {
|
||||
name: &'static str,
|
||||
input: &'static str,
|
||||
expected: super::ControllerType,
|
||||
}
|
||||
|
||||
let test_cases = vec![
|
||||
TestCase {
|
||||
name: "Top-level is help",
|
||||
input: "",
|
||||
expected: super::ControllerType::Access(super::AccessControllerType::Help),
|
||||
},
|
||||
TestCase {
|
||||
name: "Anything else goes to top-level",
|
||||
input: "whatever",
|
||||
expected: super::ControllerType::Access(super::AccessControllerType::Help),
|
||||
},
|
||||
TestCase {
|
||||
name: "Users",
|
||||
input: "users",
|
||||
expected: super::ControllerType::Access(super::AccessControllerType::GetUsers),
|
||||
},
|
||||
TestCase {
|
||||
name: "Set-users",
|
||||
input: "set-users @user:example.com @bot.*:example.org",
|
||||
expected: super::ControllerType::Access(super::AccessControllerType::SetUsers(Some(
|
||||
vec![
|
||||
"@user:example.com".to_owned(),
|
||||
"@bot.*:example.org".to_owned(),
|
||||
],
|
||||
))),
|
||||
},
|
||||
TestCase {
|
||||
name: "Room-local-agent-managers",
|
||||
input: "room-local-agent-managers",
|
||||
expected: super::ControllerType::Access(
|
||||
super::AccessControllerType::GetRoomLocalAgentManagers,
|
||||
),
|
||||
},
|
||||
TestCase {
|
||||
name: "Set-room-local-agent-managers",
|
||||
input: "set-room-local-agent-managers @user:example.com @bot.*:example.org",
|
||||
expected: super::ControllerType::Access(
|
||||
super::AccessControllerType::SetRoomLocalAgentManagers(Some(vec![
|
||||
"@user:example.com".to_owned(),
|
||||
"@bot.*:example.org".to_owned(),
|
||||
])),
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
for test_case in test_cases {
|
||||
let result = super::determine_controller(test_case.input);
|
||||
assert_eq!(result, test_case.expected, "Test case: {}", test_case.name);
|
||||
}
|
||||
}
|
||||
45
src/controller/access/dispatching.rs
Normal file
45
src/controller/access/dispatching.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{entity::MessageContext, strings, Bot};
|
||||
|
||||
use super::AccessControllerType;
|
||||
|
||||
pub async fn dispatch_controller(
|
||||
handler: &AccessControllerType,
|
||||
message_context: &MessageContext,
|
||||
bot: &Bot,
|
||||
) -> anyhow::Result<()> {
|
||||
// Only the help command is available without access control, so that all users can get familiar with how the bot's access system works.
|
||||
match handler {
|
||||
AccessControllerType::Help => {}
|
||||
_ => {
|
||||
if !message_context.sender_can_manage_global_config()? {
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
strings::global_config::no_permissions_to_administrate(),
|
||||
MessageResponseType::Reply(
|
||||
message_context.thread_info().root_event_id.clone(),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
match handler {
|
||||
AccessControllerType::Help => super::help::handle(bot, message_context).await,
|
||||
AccessControllerType::GetUsers => super::users::handle_get(bot, message_context).await,
|
||||
AccessControllerType::SetUsers(patterns) => {
|
||||
super::users::handle_set(bot, message_context, patterns).await
|
||||
}
|
||||
AccessControllerType::GetRoomLocalAgentManagers => {
|
||||
super::room_local_agent_managers::handle_get(bot, message_context).await
|
||||
}
|
||||
AccessControllerType::SetRoomLocalAgentManagers(patterns) => {
|
||||
super::room_local_agent_managers::handle_set(bot, message_context, patterns).await
|
||||
}
|
||||
}
|
||||
}
|
||||
183
src/controller/access/help.rs
Normal file
183
src/controller/access/help.rs
Normal file
@@ -0,0 +1,183 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{entity::MessageContext, strings, Bot};
|
||||
|
||||
pub async fn handle(bot: &Bot, message_context: &MessageContext) -> anyhow::Result<()> {
|
||||
let mut message = String::new();
|
||||
message.push_str(&build_section_intro());
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&build_section_joining_rooms());
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&build_section_users(
|
||||
bot.command_prefix(),
|
||||
bot.homeserver_name(),
|
||||
message_context,
|
||||
));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&build_section_administrators(bot.admin_patterns()));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&build_section_room_local_agent_managers(
|
||||
bot.command_prefix(),
|
||||
bot.homeserver_name(),
|
||||
message_context,
|
||||
));
|
||||
|
||||
bot.messaging()
|
||||
.send_text_markdown_no_fail(
|
||||
message_context.room(),
|
||||
message,
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn build_section_intro() -> String {
|
||||
let mut message = String::new();
|
||||
|
||||
message.push_str(&format!("## {}", strings::help::access::heading()));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::intro());
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
fn build_section_joining_rooms() -> String {
|
||||
let mut message = String::new();
|
||||
|
||||
message.push_str(&format!(
|
||||
"### {}",
|
||||
strings::help::access::room_auto_join_heading()
|
||||
));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::room_auto_join_intro());
|
||||
message.push_str("\n\n");
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
fn build_section_users(
|
||||
command_prefix: &str,
|
||||
homeserver_name: &str,
|
||||
message_context: &MessageContext,
|
||||
) -> String {
|
||||
let mut message = String::new();
|
||||
|
||||
message.push_str(&format!("### {}", strings::help::access::users_heading()));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::users_intro());
|
||||
message.push('\n');
|
||||
message.push_str(&strings::help::access::users_access());
|
||||
message.push_str("\n\n");
|
||||
if let Some(user_patterns) = &message_context.global_config().access.user_patterns {
|
||||
if user_patterns.is_empty() {
|
||||
message.push_str(&strings::access::users_no_patterns());
|
||||
} else {
|
||||
message.push_str(&strings::access::users_now_match_patterns(user_patterns));
|
||||
}
|
||||
} else {
|
||||
message.push_str(&strings::access::users_no_patterns());
|
||||
}
|
||||
|
||||
let can_manage_global_config = message_context.sender_can_manage_global_config();
|
||||
if let Ok(can_manage_global_config) = can_manage_global_config {
|
||||
if can_manage_global_config {
|
||||
message.push_str("\n\n");
|
||||
|
||||
message.push_str(strings::the_following_commands_are_available());
|
||||
message.push('\n');
|
||||
|
||||
message.push_str(&strings::help::access::users_command_get(command_prefix));
|
||||
message.push('\n');
|
||||
|
||||
message.push_str(&strings::help::access::users_command_set(command_prefix));
|
||||
message.push_str("\n\n");
|
||||
|
||||
message.push_str(&strings::help::access::example_user_patterns(
|
||||
homeserver_name,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
fn build_section_administrators(admin_patterns: &[String]) -> String {
|
||||
let mut message = String::new();
|
||||
|
||||
message.push_str(&format!(
|
||||
"### {}",
|
||||
strings::help::access::administrators_heading()
|
||||
));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::administrators_intro());
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::administrators_now_match_patterns(
|
||||
admin_patterns,
|
||||
));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::administrators_outro());
|
||||
|
||||
message
|
||||
}
|
||||
|
||||
fn build_section_room_local_agent_managers(
|
||||
command_prefix: &str,
|
||||
homeserver_name: &str,
|
||||
message_context: &MessageContext,
|
||||
) -> String {
|
||||
let mut message = String::new();
|
||||
|
||||
message.push_str(&format!(
|
||||
"### {}",
|
||||
strings::help::access::room_local_agent_managers_heading()
|
||||
));
|
||||
message.push_str("\n\n");
|
||||
message.push_str(&strings::help::access::room_local_agent_managers_intro(
|
||||
command_prefix,
|
||||
));
|
||||
message.push('\n');
|
||||
message.push_str(&strings::help::access::room_local_agent_managers_security_warning());
|
||||
message.push_str("\n\n");
|
||||
if let Some(user_patterns) = &message_context
|
||||
.global_config()
|
||||
.access
|
||||
.room_local_agent_manager_patterns
|
||||
{
|
||||
if user_patterns.is_empty() {
|
||||
message.push_str(&strings::access::room_local_agent_managers_no_patterns());
|
||||
} else {
|
||||
message.push_str(
|
||||
&strings::access::room_local_agent_managers_now_match_patterns(user_patterns),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
message.push_str(&strings::access::room_local_agent_managers_no_patterns());
|
||||
}
|
||||
|
||||
let can_manage_global_config = message_context.sender_can_manage_global_config();
|
||||
if let Ok(can_manage_global_config) = can_manage_global_config {
|
||||
if can_manage_global_config {
|
||||
message.push_str("\n\n");
|
||||
message.push_str(strings::the_following_commands_are_available());
|
||||
message.push('\n');
|
||||
|
||||
message.push_str(
|
||||
&strings::help::access::room_local_agent_managers_command_get(command_prefix),
|
||||
);
|
||||
message.push('\n');
|
||||
|
||||
message.push_str(
|
||||
&strings::help::access::room_local_agent_managers_command_set(command_prefix),
|
||||
);
|
||||
message.push_str("\n\n");
|
||||
|
||||
message.push_str(&strings::help::access::example_user_patterns(
|
||||
homeserver_name,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
message
|
||||
}
|
||||
8
src/controller/access/mod.rs
Normal file
8
src/controller/access/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
mod determination;
|
||||
mod dispatching;
|
||||
pub mod help;
|
||||
mod room_local_agent_managers;
|
||||
mod users;
|
||||
|
||||
pub use determination::{determine_controller, AccessControllerType};
|
||||
pub use dispatching::dispatch_controller;
|
||||
67
src/controller/access/room_local_agent_managers.rs
Normal file
67
src/controller/access/room_local_agent_managers.rs
Normal file
@@ -0,0 +1,67 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{entity::MessageContext, strings, Bot};
|
||||
|
||||
pub async fn handle_get(bot: &Bot, message_context: &MessageContext) -> anyhow::Result<()> {
|
||||
let message = match &message_context
|
||||
.global_config()
|
||||
.access
|
||||
.room_local_agent_manager_patterns
|
||||
{
|
||||
Some(patterns) => strings::access::room_local_agent_managers_now_match_patterns(patterns),
|
||||
None => strings::access::room_local_agent_managers_no_patterns(),
|
||||
};
|
||||
|
||||
bot.messaging()
|
||||
.send_text_markdown_no_fail(
|
||||
message_context.room(),
|
||||
message,
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_set(
|
||||
bot: &Bot,
|
||||
message_context: &MessageContext,
|
||||
patterns: &Option<Vec<String>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(patterns) = patterns {
|
||||
if let Err(err) = mxidwc::parse_patterns_vector(patterns) {
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&strings::access::failed_to_parse_patterns(&err.to_string()),
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut global_config_manager_guard = bot.global_config_manager().lock().await;
|
||||
|
||||
let mut global_config = global_config_manager_guard.get_or_create().await?;
|
||||
|
||||
global_config.access.room_local_agent_manager_patterns = patterns.clone();
|
||||
|
||||
global_config_manager_guard.persist(&global_config).await?;
|
||||
|
||||
let message = match patterns {
|
||||
Some(patterns) => strings::access::room_local_agent_managers_now_match_patterns(patterns),
|
||||
None => strings::access::room_local_agent_managers_no_patterns(),
|
||||
};
|
||||
|
||||
bot.messaging()
|
||||
.send_success_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&message,
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
63
src/controller/access/users.rs
Normal file
63
src/controller/access/users.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use mxlink::MessageResponseType;
|
||||
|
||||
use crate::{entity::MessageContext, strings, Bot};
|
||||
|
||||
pub async fn handle_get(bot: &Bot, message_context: &MessageContext) -> anyhow::Result<()> {
|
||||
let message = match &message_context.global_config().access.user_patterns {
|
||||
Some(patterns) => strings::access::users_now_match_patterns(patterns),
|
||||
None => strings::access::users_no_patterns(),
|
||||
};
|
||||
|
||||
bot.messaging()
|
||||
.send_text_markdown_no_fail(
|
||||
message_context.room(),
|
||||
message,
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn handle_set(
|
||||
bot: &Bot,
|
||||
message_context: &MessageContext,
|
||||
patterns: &Option<Vec<String>>,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some(patterns) = patterns {
|
||||
if let Err(err) = mxidwc::parse_patterns_vector(patterns) {
|
||||
bot.messaging()
|
||||
.send_error_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&strings::access::failed_to_parse_patterns(&err.to_string()),
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let mut global_config_manager_guard = bot.global_config_manager().lock().await;
|
||||
|
||||
let mut global_config = global_config_manager_guard.get_or_create().await?;
|
||||
|
||||
global_config.access.user_patterns = patterns.clone();
|
||||
|
||||
global_config_manager_guard.persist(&global_config).await?;
|
||||
|
||||
let message = match patterns {
|
||||
Some(patterns) => strings::access::users_now_match_patterns(patterns),
|
||||
None => strings::access::users_no_patterns(),
|
||||
};
|
||||
|
||||
bot.messaging()
|
||||
.send_success_markdown_no_fail(
|
||||
message_context.room(),
|
||||
&message,
|
||||
MessageResponseType::Reply(message_context.thread_info().root_event_id.clone()),
|
||||
)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user