// Copyright (c) 2022-2024 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 std::collections::hash_map::DefaultHasher; use std::hash::Hash; use std::hash::Hasher; use std::time::SystemTime; use ng_net::types::*; use ng_repo::errors::ProtocolError; use ng_repo::errors::StorageError; use ng_repo::kcv_storage::KCVStorage; use ng_repo::types::SymKey; use ng_repo::types::Timestamp; use ng_repo::utils::now_timestamp; use serde_bare::from_slice; use serde_bare::to_vec; pub struct Invitation<'a> { /// code id: [u8; 32], storage: &'a dyn KCVStorage, } impl<'a> Invitation<'a> { const PREFIX: u8 = b'i'; // propertie's invitation suffixes const TYPE: u8 = b't'; //const EXPIRE: u8 = b'e'; const PREFIX_EXPIRE: u8 = b'e'; // propertie's expiry suffixes const INVITATION: u8 = b'i'; const ALL_PROPERTIES: [u8; 1] = [Self::TYPE]; const SUFFIX_FOR_EXIST_CHECK: u8 = Self::TYPE; pub fn open( id: &[u8; 32], storage: &'a dyn KCVStorage, ) -> Result, StorageError> { let opening = Invitation { id: id.clone(), storage, }; if !opening.exists() { return Err(StorageError::NotFound); } Ok(opening) } pub fn create( id: &InvitationCode, expiry: u32, memo: &Option, storage: &'a dyn KCVStorage, ) -> Result, StorageError> { let (code_type, code) = match id { InvitationCode::Unique(c) => (0u8, c.slice()), InvitationCode::Multi(c) => (1u8, c.slice()), InvitationCode::Admin(c) => (2u8, c.slice()), }; let acc = Invitation { id: code.clone(), storage, }; if acc.exists() { return Err(StorageError::BackendError); } let mut value = to_vec(&(code_type, expiry, memo.clone()))?; storage.write_transaction(&mut |tx| { tx.put( Self::PREFIX, &to_vec(code)?, Some(Self::TYPE), &value, &None, )?; Ok(()) })?; Ok(acc) } pub fn get_all_invitations( storage: &'a dyn KCVStorage, mut admin: bool, mut unique: bool, mut multi: bool, ) -> Result)>, StorageError> { let size = to_vec(&[0u8; 32])?.len(); let mut res: Vec<(InvitationCode, u32, Option)> = vec![]; if !admin && !unique && !multi { admin = true; unique = true; multi = true; } for invite in storage.get_all_keys_and_values(Self::PREFIX, size, vec![], None, &None)? { if invite.0.len() == size + 2 { let code: [u8; 32] = from_slice(&invite.0[1..invite.0.len() - 1])?; if invite.0[size + 1] == Self::TYPE { let code_type: (u8, u32, Option) = from_slice(&invite.1)?; let inv_code = match code_type { (0, ex, memo) => { if unique { Some((InvitationCode::Unique(SymKey::ChaCha20Key(code)), ex, memo)) } else { None } } (1, ex, memo) => { if multi { Some((InvitationCode::Multi(SymKey::ChaCha20Key(code)), ex, memo)) } else { None } } (2, ex, memo) => { if admin { Some((InvitationCode::Admin(SymKey::ChaCha20Key(code)), ex, memo)) } else { None } } _ => panic!("invalid code type value"), }; if inv_code.is_some() { res.push(inv_code.unwrap()); } } } } Ok(res) } pub fn exists(&self) -> bool { self.storage .get( Self::PREFIX, &to_vec(&self.id).unwrap(), Some(Self::SUFFIX_FOR_EXIST_CHECK), &None, ) .is_ok() } pub fn id(&self) -> [u8; 32] { self.id } pub fn get_type(&self) -> Result { let type_ser = self.storage .get(Self::PREFIX, &to_vec(&self.id)?, Some(Self::TYPE), &None)?; let t: (u8, u32, Option) = from_slice(&type_ser)?; // if t.1 < now_timestamp() { // return Err(ProtocolError::Expired); // } Ok(t.0) } pub fn is_expired(&self) -> Result { let expire_ser = self.storage .get(Self::PREFIX, &to_vec(&self.id)?, Some(Self::TYPE), &None)?; let expire: (u8, u32, Option) = from_slice(&expire_ser)?; if expire.1 < now_timestamp() { return Ok(true); } Ok(false) } pub fn del(&self) -> Result<(), StorageError> { self.storage.write_transaction(&mut |tx| { tx.del_all( Self::PREFIX, &to_vec(&self.id)?, &Self::ALL_PROPERTIES, &None, )?; Ok(()) }) } } #[cfg(test)] mod test { use ng_repo::errors::StorageError; use ng_repo::types::*; use ng_repo::utils::*; use std::fs; use tempfile::Builder; use crate::server_storage::admin::account::Account; #[test] pub fn test_invitation() {} }