Skip to main content

lockbook_server_lib/
defense.rs

1use std::{
2    collections::HashMap,
3    net::IpAddr,
4    ops::Deref,
5    time::{SystemTime, UNIX_EPOCH},
6};
7
8use google_androidpublisher3::chrono::{Datelike, Local};
9use serde::{Deserialize, Serialize};
10use time::Duration;
11
12use crate::{
13    ServerState,
14    billing::{
15        app_store_client::AppStoreClient, google_play_client::GooglePlayClient,
16        stripe_client::StripeClient,
17    },
18    document_service::DocumentService,
19};
20
21#[derive(Debug, Clone, Serialize, Deserialize, Default)]
22pub struct BandwidthReport {
23    monthly_agg: HashMap<YearMonth, usize>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize, Hash, Eq, PartialEq)]
27pub struct YearMonth {
28    pub year: i32,
29    pub month: u32,
30}
31
32impl YearMonth {
33    fn current() -> Self {
34        let now = Local::now();
35        Self { year: now.year(), month: now.month() }
36    }
37}
38
39pub static SERVER_BANDWIDTH_CAP: usize = 1_000_000_000_000; // 1tb = $120
40
41impl BandwidthReport {
42    pub fn current_bandwidth(&self) -> usize {
43        self.monthly_agg
44            .get(&YearMonth::current())
45            .copied()
46            .unwrap_or_default()
47    }
48
49    pub fn all_bandwidth(&self) -> usize {
50        self.monthly_agg.values().sum()
51    }
52
53    pub fn increase_by(&mut self, inc: usize) {
54        let now = YearMonth::current();
55        match self.monthly_agg.get_mut(&YearMonth::current()) {
56            Some(new) => *new += inc,
57            None => {
58                self.monthly_agg.insert(now, inc);
59            }
60        }
61    }
62}
63
64/// This struct helps us ensure that a given IP isn't making too many accounts
65/// this could be expanded upon as a broader rate limit, for now we're just going
66/// to apply the pattern where it's needed (new-account).
67#[derive(Copy, Debug, Clone, Serialize, Deserialize)]
68pub struct IpData {
69    ip: IpAddr,
70    time: u64,
71}
72static MAX_IPS: u16 = 1000;
73
74impl<S, A, G, D> ServerState<S, A, G, D>
75where
76    S: StripeClient,
77    A: AppStoreClient,
78    G: GooglePlayClient,
79    D: DocumentService,
80{
81    /// Checks whether the server is configured to rate limit, and if so it will make sure that
82    /// this IP has not created an account within the last 1 minute
83    pub async fn can_create_account(&self, ip: IpAddr) -> bool {
84        if !self.config.features.new_account_rate_limit {
85            return true;
86        }
87
88        let ips = self.recent_new_account_ips.lock().await;
89        for visitor in ips.deref() {
90            if visitor.ip == ip {
91                let now = SystemTime::now()
92                    .duration_since(UNIX_EPOCH)
93                    .unwrap()
94                    .as_millis() as u64;
95                if now - visitor.time > Duration::minutes(1).whole_milliseconds() as u64 {
96                    return true;
97                } else {
98                    tracing::error!("account creation not permitted due to rate limit");
99                    return false;
100                }
101            }
102        }
103        true
104    }
105
106    pub async fn did_create_account(&self, ip: IpAddr) {
107        let mut ips = self.recent_new_account_ips.lock().await;
108        ips.retain(|visitor| visitor.ip != ip);
109        if ips.len() > MAX_IPS as usize {
110            ips.pop_front();
111        }
112
113        ips.push_back(IpData {
114            ip,
115            time: SystemTime::now()
116                .duration_since(UNIX_EPOCH)
117                .unwrap()
118                .as_millis() as u64,
119        });
120    }
121}