testing client app

This commit is contained in:
2025-12-19 18:05:45 -06:00
parent f49aa97b0f
commit 3a78a771a8
8 changed files with 200 additions and 62 deletions

View File

@@ -1,4 +1,3 @@
use std::collections::HashMap;
use std::marker::PhantomData;
use crate::client::client_auth_api::ClientAuth;
use crate::client::opaque::{ServerConnectionLogin, ServerConnectionRegister};
@@ -9,14 +8,15 @@ use crate::shared::email::Email;
use crate::shared::opaque::UserSecretKey;
use crate::shared::user_api::UserAPI;
struct ClientApp<'a, State, R, U, C>
pub struct ClientApp<'a,'b ,State, K,C, U, R>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
C: ClientRepo
R: ClientRepo
{
auth_api: ClientAuth<'a, R, U>,
client_repo: C,
auth_api: ClientAuth<'a,'b ,K,C, U>,
client_repo: R,
code_reg: Option<UserStateCodeRegister>,
key_login: Option<UserStateKey<KeyLogin>>,
code_login: Option<UserStateCodeLogin>,
@@ -25,13 +25,14 @@ where
impl <'a,State, R, U, C>ClientApp<'a,State, R, U, C>
impl <'a,'b ,State, K,C, U, R>ClientApp<'a,'b ,State, K,C, U, R>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
C: ClientRepo,
R: ClientRepo,
{
pub fn new(auth_api: ClientAuth<'a, R, U>, client_repo: C) -> Self {
pub fn new(auth_api: ClientAuth<'a,'b , K,C, U>, client_repo: R) -> Self {
Self {
auth_api,
client_repo,
@@ -41,7 +42,8 @@ where
_state: PhantomData
}
}
fn into_state<NextState>(self) -> ClientApp<'a, NextState, R, U, C> {
fn into_state<NextState>(self) -> ClientApp<'a,'b, NextState, K,C, U, R> {
ClientApp {
auth_api: self.auth_api,
client_repo: self.client_repo,
@@ -54,13 +56,14 @@ where
}
pub struct NewUserRegisterKey;
impl <'a, R, U, C>ClientApp<'a, NewUserRegisterKey, R, U, C>
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, NewUserRegisterKey, K,C, U, R>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
C: ClientRepo,
R: ClientRepo,
{
async fn new_user(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a, NewUserRegisterCode, R, U, C>, String> {
pub async fn new_user(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b, NewUserRegisterCode, K,C, U, R>, String> {
let key_register = UserStateKey::<KeyRegister>::new(email.clone(), secret_key.clone());
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:?}")) {
@@ -71,35 +74,22 @@ where
}
};
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.to_register_code(&self.auth_api).await?;
self.code_reg = Some(code_register);
Ok(self.into_state::<NewUserRegisterCode>())
}
}
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>
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, NewUserRegisterCode, K,C, U, R>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
C: ClientRepo,
R: ClientRepo,
{
async fn get_new_user_icons(&self) -> Result<Vec<Icon>, String> {
pub async fn get_new_user_icons(&self) -> Result<Vec<Icon>, String> {
Ok(
self.code_reg
.clone()
@@ -108,18 +98,49 @@ where
)
}
async fn new_user_register_code(mut self, email: Email, selected_icons: Vec<IconID>) -> Result<ClientApp<'a, NewUserRegisterCode, R, U, C>, String> {
pub async fn new_user_register_code(mut self, selected_icons: Vec<IconID>) -> Result<ClientApp<'a,'b, UserCodeLogin, K,C, U, R>, 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()
self.code_reg = None;
self.code_login = Some(code_register.clone().register(&self.auth_api,selected_icons).await?);
Ok(self.into_state::<UserCodeLogin>())
}
}
// 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,'b, K, C, U, R> ClientApp<'a,'b, UserKeyLogin, K,C, U, R>
where
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
R: ClientRepo,
{
pub async fn login(mut self, email: Email, secret_key: UserSecretKey) -> Result<ClientApp<'a,'b, UserCodeLogin,K,C, U, R>, String> {
self.code_login = Some(
UserStateKey::<KeyLogin>::new(email, secret_key)
.login(&self.auth_api).await?
.to_code_login(&self.auth_api).await?
);
Ok(self.into_state::<UserCodeLogin>())
}
}
pub struct UserCodeLogin;
impl <'a,'b, K, C, U, R> ClientApp<'a,'b, UserCodeLogin, K,C, U, R>
where
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
R: ClientRepo,
{
pub async fn get_keypad(&self) -> Result<Vec<Vec<Icon>>, String> {
let code_login = self.code_login.clone().ok_or("no code login")?;
Ok(code_login.get_keypad().await)
}
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?;
self.client_repo.add_code_logged_in(code_login.0.email.clone(), code_login).await
}
}

View File

@@ -9,22 +9,42 @@ use crate::client::opaque::{OpaqueAuthData, OpaqueAuth, ServerConnectionLogin, S
use crate::shared::signed_session_data::SignedSessionData;
use crate::shared::user_api::UserAPI;
pub struct ClientAuth<'a, R, U>
pub struct ClientAuth<'a,'b, K,C, U>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI
{
opaque_key_register: OpaqueAuth<'a, R>,
opaque_key_login: OpaqueAuth<'a, R>,
opaque_code_register: OpaqueAuth<'a, R>,
opaque_code_login: OpaqueAuth<'a, R>,
opaque_key_register: OpaqueAuth<'a, K>,
opaque_key_login: OpaqueAuth<'a, K>,
opaque_code_register: OpaqueAuth<'b, C>,
opaque_code_login: OpaqueAuth<'b, C>,
user_api: U
}
#[async_trait]
impl<'a, R, U> AuthAPI for ClientAuth<'a, R, U>
impl<'a,'b, K,C, U> ClientAuth<'a,'b, K,C, U>
where
R: ServerConnectionRegister + ServerConnectionLogin,
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
{
pub fn new(key_connection: &'a K, code_connection: &'b C, user_api: U) -> Self {
Self{
opaque_key_register: OpaqueAuth::new(key_connection),
opaque_key_login: OpaqueAuth::new(key_connection),
opaque_code_login: OpaqueAuth::new(code_connection),
opaque_code_register: OpaqueAuth::new(code_connection),
user_api
}
}
}
#[async_trait]
impl<'a,'b, K,C, U> AuthAPI for ClientAuth<'a,'b, K,C, U>
where
K: ServerConnectionRegister + ServerConnectionLogin,
C: ServerConnectionRegister + ServerConnectionLogin,
U: UserAPI,
{
async fn register_key(&self, email: &Email, secret_key: &UserSecretKey) -> Result<(), String> {

View File

@@ -2,4 +2,4 @@ pub mod client_auth_api;
pub mod opaque;
pub mod states;
pub mod app;
mod repo;
pub mod repo;

View File

@@ -87,8 +87,8 @@ pub trait ServerConnectionLogin: Send + Sync {
pub struct OpaqueAuth<'a, S>(&'a S);
impl<'a, S> OpaqueAuth<'a, S> {
pub fn new(server: &'a S) -> Self {
Self(server)
pub fn new(server_connection: &'a S) -> Self {
Self(server_connection)
}
}

View File

@@ -2,16 +2,63 @@ use async_trait::async_trait;
use crate::shared::email::Email;
use crate::shared::models::app::CodeLoggedInSession;
use crate::shared::opaque::UserSecretKey;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
#[async_trait]
pub trait ClientRepo {
async fn add_secret_key(&self, email: Email, user_secret_key: UserSecretKey) -> Result<(), String>;
async fn remove_secret_key(&self, email: &Email) -> Result<(), String>;
async fn add_code_logged_in(&self, email: &Email, login_session: CodeLoggedInSession) -> Result<(), String>;
async fn add_code_logged_in(&self, email: Email, login_session: CodeLoggedInSession) -> Result<(), String>;
async fn get_code_logged_in(&self, email: &Email) -> Result<CodeLoggedInSession, String>;
}
// TODO: Implement in memory repo
#[derive(Debug, Default)]
pub struct InMemoryClientRepo {
secret_keys: RwLock<HashMap<Email, UserSecretKey>>,
code_logged_in: RwLock<HashMap<Email, CodeLoggedInSession>>,
}
impl InMemoryClientRepo {
pub fn new() -> Self {
Self::default()
}
/// Convenience if you want to share it across services.
pub fn shared() -> Arc<Self> {
Arc::new(Self::new())
}
}
#[async_trait]
impl ClientRepo for InMemoryClientRepo {
async fn add_secret_key(&self, email: Email, user_secret_key: UserSecretKey) -> Result<(), String> {
let mut map = self.secret_keys.write().await;
map.insert(email, user_secret_key);
Ok(())
}
async fn remove_secret_key(&self, email: &Email) -> Result<(), String> {
let mut map = self.secret_keys.write().await;
map.remove(email);
Ok(())
}
async fn add_code_logged_in(&self, email: Email, login_session: CodeLoggedInSession) -> Result<(), String> {
let mut map = self.code_logged_in.write().await;
map.insert(email, login_session);
Ok(())
}
async fn get_code_logged_in(&self, email: &Email) -> Result<CodeLoggedInSession, String> {
let map = self.code_logged_in.read().await;
map.get(email)
.cloned()
.ok_or_else(|| "code_logged_in session not found".to_string())
}
}
// TODO: Implement flutter-storage
// https://chatgpt.com/c/69441737-c990-8333-9737-7ac75232da1d

View File

@@ -53,7 +53,7 @@ pub struct UserStateKeyLoggedIn {
}
impl UserStateKeyLoggedIn {
pub async fn register_code(self, api: &dyn AuthAPI) -> Result<UserStateCodeRegister, String> {
pub async fn to_register_code(self, api: &dyn AuthAPI) -> Result<UserStateCodeRegister, String> {
let icon_nonce = Nonce::new();
let icons = self.get_icons(api, &icon_nonce).await?;
Ok(UserStateCodeRegister {
@@ -77,7 +77,7 @@ impl UserStateKeyLoggedIn {
Ok(icons)
}
pub async fn login_code(self, api: &dyn AuthAPI) -> Result<UserStateCodeLogin, String> {
pub async fn to_code_login(self, api: &dyn AuthAPI) -> Result<UserStateCodeLogin, String> {
let login_data = api.get_login_data(&self.key_login).await?;
let icons = self.get_icons(api, &login_data.icon_nonce).await?;
let policy = api.get_policy().await?;
@@ -144,6 +144,7 @@ impl UserStateCodeRegister {
}
}
#[derive(Clone)]
pub struct UserStateCodeLogin {
email: Email,
mask: Vec<u64>,

View File

@@ -13,7 +13,7 @@ use rand::rngs::OsRng;
const USER_KEY_SIZE: usize = 16;
#[derive(Clone, Eq, PartialEq)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct UserSecretKey(Zeroizing<[u8; USER_KEY_SIZE]>);
impl UserSecretKey {

View File

@@ -0,0 +1,49 @@
use opaque_ke::argon2::password_hash::rand_core::OsRng;
use nkode_protocol::client::app::{ClientApp, NewUserRegisterKey};
use nkode_protocol::client::client_auth_api::ClientAuth;
use nkode_protocol::client::repo::InMemoryClientRepo;
use nkode_protocol::server::app::ServerApp;
use nkode_protocol::server::repository::in_memory::in_memory_opaque_db::InMemoryOpaqueDB;
use nkode_protocol::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaqueSession;
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::shared::email::Email;
use nkode_protocol::shared::models::app::IconID;
use nkode_protocol::shared::opaque::{NKodeServerSetup, UserSecretKey};
#[tokio::test]
async fn in_memory_client_app() {
let mut rng = OsRng;
let server_setup = NKodeServerSetup::new(&mut rng);
let server = ServerApp::new(
server_setup,
InMemoryOpaqueDB::new(),
InMemoryOpaqueSession::new(),
InMemoryUserDB::new()
);
let key_transport: InMemoryKeyServer = InMemoryServerTransport::new(&server);
let code_transport: InMemoryCodeServer = InMemoryServerTransport::new(&server);
let user_db = InMemoryUserDB::new();
let client_auth = ClientAuth::new(&key_transport, &code_transport, user_db);
let client_repo = InMemoryClientRepo::new();
let client_app_new_user: ClientApp<
'_,
'_,
NewUserRegisterKey,
InMemoryServerTransport<'_, _>,
InMemoryServerTransport<'_, _>,
_,
_
> = ClientApp::new(client_auth, client_repo);
let user_email = Email::new("a@b.com").unwrap();
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 _: Vec<IconID> = client_app_register_code
.get_new_user_icons().await.unwrap()
.iter()
.map(
|el| el.id().clone()
).collect();
// let client_app_user_login = client_app_register_code.new_user_register_code(icons[0..4].to_vec()).await.unwrap();
}