unit client app

This commit is contained in:
2025-12-19 21:15:19 -06:00
parent 3a78a771a8
commit e1acc2eab1
10 changed files with 101 additions and 69 deletions

View File

@@ -2,20 +2,20 @@ 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::{KeyLogin, KeyRegister, UserStateCodeLogin, UserStateCodeRegister, UserStateKey}; use crate::client::states::{DisplayKeypad, 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;
pub struct ClientApp<'a,'b ,State, K,C, U, R> pub struct ClientApp<'a,'b, 'c,State, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
R: ClientRepo R: ClientRepo
{ {
auth_api: ClientAuth<'a,'b ,K,C, U>, auth_api: ClientAuth<'a,'b,'c ,K,C, U>,
client_repo: R, client_repo: R,
code_reg: Option<UserStateCodeRegister>, code_reg: Option<UserStateCodeRegister>,
key_login: Option<UserStateKey<KeyLogin>>, key_login: Option<UserStateKey<KeyLogin>>,
@@ -25,14 +25,14 @@ where
impl <'a,'b ,State, K,C, U, R>ClientApp<'a,'b ,State, K,C, U, R> impl <'a,'b,'c ,State, K,C, U, R>ClientApp<'a,'b,'c ,State, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
R: ClientRepo, R: ClientRepo,
{ {
pub fn new(auth_api: ClientAuth<'a,'b , K,C, U>, client_repo: R) -> Self { pub fn new(auth_api: ClientAuth<'a,'b,'c, K,C, U>, client_repo: R) -> Self {
Self { Self {
auth_api, auth_api,
client_repo, client_repo,
@@ -43,7 +43,7 @@ where
} }
} }
fn into_state<NextState>(self) -> ClientApp<'a,'b, NextState, K,C, U, R> { fn into_state<NextState>(self) -> ClientApp<'a,'b,'c, NextState, K,C, U, R> {
ClientApp { ClientApp {
auth_api: self.auth_api, auth_api: self.auth_api,
client_repo: self.client_repo, client_repo: self.client_repo,
@@ -56,24 +56,24 @@ where
} }
pub struct NewUserRegisterKey; pub struct NewUserRegisterKey;
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, NewUserRegisterKey, K,C, U, R> impl <'a,'b, 'c,K, C, U, R> ClientApp<'a,'b,'c, NewUserRegisterKey, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
R: ClientRepo, R: ClientRepo,
{ {
pub async fn new_user(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b, NewUserRegisterCode, K,C, U, R>, String> { pub async fn new_user(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b,'c, NewUserRegisterCode, K,C, U, R>, 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 key reg: {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))
} }
}; };
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 key login: {e:?}"))?;
let code_register = key_logged_in.to_register_code(&self.auth_api).await?; let code_register = key_logged_in.to_register_code(&self.auth_api).await?;
self.code_reg = Some(code_register); self.code_reg = Some(code_register);
Ok(self.into_state::<NewUserRegisterCode>()) Ok(self.into_state::<NewUserRegisterCode>())
@@ -82,7 +82,7 @@ where
pub struct NewUserRegisterCode; pub struct NewUserRegisterCode;
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, NewUserRegisterCode, K,C, U, R> impl <'a,'b,'c, K, C, U, R> ClientApp<'a,'b,'c, NewUserRegisterCode, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
@@ -98,7 +98,7 @@ where
) )
} }
pub async fn new_user_register_code(mut self, selected_icons: Vec<IconID>) -> Result<ClientApp<'a,'b, UserCodeLogin, K,C, U, R>, String> { pub async fn new_user_register_code(mut self, selected_icons: Vec<IconID>) -> Result<ClientApp<'a,'b,'c, UserCodeLogin, K,C, U, R>, String> {
let code_register = self.code_reg.ok_or("no code register".to_string())?; let code_register = self.code_reg.ok_or("no code register".to_string())?;
self.code_reg = None; self.code_reg = None;
self.code_login = Some(code_register.clone().register(&self.auth_api,selected_icons).await?); self.code_login = Some(code_register.clone().register(&self.auth_api,selected_icons).await?);
@@ -109,14 +109,14 @@ where
pub struct UserKeyLogin; pub struct UserKeyLogin;
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, UserKeyLogin, K,C, U, R> impl <'a,'b, 'c,K, C, U, R> ClientApp<'a,'b, 'c,UserKeyLogin, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
R: ClientRepo, R: ClientRepo,
{ {
pub async fn login(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b, UserCodeLogin,K,C, U, R>, String> { pub async fn login(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b, 'c,UserCodeLogin,K,C, U, R>, String> {
self.code_login = Some( self.code_login = Some(
UserStateKey::<KeyLogin>::new(email, secret_key) UserStateKey::<KeyLogin>::new(email, secret_key)
.login(&self.auth_api).await? .login(&self.auth_api).await?
@@ -127,20 +127,20 @@ where
} }
pub struct UserCodeLogin; pub struct UserCodeLogin;
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, UserCodeLogin, K,C, U, R> impl <'a,'b,'c, K, C, U, R> ClientApp<'a,'b,'c, UserCodeLogin, K,C, U, R>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
R: ClientRepo, R: ClientRepo,
{ {
pub async fn get_keypad(&self) -> Result<Vec<Vec<Icon>>, String> { pub fn get_keypad(&self) -> Result<DisplayKeypad, String> {
let code_login = self.code_login.clone().ok_or("no code login")?; let code_login = self.code_login.clone().ok_or("no code login")?;
Ok(code_login.get_keypad().await) Ok(code_login.get_keypad())
} }
pub async fn login(self, key_selection: Vec<usize>) -> Result<(), String> { pub async fn login(self, key_selection: &Vec<usize>) -> Result<(), String> {
let code_login = self.code_login.unwrap().login(&self.auth_api, &key_selection).await?; let code_login = self.code_login.unwrap().login(&self.auth_api, key_selection).await?;
self.client_repo.add_code_logged_in(code_login.0.email.clone(), code_login).await self.client_repo.add_code_logged_in(code_login.0.email.clone(), code_login).await
} }
} }

View File

@@ -9,7 +9,7 @@ use crate::client::opaque::{OpaqueAuthData, OpaqueAuth, ServerConnectionLogin, S
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::UserAPI;
pub struct ClientAuth<'a,'b, K,C, U> pub struct ClientAuth<'a,'b,'c, K,C, U>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
@@ -19,16 +19,16 @@ where
opaque_key_login: OpaqueAuth<'a, K>, opaque_key_login: OpaqueAuth<'a, K>,
opaque_code_register: OpaqueAuth<'b, C>, opaque_code_register: OpaqueAuth<'b, C>,
opaque_code_login: OpaqueAuth<'b, C>, opaque_code_login: OpaqueAuth<'b, C>,
user_api: U user_api: &'c U
} }
impl<'a,'b, K,C, U> ClientAuth<'a,'b, K,C, U> impl<'a,'b,'c, K,C, U> ClientAuth<'a,'b,'c, K,C, U>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI, U: UserAPI,
{ {
pub fn new(key_connection: &'a K, code_connection: &'b C, user_api: U) -> Self { pub fn new(key_connection: &'a K, code_connection: &'b C, user_api: &'c U) -> Self {
Self{ Self{
opaque_key_register: OpaqueAuth::new(key_connection), opaque_key_register: OpaqueAuth::new(key_connection),
opaque_key_login: OpaqueAuth::new(key_connection), opaque_key_login: OpaqueAuth::new(key_connection),
@@ -41,7 +41,7 @@ where
#[async_trait] #[async_trait]
impl<'a,'b, K,C, U> AuthAPI for ClientAuth<'a,'b, K,C, U> impl<'a,'b,'c, K,C, U> AuthAPI for ClientAuth<'a,'b,'c, K,C, U>
where where
K: ServerConnectionRegister + ServerConnectionLogin, K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin, C: ServerConnectionRegister + ServerConnectionLogin,
@@ -53,6 +53,7 @@ where
} }
async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, register_code_data: RegisterCodeData) -> Result<(), String> { async fn register_code(&self, email: &Email, passcode: &[u64], key_login_session: &KeyLoggedInSession, register_code_data: RegisterCodeData) -> Result<(), String> {
println!("register passcode: {:?}", passcode.to_vec());
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(
@@ -70,6 +71,7 @@ where
} }
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> {
println!("login passcode: {:?}", passcode.to_vec());
let auth_data = OpaqueAuthData::from_code(email.as_str(), passcode); let auth_data = OpaqueAuthData::from_code(email.as_str(), passcode);
let signed_session = SignedSessionData::new( let signed_session = SignedSessionData::new(
key_login_session.0.session_id, key_login_session.0.session_id,
@@ -77,7 +79,7 @@ where
&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.update_keypad(signed_session).await?; self.user_api.update_keypad(signed_session).await?;
let session = self.opaque_code_login.login(&auth_data).await.map_err(|e| format!("error: {}", e))?; let session = self.opaque_code_login.login(&auth_data).await.map_err(|e| format!("opaque code login error: {}", e))?;
Ok(CodeLoggedInSession(session)) Ok(CodeLoggedInSession(session))
} }

View File

@@ -114,7 +114,7 @@ impl UserStateCodeRegister {
let (cipher, cipher_nonce) = NKodeCipher::new(policy, &secret_key); let (cipher, cipher_nonce) = NKodeCipher::new(policy, &secret_key);
let passcode_idx: Vec<usize> = selected_icons let passcode_idx: Vec<usize> = selected_icons
.iter() .iter()
.filter_map(|x1| self.icons.iter().position(|x| x.id() == x1)) .filter_map(|x1| self.icons.iter().position(|x| x.id == *x1))
.collect(); .collect();
let ciphered_nkode = cipher.encipher(&passcode_idx).unwrap(); let ciphered_nkode = cipher.encipher(&passcode_idx).unwrap();
let data = CodeLoginData{ let data = CodeLoginData{
@@ -143,6 +143,10 @@ impl UserStateCodeRegister {
self.icons.clone() self.icons.clone()
} }
} }
pub struct DisplayKeypad {
pub sorted_icons: Vec<Icon>,
pub keypad: Keypad,
}
#[derive(Clone)] #[derive(Clone)]
pub struct UserStateCodeLogin { pub struct UserStateCodeLogin {
@@ -169,7 +173,10 @@ impl UserStateCodeLogin {
todo!() todo!()
} }
pub async fn get_keypad(&self) -> Vec<Vec<Icon>> { pub fn get_keypad(&self) -> DisplayKeypad {
todo!() DisplayKeypad{
sorted_icons: self.sort_icons(),
keypad: self.keypad.clone(),
}
} }
} }

View File

@@ -15,7 +15,7 @@ pub struct ServerApp<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> {
server_setup: NKodeServerSetup, server_setup: NKodeServerSetup,
opaque_db: R, opaque_db: R,
opaque_sess: S, opaque_sess: S,
user_db: U, pub user_db: U,
} }
impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U> { impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U> {
@@ -85,7 +85,7 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U
Ok(OpaqueLoginSession { session_id: cache.session_id, response: start.message }) Ok(OpaqueLoginSession { session_id: cache.session_id, response: start.message })
} }
pub async fn login_finish( pub async fn login_finish<K: CredKind>(
&self, &self,
session_id: &Uuid, session_id: &Uuid,
finalize: CredentialFinalization<NKodeCipherSuite>, finalize: CredentialFinalization<NKodeCipherSuite>,
@@ -93,7 +93,6 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U
let cache = self.opaque_sess let cache = self.opaque_sess
.get_login_session(session_id).await .get_login_session(session_id).await
.map_err(|e| format!("get login session: {e}"))?; .map_err(|e| format!("get login session: {e}"))?;
let finish = cache.server_login let finish = cache.server_login
.finish(finalize, ServerLoginParameters::default()) .finish(finalize, ServerLoginParameters::default())
.map_err(|e| format!("opaque login finish: {e:?}"))?; .map_err(|e| format!("opaque login finish: {e:?}"))?;
@@ -101,11 +100,13 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U
.clear_login_session(session_id).await .clear_login_session(session_id).await
.map_err(|e| format!("clear login session: {e}"))?; .map_err(|e| format!("clear login session: {e}"))?;
let session_key = OpaqueSessionKey::from_bytes(&finish.session_key.to_vec()).unwrap(); let session_key = OpaqueSessionKey::from_bytes(&finish.session_key.to_vec()).unwrap();
Ok(LoggedInSession{ let session = LoggedInSession{
session_id: Uuid::new_v4(), session_id: Uuid::new_v4(),
email: Email::from_bytes(cache.identifiers.as_slice()).unwrap(), email: Email::from_bytes(cache.identifiers.as_slice()).unwrap(),
session_key session_key
}) };
K::set_session(&self.user_db, session.clone()).await?;
Ok(session)
} }
pub async fn key_login_finish( pub async fn key_login_finish(
@@ -113,7 +114,7 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U
session_id: &Uuid, session_id: &Uuid,
finalize: CredentialFinalization<NKodeCipherSuite>, finalize: CredentialFinalization<NKodeCipherSuite>,
) -> Result<KeyLoggedInSession, String> { ) -> Result<KeyLoggedInSession, String> {
let session = KeyLoggedInSession(self.login_finish(session_id, finalize).await?); let session = KeyLoggedInSession(self.login_finish::<Key>(session_id, finalize).await?);
self.user_db.set_key_session(session.clone()).await?; self.user_db.set_key_session(session.clone()).await?;
Ok(session) Ok(session)
} }
@@ -123,7 +124,7 @@ impl<R: OpaqueDatabaseRepo, S: OpaqueSessionRepo, U: UserRepo> ServerApp<R, S, U
session_id: &Uuid, session_id: &Uuid,
finalize: CredentialFinalization<NKodeCipherSuite>, finalize: CredentialFinalization<NKodeCipherSuite>,
) -> Result<CodeLoggedInSession, String> { ) -> Result<CodeLoggedInSession, String> {
let session = CodeLoggedInSession(self.login_finish(session_id, finalize).await?); let session = CodeLoggedInSession(self.login_finish::<Code>(session_id, finalize).await?);
self.user_db.set_code_session(session.clone()).await?; self.user_db.set_code_session(session.clone()).await?;
Ok(session) Ok(session)
} }
@@ -162,6 +163,10 @@ impl CredKind for Key {
async fn set_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { async fn set_password_file<R: OpaqueDatabaseRepo>(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> {
repo.new_key(id, pf).await repo.new_key(id, pf).await
} }
async fn set_session<R: UserRepo>(repo: &R, session: LoggedInSession) -> Result<(), String> {
repo.set_key_session(KeyLoggedInSession(session)).await
}
} }
#[async_trait] #[async_trait]
@@ -182,4 +187,8 @@ impl CredKind for Code {
Err(AuthRepoError::KeyNotRegistered) Err(AuthRepoError::KeyNotRegistered)
} }
} }
async fn set_session<R: UserRepo>(repo: &R, session: LoggedInSession) -> Result<(), String> {
repo.set_code_session(CodeLoggedInSession(session)).await
}
} }

View File

@@ -2,6 +2,8 @@ use async_trait::async_trait;
use uuid::Uuid; use uuid::Uuid;
use opaque_ke::ServerLogin; use opaque_ke::ServerLogin;
use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo}; use crate::server::repository::opaque_repo::{AuthRepoError, OpaqueDatabaseRepo};
use crate::server::repository::user_repo::UserRepo;
use crate::shared::models::app::LoggedInSession;
use crate::shared::opaque::{NKodeCipherSuite, PasswordFile}; use crate::shared::opaque::{NKodeCipherSuite, PasswordFile};
pub struct RegCache { pub struct RegCache {
@@ -23,4 +25,5 @@ pub trait CredKind: Send + Sync {
async fn prereq_for_register<R: OpaqueDatabaseRepo>(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> { async fn prereq_for_register<R: OpaqueDatabaseRepo>(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> {
Ok(()) Ok(())
} }
async fn set_session<R: UserRepo>(repo: &R, session: LoggedInSession) -> Result<(), String>;
} }

View File

@@ -79,7 +79,7 @@ where
) -> Result<LoggedInSession, ClientAuthError> { ) -> Result<LoggedInSession, ClientAuthError> {
Ok(self Ok(self
.auth_db .auth_db
.login_finish(session_id, message.clone()) .login_finish::<K>(session_id, message.clone())
.await .await
.map_err(|e| ClientAuthError::Transport(e))? .map_err(|e| ClientAuthError::Transport(e))?
) )

View File

@@ -44,28 +44,28 @@ impl UserRepo for InMemoryUserDB {
self.key_session.lock().await self.key_session.lock().await
.get(&session_id) .get(&session_id)
.cloned() .cloned()
.ok_or_else(|| format!("key session not found for session_id={}", session_id)) .ok_or(format!("key session not found for session_id={}", session_id))
} }
async fn get_code_session(&self, session_id: &Uuid) -> Result<CodeLoggedInSession, String> { async fn get_code_session(&self, session_id: &Uuid) -> Result<CodeLoggedInSession, String> {
self.code_session.lock().await self.code_session.lock().await
.get(&session_id) .get(&session_id)
.cloned() .cloned()
.ok_or_else(|| format!("code session not found for session_id={}", session_id)) .ok_or(format!("code session not found for session_id={}", session_id))
} }
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).ok_or("couldn't set key session".to_string())?; self.key_session.lock().await.insert(session.0.session_id, session);
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).ok_or("couldn't set code session".to_string())?; self.code_session.lock().await.insert(session.0.session_id, session);
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_store.lock().await.insert(email, data).ok_or("couldn't set code login data")?; self.code_data_store.lock().await.insert(email, data);
Ok(()) Ok(())
} }
@@ -114,11 +114,11 @@ impl UserAPI for InMemoryUserDB {
async fn set_code_login_data(&self, session: SignedSessionData<RegisterCodeData>) -> Result<(), String> { async fn set_code_login_data(&self, session: SignedSessionData<RegisterCodeData>) -> Result<(), String> {
let key_session = self.get_key_session(&session.session_id).await?; 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:?}"))?; 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())?; self.code_data_store.lock().await.insert(session.data.code_login_data.email.clone(), session.data.code_login_data);
let mut owned_icons = self.owned_icons.lock().await; let mut owned_icons = self.owned_icons.lock().await;
for icon in session.data.icons { for icon in session.data.icons {
let icon_id = icon.id().clone(); let icon_id = icon.id.clone();
owned_icons.insert(icon_id.clone(), icon).ok_or(format!("error inserting icon into owned icons {}", icon_id))?; owned_icons.insert(icon_id.clone(), icon);
} }
Ok(()) Ok(())
} }
@@ -130,7 +130,7 @@ impl UserAPI for InMemoryUserDB {
let mut code_data_store = self.code_data_store.lock().await; 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())?; 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.keypad = session.data;
code_data_store.insert(email, code_data).ok_or("couldn't update keypad")?; code_data_store.insert(email, code_data);
Ok(()) Ok(())
} }
@@ -149,8 +149,8 @@ impl UserAPI for InMemoryUserDB {
session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?; session.verify(&key_session.0.session_key).map_err(|e| format!("session error: {e:?}"))?;
let mut owned_icons = self.owned_icons.lock().await; let mut owned_icons = self.owned_icons.lock().await;
for icon in session.data { for icon in session.data {
let icon_id = icon.id().clone(); let icon_id = icon.id.clone();
owned_icons.insert(icon_id.clone(), icon).ok_or(format!("error inserting icon into owned icons {}", icon_id))?; owned_icons.insert(icon_id.clone(), icon);
} }
Ok(()) Ok(())
} }

