Skip to main content

lockbook_server_lib/
account_service.rs

1use crate::ServerError::ClientError;
2use crate::billing::app_store_client::AppStoreClient;
3use crate::billing::billing_model::BillingPlatform;
4use crate::billing::google_play_client::GooglePlayClient;
5use crate::billing::stripe_client::StripeClient;
6use crate::document_service::DocumentService;
7use crate::schema::{Account, ServerDb};
8use crate::utils::username_is_valid;
9use crate::{RequestContext, ServerError, ServerState};
10use db_rs::Db;
11use lb_rs::model::account::Username;
12use lb_rs::model::api::NewAccountError::{FileIdTaken, PublicKeyTaken, UsernameTaken};
13use lb_rs::model::api::{
14    AccountFilter, AccountIdentifier, AccountInfo, AdminDisappearAccountError,
15    AdminDisappearAccountRequest, AdminGetAccountInfoError, AdminGetAccountInfoRequest,
16    AdminGetAccountInfoResponse, AdminListUsersError, AdminListUsersRequest,
17    AdminListUsersResponse, DeleteAccountError, DeleteAccountRequest, FileUsage, GetPublicKeyError,
18    GetPublicKeyRequest, GetPublicKeyResponse, GetUsageError, GetUsageRequest, GetUsageResponse,
19    GetUsernameError, GetUsernameRequest, GetUsernameResponse, METADATA_FEE, NewAccountError,
20    NewAccountRequestV2, NewAccountResponse, PaymentPlatform,
21};
22use lb_rs::model::clock::get_time;
23use lb_rs::model::file_like::FileLike;
24use lb_rs::model::file_metadata::Owner;
25use lb_rs::model::server_meta::IntoServerMeta;
26use lb_rs::model::server_tree::ServerTree;
27use lb_rs::model::tree_like::TreeLike;
28use lb_rs::model::usage::bytes_to_human;
29use libsecp256k1::PublicKey;
30use std::collections::HashSet;
31use std::fmt::Debug;
32use std::ops::DerefMut;
33use tracing::warn;
34
35impl<S, A, G, D> ServerState<S, A, G, D>
36where
37    S: StripeClient,
38    A: AppStoreClient,
39    G: GooglePlayClient,
40    D: DocumentService,
41{
42    /// Create a new account given a username, public_key, and root folder.
43    /// Checks that username is valid, and that username, public_key and root_folder are new.
44    /// Inserts all of these values into their respective keys along with the default free account tier size
45    pub async fn new_account_v2(
46        &self, mut context: RequestContext<NewAccountRequestV2>,
47    ) -> Result<NewAccountResponse, ServerError<NewAccountError>> {
48        context.request.username = context.request.username.to_lowercase();
49        let request = &context.request;
50
51        tracing::info!("new-account attempt username: {}", request.username);
52
53        if !username_is_valid(&request.username) {
54            return Err(ClientError(NewAccountError::InvalidUsername));
55        }
56
57        if !&self.config.features.new_accounts {
58            return Err(ClientError(NewAccountError::Disabled));
59        }
60
61        let root = request.root_folder.clone();
62        let now = get_time().0 as u64;
63        let root = root.add_time(now);
64
65        let mut db = self.index_db.lock().await;
66        let handle = db.begin_transaction()?;
67
68        if let Some(ip) = context.ip {
69            if !self.can_create_account(ip.ip()).await {
70                return Err(ClientError(NewAccountError::RateLimited));
71            }
72        }
73
74        if db.accounts.get().contains_key(&Owner(request.public_key)) {
75            return Err(ClientError(PublicKeyTaken));
76        }
77
78        if db.usernames.get().contains_key(&request.username) {
79            return Err(ClientError(UsernameTaken));
80        }
81
82        if db.metas.get().contains_key(root.id()) {
83            return Err(ClientError(FileIdTaken));
84        }
85
86        if self.config.features.new_account_rate_limit {
87            if let Some(ip) = context.ip {
88                self.did_create_account(ip.ip()).await;
89            }
90        }
91
92        let username = &request.username;
93        let account = Account { username: username.clone(), billing_info: Default::default() };
94
95        let owner = Owner(request.public_key);
96
97        let mut owned_files = HashSet::new();
98        owned_files.insert(*root.id());
99
100        db.accounts.insert(owner, account)?;
101        db.usernames.insert(username.clone(), owner)?;
102        db.owned_files.insert(owner, *root.id())?;
103        db.shared_files.create_key(owner)?;
104        db.file_children.create_key(*root.id())?;
105        db.metas.insert(*root.id(), root.clone())?;
106
107        handle.drop_safely()?;
108
109        Ok(NewAccountResponse { last_synced: root.version })
110    }
111
112    pub async fn get_public_key(
113        &self, context: RequestContext<GetPublicKeyRequest>,
114    ) -> Result<GetPublicKeyResponse, ServerError<GetPublicKeyError>> {
115        let request = &context.request;
116        self.public_key_from_username(&request.username).await
117    }
118
119    pub async fn public_key_from_username(
120        &self, username: &str,
121    ) -> Result<GetPublicKeyResponse, ServerError<GetPublicKeyError>> {
122        self.index_db
123            .lock()
124            .await
125            .usernames
126            .get()
127            .get(username)
128            .map(|owner| Ok(GetPublicKeyResponse { key: owner.0 }))
129            .unwrap_or(Err(ClientError(GetPublicKeyError::UserNotFound)))
130    }
131
132    pub async fn get_username(
133        &self, context: RequestContext<GetUsernameRequest>,
134    ) -> Result<GetUsernameResponse, ServerError<GetUsernameError>> {
135        self.username_from_public_key(context.request.key).await
136    }
137
138    pub async fn username_from_public_key(
139        &self, key: PublicKey,
140    ) -> Result<GetUsernameResponse, ServerError<GetUsernameError>> {
141        self.index_db
142            .lock()
143            .await
144            .accounts
145            .get()
146            .get(&Owner(key))
147            .map(|account| Ok(GetUsernameResponse { username: account.username.clone() }))
148            .unwrap_or(Err(ClientError(GetUsernameError::UserNotFound)))
149    }
150
151    pub async fn get_usage(
152        &self, context: RequestContext<GetUsageRequest>,
153    ) -> Result<GetUsageResponse, ServerError<GetUsageError>> {
154        let mut lock = self.index_db.lock().await;
155        let db = lock.deref_mut();
156
157        let cap = Self::get_cap(db, &context.public_key)?;
158        let owner = Owner(context.public_key);
159
160        let mut tree = ServerTree::new(
161            owner,
162            &mut db.owned_files,
163            &mut db.shared_files,
164            &mut db.file_children,
165            &mut db.metas,
166        )?
167        .to_lazy();
168
169        let usages = tree
170            .ids()
171            .into_iter()
172            .filter_map(|file_id| {
173                if tree.calculate_deleted(&file_id).unwrap_or(true) {
174                    return None;
175                }
176                let file = tree.find(&file_id).ok()?;
177                if file.owner() != owner {
178                    return None;
179                }
180                let file_size = file.doc_size().unwrap_or(0) as u64;
181                Some(FileUsage { file_id, size_bytes: file_size + METADATA_FEE })
182            })
183            .collect();
184
185        Ok(GetUsageResponse { usages, cap })
186    }
187
188    pub fn get_cap(
189        db: &ServerDb, public_key: &PublicKey,
190    ) -> Result<u64, ServerError<GetUsageHelperError>> {
191        Ok(db
192            .accounts
193            .get()
194            .get(&Owner(*public_key))
195            .ok_or(ServerError::ClientError(GetUsageHelperError::UserNotFound))?
196            .billing_info
197            .data_cap())
198    }
199
200    pub async fn delete_account(
201        &self, context: RequestContext<DeleteAccountRequest>,
202    ) -> Result<(), ServerError<DeleteAccountError>> {
203        self.delete_account_helper(&context.public_key, false)
204            .await?;
205
206        Ok(())
207    }
208
209    pub async fn admin_disappear_account(
210        &self, context: RequestContext<AdminDisappearAccountRequest>,
211    ) -> Result<(), ServerError<AdminDisappearAccountError>> {
212        let owner = {
213            let db = &self.index_db.lock().await;
214
215            if !Self::is_admin::<AdminDisappearAccountError>(
216                db,
217                &context.public_key,
218                &self.config.admin.admins,
219            )? {
220                return Err(ClientError(AdminDisappearAccountError::NotPermissioned));
221            }
222
223            let admin_username = db
224                .accounts
225                .get()
226                .get(&Owner(context.public_key))
227                .cloned()
228                .map(|account| account.username)
229                .unwrap_or_else(|| "~unknown~".to_string());
230
231            warn!("admin {} is disappearing account {}", admin_username, context.request.username);
232
233            *db.usernames
234                .get()
235                .get(&context.request.username)
236                .ok_or(ClientError(AdminDisappearAccountError::UserNotFound))?
237        };
238
239        self.delete_account_helper(&owner.0, true).await?;
240
241        Ok(())
242    }
243
244    pub async fn admin_list_users(
245        &self, context: RequestContext<AdminListUsersRequest>,
246    ) -> Result<AdminListUsersResponse, ServerError<AdminListUsersError>> {
247        let (db, request) = (&self.index_db.lock().await, &context.request);
248
249        if !Self::is_admin::<AdminListUsersError>(
250            db,
251            &context.public_key,
252            &self.config.admin.admins,
253        )? {
254            return Err(ClientError(AdminListUsersError::NotPermissioned));
255        }
256
257        let mut users: Vec<String> = vec![];
258
259        for account in db.accounts.get().values() {
260            match &request.filter {
261                Some(filter) => match filter {
262                    AccountFilter::Premium => {
263                        if account.billing_info.is_premium() {
264                            users.push(account.username.clone());
265                        }
266                    }
267                    AccountFilter::AppStorePremium => match account.billing_info.billing_platform {
268                        Some(BillingPlatform::AppStore(_)) if account.billing_info.is_premium() => {
269                            users.push(account.username.clone());
270                        }
271                        _ => {}
272                    },
273                    AccountFilter::StripePremium => match account.billing_info.billing_platform {
274                        Some(BillingPlatform::Stripe(_)) if account.billing_info.is_premium() => {
275                            users.push(account.username.clone());
276                        }
277                        _ => {}
278                    },
279                    AccountFilter::GooglePlayPremium => match account.billing_info.billing_platform
280                    {
281                        Some(BillingPlatform::GooglePlay(_))
282                            if account.billing_info.is_premium() =>
283                        {
284                            users.push(account.username.clone());
285                        }
286                        _ => {}
287                    },
288                },
289                None => users.push(account.username.clone()),
290            }
291        }
292
293        Ok(AdminListUsersResponse { users })
294    }
295
296    pub async fn admin_get_account_info(
297        &self, context: RequestContext<AdminGetAccountInfoRequest>,
298    ) -> Result<AdminGetAccountInfoResponse, ServerError<AdminGetAccountInfoError>> {
299        let (mut lock, request) = (self.index_db.lock().await, &context.request);
300        let db = lock.deref_mut();
301
302        if !Self::is_admin::<AdminGetAccountInfoError>(
303            db,
304            &context.public_key,
305            &self.config.admin.admins,
306        )? {
307            return Err(ClientError(AdminGetAccountInfoError::NotPermissioned));
308        }
309
310        let owner = match &request.identifier {
311            AccountIdentifier::PublicKey(public_key) => Owner(*public_key),
312            AccountIdentifier::Username(user) => *db
313                .usernames
314                .get()
315                .get(user)
316                .ok_or(ClientError(AdminGetAccountInfoError::UserNotFound))?,
317        };
318
319        let account = db
320            .accounts
321            .get()
322            .get(&owner)
323            .ok_or(ClientError(AdminGetAccountInfoError::UserNotFound))?
324            .clone();
325
326        let mut maybe_root = None;
327        if let Some(owned_ids) = db.owned_files.get().get(&owner) {
328            for id in owned_ids {
329                if let Some(meta) = db.metas.get().get(id) {
330                    if meta.is_root() {
331                        maybe_root = Some(*meta.id());
332                    }
333                } else {
334                    return Err(internal!(
335                        "Nonexistent file indexed as owned, id: {}, owner: {:?}",
336                        id,
337                        owner
338                    ));
339                }
340            }
341        } else {
342            return Err(internal!("Owned files not indexed for user, owner: {:?}", owner));
343        }
344        let root = if let Some(root) = maybe_root {
345            root
346        } else {
347            return Err(internal!("User root not found, owner: {:?}", owner));
348        };
349
350        let payment_platform = account
351            .billing_info
352            .billing_platform
353            .map(|billing_platform| match billing_platform {
354                BillingPlatform::Stripe(user_info) => {
355                    PaymentPlatform::Stripe { card_last_4_digits: user_info.last_4 }
356                }
357                BillingPlatform::GooglePlay(user_info) => {
358                    PaymentPlatform::GooglePlay { account_state: user_info.account_state }
359                }
360                BillingPlatform::AppStore(user_info) => {
361                    PaymentPlatform::AppStore { account_state: user_info.account_state }
362                }
363            });
364
365        let mut tree = ServerTree::new(
366            owner,
367            &mut db.owned_files,
368            &mut db.shared_files,
369            &mut db.file_children,
370            &mut db.metas,
371        )?
372        .to_lazy();
373
374        let usage = tree.calculate_usage(owner)?;
375
376        let usage_str = bytes_to_human(usage);
377
378        Ok(AdminGetAccountInfoResponse {
379            account: AccountInfo {
380                username: account.username,
381                root,
382                payment_platform,
383                usage: usage_str,
384            },
385        })
386    }
387
388    pub async fn delete_account_helper(
389        &self, public_key: &PublicKey, free_username: bool,
390    ) -> Result<(), ServerError<DeleteAccountHelperError>> {
391        let mut docs_to_delete = Vec::new();
392
393        {
394            let mut lock = self.index_db.lock().await;
395            let db = lock.deref_mut();
396            let tx = db.begin_transaction()?;
397
398            let mut tree = ServerTree::new(
399                Owner(*public_key),
400                &mut db.owned_files,
401                &mut db.shared_files,
402                &mut db.file_children,
403                &mut db.metas,
404            )?
405            .to_lazy();
406            let metas_to_delete = tree.ids();
407
408            for id in metas_to_delete.clone() {
409                if !tree.calculate_deleted(&id)? {
410                    let meta = tree.find(&id)?;
411                    if meta.is_document() && &(meta.owner().0) == public_key {
412                        if let Some(hmac) = meta.document_hmac() {
413                            docs_to_delete.push((*meta.id(), *hmac));
414                        }
415                    }
416                }
417            }
418            db.owned_files.clear_key(&Owner(*public_key))?;
419            db.shared_files.clear_key(&Owner(*public_key))?;
420            db.last_seen.remove(&Owner(*public_key))?;
421
422            for id in metas_to_delete {
423                if let Some(meta) = db.metas.get().get(&id) {
424                    if &(meta.owner().0) == public_key {
425                        for user_access_key in meta.user_access_keys() {
426                            let sharee = Owner(user_access_key.encrypted_for);
427                            db.shared_files.remove(&sharee, meta.id())?;
428                        }
429                        db.metas.remove(&id)?;
430                        db.file_children.clear_key(&id)?;
431                    }
432                }
433            }
434
435            if free_username {
436                let username = db
437                    .accounts
438                    .remove(&Owner(*public_key))?
439                    .ok_or(ClientError(DeleteAccountHelperError::UserNotFound))?
440                    .username;
441                db.usernames.remove(&username)?;
442            }
443
444            tx.drop_safely()?;
445            drop(lock);
446        }
447
448        for (id, version) in docs_to_delete {
449            self.document_service.delete(&id, &version).await?;
450        }
451        Ok(())
452    }
453
454    pub fn is_admin<E: Debug>(
455        db: &ServerDb, public_key: &PublicKey, admins: &HashSet<Username>,
456    ) -> Result<bool, ServerError<E>> {
457        let is_admin = match db.accounts.get().get(&Owner(*public_key)) {
458            None => false,
459            Some(account) => admins.contains(&account.username),
460        };
461
462        Ok(is_admin)
463    }
464}
465
466#[derive(Debug)]
467pub enum GetUsageHelperError {
468    UserNotFound,
469}
470
471#[derive(Debug)]
472pub enum DeleteAccountHelperError {
473    UserNotFound,
474}