tetratto_core/database/
ipbans.rs1use oiseau::cache::Cache;
2use crate::model::addr::RemoteAddr;
3use crate::model::moderation::AuditLogEntry;
4use crate::model::{Error, Result, auth::IpBan, auth::User, permissions::FinePermission};
5use crate::{auto_method, DataManager};
6
7use oiseau::{PostgresRow, execute, get, query_row, query_rows, params};
8
9impl DataManager {
10 pub(crate) fn get_ipban_from_row(x: &PostgresRow) -> IpBan {
12 IpBan {
13 ip: get!(x->0(String)),
14 created: get!(x->1(i64)) as usize,
15 reason: get!(x->2(String)),
16 moderator: get!(x->3(i64)) as usize,
17 }
18 }
19
20 auto_method!(get_ipban_by_ip(&str)@get_ipban_from_row -> "SELECT * FROM ipbans WHERE ip = $1" --name="ip ban" --returns=IpBan --cache-key-tmpl="atto.ipban:{}");
21
22 pub async fn get_ipban_by_addr(&self, addr: &RemoteAddr) -> Result<IpBan> {
27 let conn = match self.0.connect().await {
28 Ok(c) => c,
29 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
30 };
31
32 let res = query_row!(
33 &conn,
34 "SELECT * FROM ipbans WHERE ip = $1",
35 &[&addr.prefix(None)],
36 |x| { Ok(Self::get_ipban_from_row(x)) }
37 );
38
39 if res.is_err() {
40 return Err(Error::GeneralNotFound("ip ban".to_string()));
41 }
42
43 Ok(res.unwrap())
44 }
45
46 pub async fn get_ipbans(&self, batch: usize, page: usize) -> Result<Vec<IpBan>> {
52 let conn = match self.0.connect().await {
53 Ok(c) => c,
54 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
55 };
56
57 let res = query_rows!(
58 &conn,
59 "SELECT * FROM ipbans ORDER BY created DESC LIMIT $1 OFFSET $2",
60 &[&(batch as i64), &((page * batch) as i64)],
61 |x| { Self::get_ipban_from_row(x) }
62 );
63
64 if res.is_err() {
65 return Err(Error::GeneralNotFound("ip ban".to_string()));
66 }
67
68 Ok(res.unwrap())
69 }
70
71 pub async fn create_ipban(&self, data: IpBan) -> Result<()> {
76 let user = self.get_user_by_id(data.moderator).await?;
77
78 if !user.permissions.check(FinePermission::MANAGE_BANS) {
80 return Err(Error::NotAllowed);
81 }
82
83 let conn = match self.0.connect().await {
84 Ok(c) => c,
85 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
86 };
87
88 let res = execute!(
89 &conn,
90 "INSERT INTO ipbans VALUES ($1, $2, $3, $4)",
91 params![
92 &data.ip.as_str(),
93 &(data.created as i64),
94 &data.reason.as_str(),
95 &(data.moderator as i64)
96 ]
97 );
98
99 if let Err(e) = res {
100 return Err(Error::DatabaseError(e.to_string()));
101 }
102
103 self.create_audit_log_entry(AuditLogEntry::new(
105 user.id,
106 format!("invoked `create_ipban` with x value `{}`", data.ip),
107 ))
108 .await?;
109
110 Ok(())
112 }
113
114 pub async fn delete_ipban(&self, ip: &str, user: User) -> Result<()> {
115 if !user.permissions.check(FinePermission::MANAGE_BANS) {
117 return Err(Error::NotAllowed);
118 }
119
120 let conn = match self.0.connect().await {
121 Ok(c) => c,
122 Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
123 };
124
125 let res = execute!(&conn, "DELETE FROM ipbans WHERE ip = $1", &[&ip]);
126
127 if let Err(e) = res {
128 return Err(Error::DatabaseError(e.to_string()));
129 }
130
131 self.0.1.remove(format!("atto.ipban:{}", ip)).await;
132
133 self.create_audit_log_entry(AuditLogEntry::new(
135 user.id,
136 format!("invoked `delete_ipban` with x value `{ip}`"),
137 ))
138 .await?;
139
140 Ok(())
142 }
143}