From fe499add9e71fba0436d91e34f7157b6c3a20b60 Mon Sep 17 00:00:00 2001 From: Donovan Date: Wed, 17 Dec 2025 17:57:42 -0600 Subject: [PATCH] refactor mutibility --- src/server/app.rs | 38 ++++++------ src/server/models.rs | 2 +- .../in_memory/in_memory_transport.rs | 28 ++++----- .../repository/in_memory/in_memory_user_db.rs | 42 +++++++------ src/server/repository/user_repo.rs | 14 +++-- src/shared/models/app.rs | 2 - src/shared/models/email.rs | 61 +------------------ tests/in_memory_test.rs | 3 +- 8 files changed, 64 insertions(+), 126 deletions(-) diff --git a/src/server/app.rs b/src/server/app.rs index 83caa45..49441a4 100644 --- a/src/server/app.rs +++ b/src/server/app.rs @@ -23,7 +23,7 @@ impl ServerApp( - &mut self, + &self, identifier: &[u8], request: RegistrationRequest, ) -> Result { @@ -42,7 +42,7 @@ impl ServerApp( - &mut self, + &self, session_id: &Uuid, password_file: PasswordFile, ) -> Result<(), String> { @@ -51,7 +51,7 @@ impl ServerApp ServerApp( - &mut self, + &self, identifier: &[u8], request: CredentialRequest, ) -> Result { @@ -85,7 +85,7 @@ impl ServerApp, ) -> Result { @@ -108,39 +108,39 @@ impl ServerApp, ) -> Result { let session = KeyLoggedInSession(self.login_finish(session_id, finalize).await?); - self.user_db.set_key_session(session.clone())?; + self.user_db.set_key_session(session.clone()).await?; Ok(session) } pub async fn code_login_finish( - &mut self, + &self, session_id: &Uuid, finalize: CredentialFinalization, ) -> Result { let session = CodeLoggedInSession(self.login_finish(session_id, finalize).await?); - self.user_db.set_code_session(session.clone())?; + self.user_db.set_code_session(session.clone()).await?; Ok(session) } - pub async fn get_key_session(&mut self, session_id: &Uuid) -> Result { - self.user_db.get_key_session(session_id) + pub async fn get_key_session(&self, session_id: &Uuid) -> Result { + self.user_db.get_key_session(session_id).await } - pub async fn get_code_session(&mut self, session_id: &Uuid) -> Result { - self.user_db.get_code_session(session_id) + pub async fn get_code_session(&self, session_id: &Uuid) -> Result { + self.user_db.get_code_session(session_id).await } - pub async fn get_code_login_data(&mut self, email: &Email) -> Result { - self.user_db.get_code_login_data(email) + pub async fn get_code_login_data(&self, email: &Email) -> Result { + self.user_db.get_code_login_data(email).await } - pub async fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String> { - self.user_db.set_code_login_data(email, data) + pub async fn set_code_login_data(&self, email: Email, data: CodeLoginData) -> Result<(), String> { + self.user_db.set_code_login_data(email, data).await } } @@ -158,7 +158,7 @@ impl CredKind for Key { async fn get_password_file(repo: &R, id: &[u8]) -> Result { repo.get_key_passcode_file(id).await } - async fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { + async fn set_password_file(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { repo.new_key(id, pf).await } } @@ -171,7 +171,7 @@ impl CredKind for Code { async fn get_password_file(repo: &R, id: &[u8]) -> Result { repo.get_code_passcode_file(id).await } - async fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { + async fn set_password_file(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError> { repo.new_code(id, pf).await } async fn prereq_for_register(repo: &R, id: &[u8]) -> Result<(), AuthRepoError> { diff --git a/src/server/models.rs b/src/server/models.rs index 55aa615..f56badb 100644 --- a/src/server/models.rs +++ b/src/server/models.rs @@ -19,7 +19,7 @@ pub struct LoginCache { pub trait CredKind { async fn has(repo: &R, id: &[u8]) -> bool; async fn get_password_file(repo: &R, id: &[u8]) -> Result; - async fn set_password_file(repo: &mut R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>; + async fn set_password_file(repo: &R, id: &[u8], pf: PasswordFile) -> Result<(), AuthRepoError>; async fn prereq_for_register(_repo: &R, _id: &[u8]) -> Result<(), AuthRepoError> { Ok(()) } diff --git a/src/server/repository/in_memory/in_memory_transport.rs b/src/server/repository/in_memory/in_memory_transport.rs index 10ffd70..d398381 100644 --- a/src/server/repository/in_memory/in_memory_transport.rs +++ b/src/server/repository/in_memory/in_memory_transport.rs @@ -1,7 +1,5 @@ use async_trait::async_trait; use std::marker::PhantomData; -use tokio::sync::Mutex; -use std::sync::Arc; use uuid::Uuid; use opaque_ke::{CredentialFinalization, CredentialRequest, RegistrationRequest}; use crate::shared::models::opaque::{NKodeCipherSuite, NKodeServerSetup, OpaqueLoginSession, OpaqueRegisterSession, PasswordFile}; @@ -14,23 +12,20 @@ use crate::server::repository::in_memory::in_memory_opaque_session::InMemoryOpaq use crate::server::repository::in_memory::in_memory_user_db::InMemoryUserDB; use crate::shared::models::app::LoggedInSession; -#[derive(Clone)] pub struct InMemoryServer { - auth_db: Arc>>, + auth_db: ServerApp, _kind: PhantomData, } impl InMemoryServer { pub fn new(server_setup: NKodeServerSetup) -> Self { Self { - auth_db: Arc::new(Mutex::new( - ServerApp::new( - server_setup, - InMemoryOpaqueDB::new(), - InMemoryOpaqueSession::new(), - InMemoryUserDB::new() - ) - )), + auth_db: ServerApp::new( + server_setup, + InMemoryOpaqueDB::new(), + InMemoryOpaqueSession::new(), + InMemoryUserDB::new() + ), _kind: PhantomData, } } @@ -51,7 +46,7 @@ where ) -> Result { // Server API takes ownership; client trait gives us a reference. // opaque-ke request types are typically Clone; if not, you'll need to adjust signatures. - self.auth_db.lock().await + self.auth_db .reg_start::(identifier, message.clone()) .await .map_err(|e| ClientAuthError::Transport(e)) @@ -62,8 +57,7 @@ where session_id: &Uuid, password_file: PasswordFile, ) -> Result<(), ClientAuthError> { - self.auth_db.lock().await - .reg_finish::(session_id, password_file) + self.auth_db.reg_finish::(session_id, password_file) .await .map_err(|e| ClientAuthError::Transport(e)) } @@ -79,7 +73,7 @@ where identifier: &[u8], request: &CredentialRequest, ) -> Result { - self.auth_db.lock().await + self.auth_db .login_start::(identifier, request.clone()) .await .map_err(|e| ClientAuthError::Transport(e)) @@ -91,7 +85,7 @@ where message: &CredentialFinalization, ) -> Result { Ok(self - .auth_db.lock().await + .auth_db .login_finish(session_id, message.clone()) .await .map_err(|e| ClientAuthError::Transport(e))? diff --git a/src/server/repository/in_memory/in_memory_user_db.rs b/src/server/repository/in_memory/in_memory_user_db.rs index 8a06d1c..dc48b9b 100644 --- a/src/server/repository/in_memory/in_memory_user_db.rs +++ b/src/server/repository/in_memory/in_memory_user_db.rs @@ -1,21 +1,24 @@ use std::collections::HashMap; +use std::sync::Arc; +use async_trait::async_trait; +use tokio::sync::Mutex; use uuid::Uuid; use crate::server::repository::user_repo::UserRepo; use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession}; use crate::shared::models::email::Email; pub struct InMemoryUserDB { - key_session: HashMap, - code_session: HashMap, - code_data: HashMap, + key_session: Arc>>, + code_session: Arc>>, + code_data: Arc>>, } impl InMemoryUserDB { pub fn new() -> Self { Self { - key_session: HashMap::new(), - code_session: HashMap::new(), - code_data: HashMap::new(), + key_session: Arc::new(Mutex::new(HashMap::new())), + code_session: Arc::new(Mutex::new(HashMap::new())), + code_data: Arc::new(Mutex::new(HashMap::new())), } } } @@ -26,40 +29,39 @@ impl Default for InMemoryUserDB { } } +#[async_trait] impl UserRepo for InMemoryUserDB { - fn get_key_session(&mut self, session_id: &Uuid) -> Result { - self.key_session + async fn get_key_session(&self, session_id: &Uuid) -> Result { + self.key_session.lock().await .get(&session_id) .cloned() .ok_or_else(|| format!("key session not found for session_id={}", session_id)) } - fn get_code_session(&mut self, session_id: &Uuid) -> Result { - self.code_session + async fn get_code_session(&self, session_id: &Uuid) -> Result { + self.code_session.lock().await .get(&session_id) .cloned() .ok_or_else(|| format!("code session not found for session_id={}", session_id)) } - fn set_key_session(&mut self, session: KeyLoggedInSession) -> Result<(), String> { - // Assumes KeyLoggedInSession has a session_id: Uuid field (common pattern) - self.key_session.insert(session.0.session_id, session); + async fn set_key_session(&self, session: KeyLoggedInSession) -> Result<(), String> { + self.key_session.lock().await.insert(session.0.session_id, session); Ok(()) } - fn set_code_session(&mut self, session: CodeLoggedInSession) -> Result<(), String> { - // Assumes CodeLoggedInSession has a session_id: Uuid field (common pattern) - self.code_session.insert(session.0.session_id, session); + async fn set_code_session(&self, session: CodeLoggedInSession) -> Result<(), String> { + self.code_session.lock().await.insert(session.0.session_id, session); Ok(()) } - fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String> { - self.code_data.insert(email, data); + async fn set_code_login_data(&self, email: Email, data: CodeLoginData) -> Result<(), String> { + self.code_data.lock().await.insert(email, data); Ok(()) } - fn get_code_login_data(&mut self, email: &Email) -> Result { - self.code_data + async fn get_code_login_data(&self, email: &Email) -> Result { + self.code_data.lock().await .get(email) .cloned() .ok_or_else(|| "code login data not found for email".to_string()) diff --git a/src/server/repository/user_repo.rs b/src/server/repository/user_repo.rs index a7aa429..aa18e2a 100644 --- a/src/server/repository/user_repo.rs +++ b/src/server/repository/user_repo.rs @@ -1,14 +1,16 @@ +use async_trait::async_trait; use uuid::Uuid; use crate::shared::models::app::{CodeLoggedInSession, CodeLoginData, KeyLoggedInSession}; use crate::shared::models::email::Email; +#[async_trait] pub trait UserRepo { - fn get_key_session(&mut self, session_id: &Uuid) -> Result; - fn get_code_session(&mut self, session_id: &Uuid) -> Result; + async fn get_key_session(&self, session_id: &Uuid) -> Result; + async fn get_code_session(&self, session_id: &Uuid) -> Result; - fn set_key_session(&mut self, session: KeyLoggedInSession) -> Result<(), String>; - fn set_code_session(&mut self, session: CodeLoggedInSession) -> Result<(), String>; + async fn set_key_session(&self, session: KeyLoggedInSession) -> Result<(), String>; + async fn set_code_session(&self, session: CodeLoggedInSession) -> Result<(), String>; - fn set_code_login_data(&mut self, email: Email, data: CodeLoginData) -> Result<(), String>; - fn get_code_login_data(&mut self, email: &Email) -> Result; + async fn set_code_login_data(&self, email: Email, data: CodeLoginData) -> Result<(), String>; + async fn get_code_login_data(&self, email: &Email) -> Result; } \ No newline at end of file diff --git a/src/shared/models/app.rs b/src/shared/models/app.rs index 45ca8b1..eecdce3 100644 --- a/src/shared/models/app.rs +++ b/src/shared/models/app.rs @@ -8,7 +8,6 @@ use uuid::Uuid; use crate::shared::models::email::Email; use crate::shared::models::opaque::{OpaqueSessionKey, UserSecretKey}; - #[derive(Debug, Clone)] pub struct LoggedInSession { pub(crate) session_id: Uuid, @@ -73,4 +72,3 @@ pub trait AuthAPI { async fn is_code_registered(&self, key_login_session: &KeyLoggedInSession) -> Result; async fn get_policy(&self) -> Result; } - diff --git a/src/shared/models/email.rs b/src/shared/models/email.rs index 69a5bb4..d70331e 100644 --- a/src/shared/models/email.rs +++ b/src/shared/models/email.rs @@ -1,5 +1,4 @@ -use std::{fmt, str::FromStr}; - +use std::fmt; use email_address::{EmailAddress, Options}; #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -18,11 +17,8 @@ impl Email { if s.is_empty() { return Err(EmailError::Empty); } - - // Strict "address only" validation (no "Name ") EmailAddress::parse_with_options(s, Options::default().without_display_text()) .map_err(|_| EmailError::InvalidFormat)?; - Ok(Self(s.to_owned())) } @@ -48,63 +44,8 @@ impl Email { } } -/* ---- std traits ---- */ - impl fmt::Display for Email { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(self.as_str()) } } - -impl FromStr for Email { - type Err = EmailError; - fn from_str(s: &str) -> Result { - Email::new(s) - } -} - -impl TryFrom for Email { - type Error = EmailError; - fn try_from(value: String) -> Result { - Email::new(value) - } -} - -impl TryFrom<&str> for Email { - type Error = EmailError; - fn try_from(value: &str) -> Result { - Email::new(value) - } -} - -impl TryFrom<&[u8]> for Email { - type Error = EmailError; - fn try_from(value: &[u8]) -> Result { - Email::from_bytes(value) - } -} - -impl TryFrom> for Email { - type Error = EmailError; - fn try_from(value: Vec) -> Result { - Email::from_bytes(&value) - } -} - -impl AsRef for Email { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl AsRef<[u8]> for Email { - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl From for Vec { - fn from(value: Email) -> Self { - value.into_bytes() - } -} diff --git a/tests/in_memory_test.rs b/tests/in_memory_test.rs index d0f8061..7b56fbc 100644 --- a/tests/in_memory_test.rs +++ b/tests/in_memory_test.rs @@ -8,13 +8,14 @@ async fn opaque_key_registration_and_login_roundtrip() { let mut rng = OsRng; let server_setup = NKodeServerSetup::new(&mut rng); let server = InMemoryKeyServer::new(server_setup); - let auth_reg = OpaqueAuthRegister::new(server.clone()); + let auth_reg = OpaqueAuthRegister::new(server); let auth_data = AuthenticationData::from_secret_key("a@b.com", b"supersecret16bytes"); auth_reg.register(&auth_data).await.expect("registration should succeed"); let login_reg = OpaqueAuthLogin::new(server); let _ =login_reg.login(&auth_data) .await .expect("login should succeed"); + // assert!(!session_key.is_empty()); } #[tokio::test]