Skip to main content

autter_core/database/
ip_bans.rs

1use crate::{
2    DataManager,
3    model::{AuditLogEntry, Error, IpBan, Result, User, UserPermission},
4};
5use oiseau::cache::Cache;
6use oiseau::{PostgresRow, execute, get, params, query_row, query_rows};
7use tetratto_core2::{
8    auto_method,
9    model::{addr::RemoteAddr, id::Id},
10};
11
12impl DataManager {
13    /// Get a [`IpBan`] from an SQL row.
14    pub(crate) fn get_ipban_from_row(x: &PostgresRow) -> IpBan {
15        IpBan {
16            ip: get!(x->0(String)),
17            created: get!(x->1(i64)) as u128,
18            reason: get!(x->2(String)),
19            moderator: Id::Legacy(get!(x->3(i64)) as usize),
20        }
21    }
22
23    auto_method!(get_ipban_by_ip(&str)@get_ipban_from_row -> "SELECT * FROM a_ip_bans WHERE ip = $1" --name="ip ban" --returns=IpBan --cache-key-tmpl="srmp.ipban:{}");
24
25    /// Get an IP ban as a [`RemoteAddr`].
26    ///
27    /// # Arguments
28    /// * `prefix`
29    pub async fn get_ipban_by_addr(&self, addr: &RemoteAddr) -> Result<IpBan> {
30        let conn = match self.0.connect().await {
31            Ok(c) => c,
32            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
33        };
34
35        let res = query_row!(
36            &conn,
37            "SELECT * FROM a_ip_bans WHERE ip = $1",
38            &[&addr.prefix(None)],
39            |x| { Ok(Self::get_ipban_from_row(x)) }
40        );
41
42        if res.is_err() {
43            return Err(Error::GeneralNotFound("ip ban".to_string()));
44        }
45
46        Ok(res.unwrap())
47    }
48
49    /// Get all IP bans (paginated).
50    ///
51    /// # Arguments
52    /// * `batch` - the limit of items in each page
53    /// * `page` - the page number
54    pub async fn get_ip_bans(&self, batch: usize, page: usize) -> Result<Vec<IpBan>> {
55        let conn = match self.0.connect().await {
56            Ok(c) => c,
57            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
58        };
59
60        let res = query_rows!(
61            &conn,
62            "SELECT * FROM a_ip_bans ORDER BY created DESC LIMIT $1 OFFSET $2",
63            &[&(batch as i64), &((page * batch) as i64)],
64            |x| { Self::get_ipban_from_row(x) }
65        );
66
67        if res.is_err() {
68            return Err(Error::GeneralNotFound("ip ban".to_string()));
69        }
70
71        Ok(res.unwrap())
72    }
73
74    /// Create a new IP ban in the database.
75    ///
76    /// # Arguments
77    /// * `data` - a mock [`IpBan`] object to insert
78    pub async fn create_ipban(&self, data: IpBan) -> Result<()> {
79        let user = self.get_user_by_id(&data.moderator).await?;
80
81        // ONLY moderators can create ip bans
82        if !user.permissions.contains(&UserPermission::ManageBans) {
83            return Err(Error::NotAllowed);
84        }
85
86        let conn = match self.0.connect().await {
87            Ok(c) => c,
88            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
89        };
90
91        let res = execute!(
92            &conn,
93            "INSERT INTO a_ip_bans VALUES ($1, $2, $3, $4)",
94            params![
95                &data.ip.as_str(),
96                &(data.created as i64),
97                &data.reason.as_str(),
98                &(data.moderator.as_usize() as i64)
99            ]
100        );
101
102        if let Err(e) = res {
103            return Err(Error::DatabaseError(e.to_string()));
104        }
105
106        // create audit log entry
107        self.create_audit_log_entry(AuditLogEntry::new(
108            user.id,
109            format!("invoked `create_ipban` with x value `{}`", data.ip),
110        ))
111        .await?;
112
113        // return
114        Ok(())
115    }
116
117    pub async fn delete_ipban(&self, ip: &str, user: User) -> Result<()> {
118        // ONLY moderators can manage ip bans
119        if !user.permissions.contains(&UserPermission::ManageBans) {
120            return Err(Error::NotAllowed);
121        }
122
123        let conn = match self.0.connect().await {
124            Ok(c) => c,
125            Err(e) => return Err(Error::DatabaseConnection(e.to_string())),
126        };
127
128        let res = execute!(&conn, "DELETE FROM a_ip_bans WHERE ip = $1", &[&ip]);
129
130        if let Err(e) = res {
131            return Err(Error::DatabaseError(e.to_string()));
132        }
133
134        self.0.1.remove(format!("srmp.ipban:{}", ip)).await;
135
136        // create audit log entry
137        self.create_audit_log_entry(AuditLogEntry::new(
138            user.id,
139            format!("invoked `delete_ipban` with x value `{ip}`"),
140        ))
141        .await?;
142
143        // return
144        Ok(())
145    }
146}