// Copyright (c) 2022-2023 Niko Bonnieure, Par le Peuple, NextGraph.org developers // All rights reserved. // Licensed under the Apache License, Version 2.0 // // or the MIT license , // at your option. All files in the project carrying such // notice may not be copied, modified, or distributed except // according to those terms. //! User account use p2p_net::types::*; use p2p_repo::kcv_store::KCVStore; use p2p_repo::store::*; use p2p_repo::types::*; use serde_bare::to_vec; pub struct Account<'a> { /// User ID id: UserId, store: &'a dyn KCVStore, } impl<'a> Account<'a> { const PREFIX: u8 = b"u"[0]; // propertie's suffixes const CLIENT: u8 = b"c"[0]; const ADMIN: u8 = b"a"[0]; const OVERLAY: u8 = b"o"[0]; const ALL_PROPERTIES: [u8; 3] = [Self::CLIENT, Self::ADMIN, Self::OVERLAY]; const SUFFIX_FOR_EXIST_CHECK: u8 = Self::ADMIN; pub fn open(id: &UserId, store: &'a dyn KCVStore) -> Result, StorageError> { let opening = Account { id: id.clone(), store, }; if !opening.exists() { return Err(StorageError::NotFound); } Ok(opening) } pub fn create( id: &UserId, admin: bool, store: &'a dyn KCVStore, ) -> Result, StorageError> { let acc = Account { id: id.clone(), store, }; if acc.exists() { return Err(StorageError::BackendError); } store.put( Self::PREFIX, &to_vec(&id)?, Some(Self::ADMIN), to_vec(&admin)?, )?; Ok(acc) } pub fn exists(&self) -> bool { self.store .get( Self::PREFIX, &to_vec(&self.id).unwrap(), Some(Self::SUFFIX_FOR_EXIST_CHECK), ) .is_ok() } pub fn id(&self) -> UserId { self.id } pub fn add_client(&self, client: &ClientId) -> Result<(), StorageError> { if !self.exists() { return Err(StorageError::BackendError); } self.store.put( Self::PREFIX, &to_vec(&self.id)?, Some(Self::CLIENT), to_vec(client)?, ) } pub fn remove_client(&self, client: &ClientId) -> Result<(), StorageError> { self.store.del_property_value( Self::PREFIX, &to_vec(&self.id)?, Some(Self::CLIENT), to_vec(client)?, ) } pub fn has_client(&self, client: &ClientId) -> Result<(), StorageError> { self.store.has_property_value( Self::PREFIX, &to_vec(&self.id)?, Some(Self::CLIENT), to_vec(client)?, ) } pub fn add_overlay(&self, overlay: &OverlayId) -> Result<(), StorageError> { if !self.exists() { return Err(StorageError::BackendError); } self.store.put( Self::PREFIX, &to_vec(&self.id)?, Some(Self::OVERLAY), to_vec(overlay)?, ) } pub fn remove_overlay(&self, overlay: &OverlayId) -> Result<(), StorageError> { self.store.del_property_value( Self::PREFIX, &to_vec(&self.id)?, Some(Self::OVERLAY), to_vec(overlay)?, ) } pub fn has_overlay(&self, overlay: &OverlayId) -> Result<(), StorageError> { self.store.has_property_value( Self::PREFIX, &to_vec(&self.id)?, Some(Self::OVERLAY), to_vec(overlay)?, ) } pub fn is_admin(&self) -> Result { if self .store .has_property_value( Self::PREFIX, &to_vec(&self.id)?, Some(Self::ADMIN), to_vec(&true)?, ) .is_ok() { return Ok(true); } Ok(false) } pub fn del(&self) -> Result<(), StorageError> { self.store .del_all(Self::PREFIX, &to_vec(&self.id)?, &Self::ALL_PROPERTIES) } } #[cfg(test)] mod test { use p2p_repo::store::*; use p2p_repo::types::*; use p2p_repo::utils::*; use stores_lmdb::kcv_store::LmdbKCVStore; use std::fs; use tempfile::Builder; use crate::broker_store::account::Account; #[test] pub fn test_account() { let path_str = "test-env"; let root = Builder::new().prefix(path_str).tempdir().unwrap(); let key: [u8; 32] = [0; 32]; fs::create_dir_all(root.path()).unwrap(); println!("{}", root.path().to_str().unwrap()); let mut store = LmdbKCVStore::open(root.path(), key); let user_id = PubKey::Ed25519PubKey([1; 32]); let account = Account::create(&user_id, true, &store).unwrap(); println!("account created {}", account.id()); let account2 = Account::open(&user_id, &store).unwrap(); println!("account opened {}", account2.id()); let client_id = PubKey::Ed25519PubKey([56; 32]); let client_id_not_added = PubKey::Ed25519PubKey([57; 32]); account2.add_client(&client_id).unwrap(); assert!(account2.is_admin().unwrap()); account.has_client(&client_id).unwrap(); assert!(account.has_client(&client_id_not_added).is_err()); } }