View File

@@ -2,7 +2,6 @@ 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};
use getset::Getters;
use nkode_rs::from_bytes::FromBytes; use nkode_rs::from_bytes::FromBytes;
use nkode_rs::nkode_core::policy::NKodePolicy; use nkode_rs::nkode_core::policy::NKodePolicy;
use uuid::Uuid; use uuid::Uuid;
@@ -24,7 +23,7 @@ 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,Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq,Hash, Serialize, Deserialize)]
pub struct IconID([u8; 32]); pub struct IconID(pub [u8; 32]);
impl fmt::Display for IconID { impl fmt::Display for IconID {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@@ -41,12 +40,10 @@ impl FromBytes<ICON_ID_SIZE> for IconID {
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize, Getters)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Icon { pub struct Icon {
#[get = "pub"] pub id: IconID,
id: IconID, pub data: Vec<u8>,
#[get = "pub"]
data: Vec<u8>,
} }
impl Icon { impl Icon {

View File

@@ -8,26 +8,37 @@ use nkode_protocol::server::repository::in_memory::in_memory_opaque_session::InM
use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemoryServerTransport}; use nkode_protocol::server::repository::in_memory::in_memory_transport::{InMemoryCodeServer, InMemoryKeyServer, InMemoryServerTransport};
use nkode_protocol::server::repository::in_memory::in_memory_user_db::InMemoryUserDB; use nkode_protocol::server::repository::in_memory::in_memory_user_db::InMemoryUserDB;
use nkode_protocol::shared::email::Email; use nkode_protocol::shared::email::Email;
use nkode_protocol::shared::models::app::IconID; use nkode_protocol::shared::models::app::{Icon, IconID, ICON_ID_SIZE};
use nkode_protocol::shared::opaque::{NKodeServerSetup, UserSecretKey}; use nkode_protocol::shared::opaque::{NKodeServerSetup, UserSecretKey};
use nkode_protocol::shared::user_api::IconPoolAPI;
#[tokio::test] #[tokio::test]
async fn in_memory_client_app() { async fn in_memory_client_app() {
let mut rng = OsRng; let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng); let server_setup = NKodeServerSetup::new(&mut rng);
let user_db = InMemoryUserDB::new();
let n: usize = 200;
let mut new_icons: Vec<Icon> = Vec::with_capacity(n);
for i in 0u8..(n as u8) {
let id = IconID([i; ICON_ID_SIZE]); // [0; 32], [1; 32], ... [199; 32]
new_icons.push(Icon {
id,
data: vec![i], // dummy payload; adjust as needed
});
}
user_db.add_icons(new_icons).await.unwrap();
let server = ServerApp::new( let server = ServerApp::new(
server_setup, server_setup,
InMemoryOpaqueDB::new(), InMemoryOpaqueDB::new(),
InMemoryOpaqueSession::new(), InMemoryOpaqueSession::new(),
InMemoryUserDB::new() user_db,
); );
let key_transport: InMemoryKeyServer = InMemoryServerTransport::new(&server); let key_transport: InMemoryKeyServer = InMemoryServerTransport::new(&server);
let code_transport: InMemoryCodeServer = InMemoryServerTransport::new(&server); let code_transport: InMemoryCodeServer = InMemoryServerTransport::new(&server);
let user_db = InMemoryUserDB::new(); let client_auth = ClientAuth::new(&key_transport, &code_transport, &server.user_db);
let client_auth = ClientAuth::new(&key_transport, &code_transport, user_db);
let client_repo = InMemoryClientRepo::new(); let client_repo = InMemoryClientRepo::new();
let client_app_new_user: ClientApp< let new_user: ClientApp<
'_,
'_, '_,
'_, '_,
NewUserRegisterKey, NewUserRegisterKey,
@@ -38,12 +49,15 @@ async fn in_memory_client_app() {
> = ClientApp::new(client_auth, client_repo); > = ClientApp::new(client_auth, client_repo);
let user_email = Email::new("a@b.com").unwrap(); let user_email = Email::new("a@b.com").unwrap();
let user_secret_key = UserSecretKey::new(); let user_secret_key = UserSecretKey::new();
let client_app_register_code = client_app_new_user.new_user(user_email, user_secret_key).await.unwrap(); let register_code = new_user.new_user(user_email, user_secret_key).await.unwrap();
let _: Vec<IconID> = client_app_register_code let icons: Vec<Icon> = register_code.get_new_user_icons().await.unwrap();
.get_new_user_icons().await.unwrap() let icon_ids:Vec<IconID> = icons.iter().map(|el| el.id.clone()).collect();
.iter() let selected_icons = icon_ids[0..4].to_vec();
.map( let code_login = register_code.new_user_register_code(selected_icons.clone()).await.unwrap();
|el| el.id().clone() let display_keypad = code_login.get_keypad().unwrap();
).collect(); let icon_idxs: Vec<usize> = selected_icons.iter().map(|x| {
// let client_app_user_login = client_app_register_code.new_user_register_code(icons[0..4].to_vec()).await.unwrap(); display_keypad.sorted_icons.iter().position(|x1| {x1.id == *x}).unwrap()
}).collect();
let selected_keys = display_keypad.keypad.select_keys(&icon_idxs).unwrap();
code_login.login(&selected_keys).await.unwrap();
} }