Files
fluxer/fluxer_admin/src/routes/user_tabs.rs
T

342 lines
12 KiB
Rust

// SPDX-License-Identifier: AGPL-3.0-or-later
use crate::{
acl,
api::{
audit::SearchAuditLogsParams,
client::{AdminApiClient, ApiResultExt},
types::AccountIdentityMode,
},
config::AdminConfig,
templates::{
components::audit_logs_for_target::audit_logs_for_target, pages::user_detail_tabs as tabs,
},
};
pub struct TabQuery {
pub reports_sent_page: Option<u32>,
pub reports_received_page: Option<u32>,
pub reports_limit: Option<u32>,
pub dm_before: Option<String>,
pub dm_after: Option<String>,
pub dm_limit: Option<u32>,
pub audit_logs_page: Option<u32>,
pub message_shred_job_id: Option<String>,
pub delete_all_messages_dry_run: Option<String>,
pub delete_all_messages_channel_count: Option<u64>,
pub delete_all_messages_message_count: Option<u64>,
}
#[allow(clippy::too_many_arguments)]
pub async fn render(
client: &AdminApiClient,
config: &AdminConfig,
csrf_token: &str,
user_id: &str,
tab: &str,
query: &TabQuery,
admin_acls: &[String],
account_identity: AccountIdentityMode,
) -> Option<maud::Markup> {
match tab {
"overview" => {
let u = client
.get_user_by_id(user_id)
.await
.log_error("load user overview")?;
let change_log = if acl::has_permission(admin_acls, acl::USER_VIEW_CONTACT_LOG) {
client
.list_user_change_log(user_id, Some(50))
.await
.log_error("load user change log")
} else {
None
};
let limit_config = client
.get_limit_config()
.await
.log_error("load limit config for user overview");
Some(tabs::overview::overview_tab_with_limit_config(
config,
&u,
admin_acls,
csrf_token,
change_log.as_ref(),
limit_config.as_ref(),
account_identity.is_username(),
))
}
"account" => {
render_account(
client,
config,
csrf_token,
user_id,
&tabs::account::AccountTabOptions {
admin_acls,
account_identity,
password_reset_link: None,
},
)
.await
}
"moderation" => {
let u = client
.get_user_by_id(user_id)
.await
.log_error("load user moderation")?;
let message_shred_status = if let Some(job_id) = query
.message_shred_job_id
.as_deref()
.filter(|job_id| !job_id.trim().is_empty())
{
Some(client.get_message_shred_status(job_id).await)
} else {
None
};
let delete_all_messages_dry_run = parse_bool_flag(
query
.delete_all_messages_dry_run
.as_deref()
.unwrap_or_default(),
)
.unwrap_or(false)
.then_some((
query.delete_all_messages_channel_count.unwrap_or(0),
query.delete_all_messages_message_count.unwrap_or(0),
));
let deletion_scheduler = match u.deletion_scheduled_by.as_deref() {
Some(scheduler_id) if u.pending_deletion_at.is_some() && scheduler_id != u.id => {
client
.get_user_by_id(scheduler_id)
.await
.log_error("load deletion scheduler")
}
_ => None,
};
let ban_logs = if u.temp_banned_until.is_some()
&& acl::has_permission(admin_acls, acl::AUDIT_LOG_VIEW)
{
client
.search_audit_logs(&SearchAuditLogsParams {
query: None,
admin_user_id: None,
target_id: Some(user_id.to_owned()),
target_type: Some("user".to_owned()),
access: Some("write".to_owned()),
sort_by: Some("created_at".to_owned()),
sort_order: Some("desc".to_owned()),
limit: 100,
offset: 0,
})
.await
.log_error("load ban audit logs")
.map(|response| response.logs)
.unwrap_or_default()
} else {
Vec::new()
};
let context = tabs::moderation::ModerationContext {
deletion_scheduler: deletion_scheduler.as_ref(),
current_ban: tabs::moderation::find_current_ban(&u, &ban_logs),
username_sign_in: account_identity.is_username(),
};
Some(tabs::moderation::moderation_tab(
config,
&u,
csrf_token,
admin_acls,
&context,
query.message_shred_job_id.as_deref(),
message_shred_status.as_ref(),
delete_all_messages_dry_run,
))
}
"applications" => {
if !acl::has_permission(admin_acls, acl::APPLICATION_LIST_BY_OWNER) {
return None;
}
let apps = client
.list_user_applications(user_id)
.await
.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list user applications"))
.unwrap_or_default();
Some(tabs::applications::applications_tab(
config,
&apps,
"No applications owned",
"This user does not own any applications.",
))
}
"archives" => {
if !acl::has_any_permission(
admin_acls,
&[acl::ARCHIVE_VIEW_ALL, acl::ARCHIVE_TRIGGER_USER],
) {
return None;
}
let a = client
.list_archives("user", Some(user_id), false, None)
.await;
Some(tabs::archives::archives_tab(
config,
user_id,
&a.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list user archives"))
.map(|r| r.archives)
.unwrap_or_default(),
csrf_token,
))
}
"guilds" => {
let g = client
.get_user_guilds(user_id, Some(200), None, None, Some(true))
.await
.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list user guilds"))
.unwrap_or_default();
Some(tabs::guilds::guilds_tab(config, user_id, &g))
}
"reports" => {
let limit = query.reports_limit.unwrap_or(25);
let sent_page = query.reports_sent_page.unwrap_or(0);
let received_page = query.reports_received_page.unwrap_or(0);
let sent = client
.search_reports_by_reporter(user_id, limit, u64::from(sent_page) * u64::from(limit))
.await
.log_error("load reports sent by user");
let received = client
.search_reports_by_reported_user(
user_id,
limit,
u64::from(received_page) * u64::from(limit),
)
.await
.log_error("load reports against user");
Some(tabs::reports::reports_tab(
config,
user_id,
sent.as_ref(),
received.as_ref(),
sent_page,
received_page,
limit,
))
}
"relationships" => {
if !acl::has_permission(admin_acls, acl::USER_LIST_RELATIONSHIPS) {
return None;
}
let d = client
.list_user_relationships(user_id)
.await
.log_error("load user relationships")?;
Some(tabs::relationships::relationships_tab(
config,
user_id,
&d,
acl::has_permission(admin_acls, acl::USER_REMOVE_RELATIONSHIP),
csrf_token,
))
}
"dm-history" | "dm_history" => {
let limit = query.dm_limit.unwrap_or(50);
let before = query.dm_before.as_deref();
let after = query.dm_after.as_deref();
let ch = client
.list_user_dm_channels(user_id, before, after, Some(limit))
.await
.map(|r| r.channels)
.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list user DM channels"))
.unwrap_or_default();
Some(tabs::dm_history::dm_history_tab(
config,
user_id,
&ch,
before,
after,
limit as usize,
))
}
"group-dm" | "group_dms" => {
let ch = client
.list_user_group_dm_channels(user_id)
.await
.map(|r| r.channels)
.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list user group DM channels"))
.unwrap_or_default();
Some(tabs::group_dm::group_dm_tab(config, user_id, &ch))
}
"audit_logs" | "audit-logs" => {
if !acl::has_permission(admin_acls, acl::AUDIT_LOG_VIEW) {
return None;
}
let page = query.audit_logs_page.unwrap_or(0);
let limit = 50;
let resp = client
.search_audit_logs(&SearchAuditLogsParams {
query: None,
admin_user_id: None,
target_id: Some(user_id.to_owned()),
target_type: None,
access: Some("write".to_owned()),
sort_by: Some("created_at".to_owned()),
sort_order: Some("desc".to_owned()),
limit,
offset: u64::from(page) * u64::from(limit),
})
.await
.log_error("load user admin audit logs")?;
Some(audit_logs_for_target(
&config.base_path,
&resp.logs,
user_id,
page,
resp.total,
&format!("{}/users/{}?tab=audit_logs", config.base_path, user_id),
))
}
_ => None,
}
}
pub async fn render_account(
client: &AdminApiClient,
config: &AdminConfig,
csrf_token: &str,
user_id: &str,
options: &tabs::account::AccountTabOptions<'_>,
) -> Option<maud::Markup> {
let u = client
.get_user_by_id(user_id)
.await
.log_error("load user account")?;
let s = client
.list_user_sessions(user_id)
.await
.map(|r| r.sessions)
.map_err(
|error| tracing::warn!(%error, user_id, "admin API request failed: list user sessions"),
)
.unwrap_or_default();
let webauthn_credentials = client
.list_webauthn_credentials(user_id)
.await
.map_err(|error| tracing::warn!(%error, user_id, "admin API request failed: list webauthn credentials"))
.unwrap_or_default();
Some(tabs::account::account_tab(
config,
&u,
&s,
&webauthn_credentials,
csrf_token,
options,
))
}
fn parse_bool_flag(value: &str) -> Option<bool> {
match value.trim().to_ascii_lowercase().as_str() {
"1" | "true" => Some(true),
"0" | "false" => Some(false),
_ => None,
}
}