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