implement icon management and update client app state
This commit is contained in:
@@ -1,14 +1,15 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use crate::client::client_auth_api::ClientAuth;
|
use crate::client::client_auth_api::ClientAuth;
|
||||||
use crate::client::opaque::{ServerConnectionLogin, ServerConnectionRegister};
|
use crate::client::opaque::{ServerConnectionLogin, ServerConnectionRegister};
|
||||||
use crate::client::repo::ClientRepo;
|
use crate::client::repo::ClientRepo;
|
||||||
use crate::client::states::{KeyRegister, UserStateCodeLogin, UserStateCodeRegister, UserStateKey};
|
use crate::client::states::{KeyLogin, KeyRegister, UserStateCodeLogin, UserStateCodeRegister, UserStateKey};
|
||||||
use crate::shared::models::app::{Icon, IconID};
|
use crate::shared::models::app::{Icon, IconID};
|
||||||
use crate::shared::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
use crate::shared::user_api::UserAPI;
|
use crate::shared::user_api::UserAPI;
|
||||||
|
|
||||||
struct ClientApp<'a, R, U, C>
|
struct ClientApp<'a, State, R, U, C>
|
||||||
where
|
where
|
||||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
U: UserAPI,
|
U: UserAPI,
|
||||||
@@ -16,11 +17,15 @@ where
|
|||||||
{
|
{
|
||||||
auth_api: ClientAuth<'a, R, U>,
|
auth_api: ClientAuth<'a, R, U>,
|
||||||
client_repo: C,
|
client_repo: C,
|
||||||
new_user: HashMap<Email, UserStateCodeRegister>,
|
code_reg: Option<UserStateCodeRegister>,
|
||||||
user_login: HashMap<Email, UserStateCodeLogin>
|
key_login: Option<UserStateKey<KeyLogin>>,
|
||||||
|
code_login: Option<UserStateCodeLogin>,
|
||||||
|
_state: PhantomData<State>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a, R, U, C>ClientApp<'a, R, U, C>
|
|
||||||
|
|
||||||
|
impl <'a,State, R, U, C>ClientApp<'a,State, R, U, C>
|
||||||
where
|
where
|
||||||
R: ServerConnectionRegister + ServerConnectionLogin,
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
U: UserAPI,
|
U: UserAPI,
|
||||||
@@ -30,16 +35,36 @@ where
|
|||||||
Self {
|
Self {
|
||||||
auth_api,
|
auth_api,
|
||||||
client_repo,
|
client_repo,
|
||||||
new_user: HashMap::new(),
|
code_reg: None,
|
||||||
user_login: HashMap::new()
|
key_login: None,
|
||||||
|
code_login: None,
|
||||||
|
_state: PhantomData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn into_state<NextState>(self) -> ClientApp<'a, NextState, R, U, C> {
|
||||||
|
ClientApp {
|
||||||
|
auth_api: self.auth_api,
|
||||||
|
client_repo: self.client_repo,
|
||||||
|
code_reg: self.code_reg,
|
||||||
|
key_login: self.key_login,
|
||||||
|
code_login: self.code_login,
|
||||||
|
_state: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async fn new_user(&mut self, email: Email, secret_key: UserSecretKey) -> Result<Vec<Icon>, String> {
|
pub struct NewUserRegisterKey;
|
||||||
|
impl <'a, R, U, C>ClientApp<'a, NewUserRegisterKey, R, U, C>
|
||||||
|
where
|
||||||
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
|
U: UserAPI,
|
||||||
|
C: ClientRepo,
|
||||||
|
{
|
||||||
|
async fn new_user(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a, NewUserRegisterCode, R, U, C>, String> {
|
||||||
let key_register = UserStateKey::<KeyRegister>::new(email.clone(), secret_key.clone());
|
let key_register = UserStateKey::<KeyRegister>::new(email.clone(), secret_key.clone());
|
||||||
self.client_repo.add_secret_key(email.clone(), secret_key.clone()).await?;
|
self.client_repo.add_secret_key(email.clone(), secret_key.clone()).await?;
|
||||||
let key_login = match key_register.register(&self.auth_api).await.map_err(|e| format!("error: {e:?}")) {
|
let key_login = match key_register.register(&self.auth_api).await.map_err(|e| format!("error: {e:?}")) {
|
||||||
Ok(s) => {s}
|
Ok(s) => s,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
self.client_repo.remove_secret_key(&email).await.expect("couldn't delete");
|
self.client_repo.remove_secret_key(&email).await.expect("couldn't delete");
|
||||||
return Err(format!("error registering key {}", e))
|
return Err(format!("error registering key {}", e))
|
||||||
@@ -47,23 +72,54 @@ where
|
|||||||
};
|
};
|
||||||
let key_logged_in = key_login.login(&self.auth_api).await.map_err(|e| format!("error: {e:?}"))?;
|
let key_logged_in = key_login.login(&self.auth_api).await.map_err(|e| format!("error: {e:?}"))?;
|
||||||
let code_register = key_logged_in.register_code(&self.auth_api).await?;
|
let code_register = key_logged_in.register_code(&self.auth_api).await?;
|
||||||
let icons = code_register.get_icons();
|
self.code_reg = Some(code_register);
|
||||||
self.new_user.insert(email, code_register);
|
Ok(self.into_state::<NewUserRegisterCode>())
|
||||||
Ok(icons)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn new_user_register_code(&mut self, email: Email, selected_icons: Vec<IconID>) -> Result<Vec<Vec<Icon>>, String> {
|
|
||||||
let code_register = self.new_user.remove(&email).unwrap(); // Todo should be an error
|
|
||||||
let code_login = code_register.register(&self.auth_api,selected_icons).await?;
|
|
||||||
let keypad = code_login.get_keypad().await;
|
|
||||||
self.user_login.insert(email, code_login);
|
|
||||||
Ok(keypad)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn user_login(&mut self, email: Email, selected_keys: &Vec<usize>) -> Result<(), String> {
|
|
||||||
let mut code_login = self.user_login.remove(&email).unwrap(); // Todo should be an error
|
|
||||||
let code_logged_in = code_login.login(&self.auth_api, &selected_keys).await?;
|
|
||||||
self.client_repo.add_code_logged_in(&email, code_logged_in).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct UserKeyLogin;
|
||||||
|
impl <'a, R, U, C>ClientApp<'a, UserKeyLogin, R, U, C>
|
||||||
|
where
|
||||||
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
|
U: UserAPI,
|
||||||
|
C: ClientRepo,
|
||||||
|
{
|
||||||
|
async fn login(mut self, email: Email, secret_key: UserSecretKey) -> Result<(), String> {
|
||||||
|
let key_login = UserStateKey::<KeyLogin>::new(email, secret_key);
|
||||||
|
self.key_login = Some(key_login);
|
||||||
|
let key_log_in = self.into_state::<UserKeyLogin>();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NewUserRegisterCode;
|
||||||
|
impl <'a, R, U, C>ClientApp<'a, NewUserRegisterCode, R, U, C>
|
||||||
|
where
|
||||||
|
R: ServerConnectionRegister + ServerConnectionLogin,
|
||||||
|
U: UserAPI,
|
||||||
|
C: ClientRepo,
|
||||||
|
{
|
||||||
|
async fn get_new_user_icons(&self) -> Result<Vec<Icon>, String> {
|
||||||
|
Ok(
|
||||||
|
self.code_reg
|
||||||
|
.clone()
|
||||||
|
.ok_or("no code register".to_string())?
|
||||||
|
.get_icons()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn new_user_register_code(mut self, email: Email, selected_icons: Vec<IconID>) -> Result<ClientApp<'a, NewUserRegisterCode, R, U, C>, String> {
|
||||||
|
let code_register = self.code_reg.ok_or("no code register".to_string())?;
|
||||||
|
let code_login = code_register.register(&self.auth_api,selected_icons).await?;
|
||||||
|
todo!()
|
||||||
|
// Ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// async fn user_login(&mut self, email: Email, selected_keys: &Vec<usize>) -> Result<(), String> {
|
||||||
|
// let mut code_login = self.user_login.remove(&email).unwrap(); // Todo should be an error
|
||||||
|
// let code_logged_in = code_login.login(&self.auth_api, &selected_keys).await?;
|
||||||
|
// self.client_repo.add_code_logged_in(&email, code_logged_in).await?;
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession};
|
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession, RegisterCodeData};
|
||||||
use crate::shared::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
@@ -32,12 +32,12 @@ where
|
|||||||
self.opaque_key_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))
|
self.opaque_key_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: CodeLoginData) -> Result<(), String> {
|
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, register_code_data: RegisterCodeData) -> Result<(), String> {
|
||||||
let auth_data = OpaqueAuthData::from_code(email.as_str(), passcode);
|
let auth_data = OpaqueAuthData::from_code(email.as_str(), passcode);
|
||||||
self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))?;
|
self.opaque_code_register.register(&auth_data).await.map_err(|e| format!("error: {}", e))?;
|
||||||
let signed_session = SignedSessionData::new(
|
let signed_session = SignedSessionData::new(
|
||||||
key_login_session.0.session_id,
|
key_login_session.0.session_id,
|
||||||
data,
|
register_code_data,
|
||||||
&key_login_session.0.session_key
|
&key_login_session.0.session_key
|
||||||
).map_err(|e| format!("error: {e:?}"))?;
|
).map_err(|e| format!("error: {e:?}"))?;
|
||||||
self.user_api.set_code_login_data(signed_session).await
|
self.user_api.set_code_login_data(signed_session).await
|
||||||
@@ -64,7 +64,8 @@ where
|
|||||||
async fn get_new_icons(
|
async fn get_new_icons(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<Vec<Icon>, String> {
|
) -> Result<Vec<Icon>, String> {
|
||||||
self.user_api.get_new_icons().await
|
let total_props = self.get_policy().await?.keypad_dimension().total_props();
|
||||||
|
self.user_api.get_new_icons(total_props).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_login_data(
|
async fn get_login_data(
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use nkode_rs::nkode_core::chacha20prng::Nonce;
|
|||||||
use nkode_rs::nkode_core::keypad::Keypad;
|
use nkode_rs::nkode_core::keypad::Keypad;
|
||||||
use nkode_rs::nkode_core::nkode_cipher::NKodeCipher;
|
use nkode_rs::nkode_core::nkode_cipher::NKodeCipher;
|
||||||
use nkode_rs::from_bytes::FromBytes;
|
use nkode_rs::from_bytes::FromBytes;
|
||||||
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, ICON_ID_SIZE};
|
use crate::shared::models::app::{AuthAPI, CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, RegisterCodeData, ICON_ID_SIZE};
|
||||||
use crate::shared::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::opaque::UserSecretKey;
|
use crate::shared::opaque::UserSecretKey;
|
||||||
|
|
||||||
@@ -79,10 +79,10 @@ impl UserStateKeyLoggedIn {
|
|||||||
|
|
||||||
pub async fn login_code(self, api: &dyn AuthAPI) -> Result<UserStateCodeLogin, String> {
|
pub async fn login_code(self, api: &dyn AuthAPI) -> Result<UserStateCodeLogin, String> {
|
||||||
let login_data = api.get_login_data(&self.key_login).await?;
|
let login_data = api.get_login_data(&self.key_login).await?;
|
||||||
let icons = self.get_icons(api, &login_data.icon_nonce()).await?;
|
let icons = self.get_icons(api, &login_data.icon_nonce).await?;
|
||||||
let policy = api.get_policy().await?;
|
let policy = api.get_policy().await?;
|
||||||
let nkode_secret_key = self.user_secret_key.chacha20_secret_key();
|
let nkode_secret_key = self.user_secret_key.chacha20_secret_key();
|
||||||
let cipher = NKodeCipher::from_nonce(policy, &nkode_secret_key, login_data.icon_nonce())?;
|
let cipher = NKodeCipher::from_nonce(policy, &nkode_secret_key, &login_data.icon_nonce)?;
|
||||||
Ok(
|
Ok(
|
||||||
UserStateCodeLogin {
|
UserStateCodeLogin {
|
||||||
email: self.email,
|
email: self.email,
|
||||||
@@ -96,6 +96,7 @@ impl UserStateKeyLoggedIn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct UserStateCodeRegister {
|
pub struct UserStateCodeRegister {
|
||||||
email: Email,
|
email: Email,
|
||||||
user_secret_key: UserSecretKey,
|
user_secret_key: UserSecretKey,
|
||||||
@@ -121,8 +122,13 @@ impl UserStateCodeRegister {
|
|||||||
cipher_nonce,
|
cipher_nonce,
|
||||||
icon_nonce: self.icon_nonce,
|
icon_nonce: self.icon_nonce,
|
||||||
keypad,
|
keypad,
|
||||||
|
email: self.email.clone()
|
||||||
};
|
};
|
||||||
api.register_code(&self.email, &ciphered_nkode.passcode, &self.key_login, data.clone()).await?;
|
let register_data = RegisterCodeData {
|
||||||
|
code_login_data: data.clone(),
|
||||||
|
icons: self.icons.clone(),
|
||||||
|
};
|
||||||
|
api.register_code(&self.email, &ciphered_nkode.passcode, &self.key_login, register_data).await?;
|
||||||
Ok(UserStateCodeLogin {
|
Ok(UserStateCodeLogin {
|
||||||
mask: data.mask,
|
mask: data.mask,
|
||||||
email: self.email,
|
email: self.email,
|
||||||
|
|||||||
@@ -85,3 +85,4 @@ where
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,18 +2,22 @@ use std::collections::HashMap;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nkode_rs::nkode_core::keypad::Keypad;
|
use nkode_rs::nkode_core::keypad::Keypad;
|
||||||
|
use rand::Rng;
|
||||||
|
use rand::rngs::ThreadRng;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::server::repository::user_repo::UserRepo;
|
use crate::server::repository::user_repo::UserRepo;
|
||||||
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, Icon, KeyLoggedInSession};
|
use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, Icon, IconID, KeyLoggedInSession, RegisterCodeData};
|
||||||
use crate::shared::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::signed_session_data::SignedSessionData;
|
use crate::shared::signed_session_data::SignedSessionData;
|
||||||
use crate::shared::user_api::UserAPI;
|
use crate::shared::user_api::{IconPoolAPI, UserAPI};
|
||||||
|
|
||||||
pub struct InMemoryUserDB {
|
pub struct InMemoryUserDB {
|
||||||
key_session: Arc<Mutex<HashMap<Uuid, KeyLoggedInSession>>>,
|
key_session: Arc<Mutex<HashMap<Uuid, KeyLoggedInSession>>>,
|
||||||
code_session: Arc<Mutex<HashMap<Uuid, CodeLoggedInSession>>>,
|
code_session: Arc<Mutex<HashMap<Uuid, CodeLoggedInSession>>>,
|
||||||
code_data: Arc<Mutex<HashMap<Email, CodeLoginData>>>,
|
code_data_store: Arc<Mutex<HashMap<Email, CodeLoginData>>>,
|
||||||
|
owned_icons: Arc<Mutex<HashMap<IconID, Icon>>>,
|
||||||
|
icon_pool: Arc<Mutex<Vec<Icon>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InMemoryUserDB {
|
impl InMemoryUserDB {
|
||||||
@@ -21,7 +25,9 @@ impl InMemoryUserDB {
|
|||||||
Self {
|
Self {
|
||||||
key_session: Arc::new(Mutex::new(HashMap::new())),
|
key_session: Arc::new(Mutex::new(HashMap::new())),
|
||||||
code_session: Arc::new(Mutex::new(HashMap::new())),
|
code_session: Arc::new(Mutex::new(HashMap::new())),
|
||||||
code_data: Arc::new(Mutex::new(HashMap::new())),
|
code_data_store: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
owned_icons: Arc::new(Mutex::new(HashMap::new())),
|
||||||
|
icon_pool: Arc::new(Mutex::new(Vec::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,22 +55,22 @@ impl UserRepo for InMemoryUserDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn set_key_session(&self, session: KeyLoggedInSession) -> Result<(), String> {
|
async fn set_key_session(&self, session: KeyLoggedInSession) -> Result<(), String> {
|
||||||
self.key_session.lock().await.insert(session.0.session_id, session);
|
self.key_session.lock().await.insert(session.0.session_id, session).ok_or("couldn't set key session".to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_code_session(&self, session: CodeLoggedInSession) -> Result<(), String> {
|
async fn set_code_session(&self, session: CodeLoggedInSession) -> Result<(), String> {
|
||||||
self.code_session.lock().await.insert(session.0.session_id, session);
|
self.code_session.lock().await.insert(session.0.session_id, session).ok_or("couldn't set code session".to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_code_login_data(&self, email: Email, data: CodeLoginData) -> Result<(), String> {
|
async fn set_code_login_data(&self, email: Email, data: CodeLoginData) -> Result<(), String> {
|
||||||
self.code_data.lock().await.insert(email, data);
|
self.code_data_store.lock().await.insert(email, data).ok_or("couldn't set code login data")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_code_login_data(&self, email: &Email) -> Result<CodeLoginData, String> {
|
async fn get_code_login_data(&self, email: &Email) -> Result<CodeLoginData, String> {
|
||||||
self.code_data.lock().await
|
self.code_data_store.lock().await
|
||||||
.get(email)
|
.get(email)
|
||||||
.cloned()
|
.cloned()
|
||||||
.ok_or_else(|| "code login data not found for email".to_string())
|
.ok_or_else(|| "code login data not found for email".to_string())
|
||||||
@@ -73,23 +79,87 @@ impl UserRepo for InMemoryUserDB {
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl UserAPI for InMemoryUserDB {
|
impl UserAPI for InMemoryUserDB {
|
||||||
async fn get_new_icons(&self) -> Result<Vec<Icon>, String> {
|
async fn get_new_icons(&self, n: usize) -> Result<Vec<Icon>, String> {
|
||||||
todo!()
|
let mut pool = self.icon_pool.lock().await;
|
||||||
|
if n > pool.len() {
|
||||||
|
return Err(format!(
|
||||||
|
"not enough icons in pool: requested {}, have {}",
|
||||||
|
n,
|
||||||
|
pool.len()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let mut rng: ThreadRng = rand::thread_rng();
|
||||||
|
let mut out = Vec::with_capacity(n);
|
||||||
|
for _ in 0..n {
|
||||||
|
let idx = rng.gen_range(0..pool.len());
|
||||||
|
out.push(pool.swap_remove(idx));
|
||||||
|
}
|
||||||
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_login_data(&self, session: SignedSessionData<Email>) -> Result<CodeLoginData, String> {
|
async fn get_login_data(&self, session: SignedSessionData<Email>) -> Result<CodeLoginData, String> {
|
||||||
todo!()
|
let key_session = self.get_key_session(&session.session_id).await?;
|
||||||
|
if key_session.0.email != session.data {
|
||||||
|
return Err("email does not match session email".to_string());
|
||||||
|
}
|
||||||
|
session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?;
|
||||||
|
self.get_code_login_data(&session.data).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn is_code_registered(&self, session: SignedSessionData<Email>) -> Result<bool, String> {
|
async fn is_code_registered(&self, session: SignedSessionData<Email>) -> Result<bool, String> {
|
||||||
todo!()
|
let _ = self.get_login_data(session).await?;
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn set_code_login_data(&self, session: SignedSessionData<CodeLoginData>) -> Result<(), String> {
|
async fn set_code_login_data(&self, session: SignedSessionData<RegisterCodeData>) -> Result<(), String> {
|
||||||
todo!()
|
let key_session = self.get_key_session(&session.session_id).await?;
|
||||||
|
session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?;
|
||||||
|
self.code_data_store.lock().await.insert(session.data.code_login_data.email.clone(), session.data.code_login_data).ok_or("couldn't set code data".to_string())?;
|
||||||
|
let mut owned_icons = self.owned_icons.lock().await;
|
||||||
|
for icon in session.data.icons {
|
||||||
|
let icon_id = icon.id().clone();
|
||||||
|
owned_icons.insert(icon_id.clone(), icon).ok_or(format!("error inserting icon into owned icons {}", icon_id))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_keypad(&self, keypad: SignedSessionData<Keypad>) -> Result<(), String> {
|
async fn update_keypad(&self, session: SignedSessionData<Keypad>) -> Result<(), String> {
|
||||||
todo!()
|
let key_session = self.get_key_session(&session.session_id).await?;
|
||||||
|
session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?;
|
||||||
|
let email = key_session.0.email;
|
||||||
|
let mut code_data_store = self.code_data_store.lock().await;
|
||||||
|
let mut code_data = code_data_store.remove(&email).ok_or("user doesn't have code data".to_string())?;
|
||||||
|
code_data.keypad = session.data;
|
||||||
|
code_data_store.insert(email, code_data).ok_or("couldn't update keypad")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_icons(&self, icon_ids: &[IconID]) -> Result<Vec<Icon>, String> {
|
||||||
|
let owned_icons = self.owned_icons.lock().await;
|
||||||
|
let mut user_icons = Vec::<Icon>::with_capacity(icon_ids.len());
|
||||||
|
for id in icon_ids {
|
||||||
|
let icon = owned_icons.get(id).cloned().ok_or(format!("icon: {} dne", id))?;
|
||||||
|
user_icons.push(icon);
|
||||||
|
}
|
||||||
|
Ok(user_icons)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_icons(&self, session: SignedSessionData<Vec<Icon>>) -> Result<(), String> {
|
||||||
|
let key_session = self.get_key_session(&session.session_id).await?;
|
||||||
|
session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?;
|
||||||
|
let mut owned_icons = self.owned_icons.lock().await;
|
||||||
|
for icon in session.data {
|
||||||
|
let icon_id = icon.id().clone();
|
||||||
|
owned_icons.insert(icon_id.clone(), icon).ok_or(format!("error inserting icon into owned icons {}", icon_id))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl IconPoolAPI for InMemoryUserDB {
|
||||||
|
async fn add_icons(&self, icons: Vec<Icon>) -> Result<(), String> {
|
||||||
|
self.icon_pool.lock().await.extend(icons);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fmt;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nkode_rs::nkode_core::chacha20prng::Nonce; use nkode_rs::nkode_core::keypad::Keypad;
|
use nkode_rs::nkode_core::chacha20prng::Nonce; use nkode_rs::nkode_core::keypad::Keypad;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -22,9 +23,18 @@ pub struct KeyLoggedInSession(pub(crate) LoggedInSession);
|
|||||||
pub struct CodeLoggedInSession(pub(crate) LoggedInSession);
|
pub struct CodeLoggedInSession(pub(crate) LoggedInSession);
|
||||||
|
|
||||||
pub const ICON_ID_SIZE: usize = 32;
|
pub const ICON_ID_SIZE: usize = 32;
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq,Hash, Serialize, Deserialize)]
|
||||||
pub struct IconID([u8; 32]);
|
pub struct IconID([u8; 32]);
|
||||||
|
|
||||||
|
impl fmt::Display for IconID {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
for b in &self.0 {
|
||||||
|
write!(f, "{:02x}", b)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl FromBytes<ICON_ID_SIZE> for IconID {
|
impl FromBytes<ICON_ID_SIZE> for IconID {
|
||||||
fn from_array(arr: [u8; ICON_ID_SIZE]) -> Self {
|
fn from_array(arr: [u8; ICON_ID_SIZE]) -> Self {
|
||||||
Self(arr)
|
Self(arr)
|
||||||
@@ -45,22 +55,25 @@ impl Icon {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Getters, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct CodeLoginData {
|
pub struct CodeLoginData {
|
||||||
#[get = "pub"]
|
pub(crate) email: Email,
|
||||||
pub(crate) mask: Vec<u64>,
|
pub(crate) mask: Vec<u64>,
|
||||||
#[get = "pub"]
|
|
||||||
pub(crate) cipher_nonce: Nonce,
|
pub(crate) cipher_nonce: Nonce,
|
||||||
#[get = "pub"]
|
|
||||||
pub(crate) icon_nonce: Nonce,
|
pub(crate) icon_nonce: Nonce,
|
||||||
#[get = "pub"]
|
|
||||||
pub(crate) keypad: Keypad,
|
pub(crate) keypad: Keypad,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RegisterCodeData {
|
||||||
|
pub(crate) code_login_data: CodeLoginData,
|
||||||
|
pub(crate) icons: Vec<Icon>,
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait AuthAPI {
|
pub trait AuthAPI {
|
||||||
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String>;
|
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String>;
|
||||||
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, data: CodeLoginData) -> Result<(), String>;
|
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, register_data: RegisterCodeData) -> Result<(), String>;
|
||||||
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String>;
|
async fn login_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<KeyLoggedInSession, String>;
|
||||||
async fn login_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, keypad: Keypad) -> Result<CodeLoggedInSession, String>;
|
async fn login_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, keypad: Keypad) -> Result<CodeLoggedInSession, String>;
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ impl<T> SignedSessionData<T>
|
|||||||
where
|
where
|
||||||
T: Serialize + DeserializeOwned,
|
T: Serialize + DeserializeOwned,
|
||||||
{
|
{
|
||||||
/// Create a signed envelope around (session_id, issued/expires, data).
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
session_id: Uuid,
|
session_id: Uuid,
|
||||||
data: T,
|
data: T,
|
||||||
@@ -57,7 +56,6 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify signature + time bounds.
|
|
||||||
pub fn verify(&self, session_key: &OpaqueSessionKey) -> Result<(), SignedSessionError> {
|
pub fn verify(&self, session_key: &OpaqueSessionKey) -> Result<(), SignedSessionError> {
|
||||||
let expected = Self::compute_signature(
|
let expected = Self::compute_signature(
|
||||||
self.session_id,
|
self.session_id,
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use nkode_rs::nkode_core::keypad::Keypad;
|
use nkode_rs::nkode_core::keypad::Keypad;
|
||||||
use crate::shared::email::Email;
|
use crate::shared::email::Email;
|
||||||
use crate::shared::models::app::{CodeLoginData, Icon};
|
use crate::shared::models::app::{CodeLoginData, Icon, IconID, RegisterCodeData};
|
||||||
use crate::shared::signed_session_data::SignedSessionData;
|
use crate::shared::signed_session_data::SignedSessionData;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait UserAPI: Sync + Send {
|
pub trait UserAPI: Sync + Send {
|
||||||
// TODO: this should have a session
|
// TODO: this should have a session
|
||||||
async fn get_new_icons(&self) -> Result<Vec<Icon>, String>;
|
async fn get_new_icons(&self, n: usize) -> Result<Vec<Icon>, String>;
|
||||||
async fn get_login_data(&self, session: SignedSessionData<Email>) -> Result<CodeLoginData, String>;
|
async fn get_login_data(&self, session: SignedSessionData<Email>) -> Result<CodeLoginData, String>;
|
||||||
async fn is_code_registered(&self, session: SignedSessionData<Email>) -> Result<bool, String>;
|
async fn is_code_registered(&self, session: SignedSessionData<Email>) -> Result<bool, String>;
|
||||||
async fn set_code_login_data(&self, session: SignedSessionData<CodeLoginData>) -> Result<(), String>;
|
async fn set_code_login_data(&self, session: SignedSessionData<RegisterCodeData>) -> Result<(), String>;
|
||||||
async fn update_keypad(&self, keypad: SignedSessionData<Keypad>) -> Result<(), String>;
|
async fn update_keypad(&self, session: SignedSessionData<Keypad>) -> Result<(), String>;
|
||||||
|
async fn get_icons(&self, icon_ids: &[IconID]) -> Result<Vec<Icon>, String>;
|
||||||
|
async fn set_icons(&self, session: SignedSessionData<Vec<Icon>>) -> Result<(), String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait IconPoolAPI: Sync + Send {
|
||||||
|
async fn add_icons(&self, icons: Vec<Icon>) -> Result<(), String>;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user