sharenet/passport/src/application/use_cases.rs
continuist 05674b4caa
Some checks failed
Podman Rootless Demo / test-backend (push) Has been skipped
Podman Rootless Demo / test-frontend (push) Has been skipped
Podman Rootless Demo / build-backend (push) Has been skipped
Podman Rootless Demo / build-frontend (push) Failing after 5m32s
Podman Rootless Demo / deploy-prod (push) Has been skipped
Add to project
2025-11-01 11:53:11 -04:00

535 lines
No EOL
15 KiB
Rust

use crate::domain::entities::*;
use crate::domain::traits::*;
use crate::application::error::ApplicationError;
use ed25519_dalek::Signer;
use crate::infrastructure::time;
pub struct CreatePassportUseCase<MG, KD, FE, FS>
where
MG: MnemonicGenerator,
KD: KeyDeriver,
FE: FileEncryptor,
FS: FileStorage,
{
mnemonic_generator: MG,
key_deriver: KD,
file_encryptor: FE,
file_storage: FS,
}
impl<MG, KD, FE, FS> CreatePassportUseCase<MG, KD, FE, FS>
where
MG: MnemonicGenerator,
KD: KeyDeriver,
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(
mnemonic_generator: MG,
key_deriver: KD,
file_encryptor: FE,
file_storage: FS,
) -> Self {
Self {
mnemonic_generator,
key_deriver,
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
univ_id: &str,
password: &str,
output_path: &str,
) -> Result<(Passport, RecoveryPhrase), ApplicationError> {
// Generate recovery phrase
let recovery_phrase = self
.mnemonic_generator
.generate()
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to generate mnemonic: {}", e.into())))?;
// Derive seed from mnemonic and universe
let seed = self
.key_deriver
.derive_from_mnemonic(&recovery_phrase, univ_id)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to derive seed: {}", e.into())))?;
// Derive keys from seed
let (public_key, private_key) = self
.key_deriver
.derive_from_seed(&seed)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to derive keys: {}", e.into())))?;
// Create passport (without storing recovery phrase)
let passport = Passport::new(
seed,
public_key,
private_key,
univ_id.to_string(),
);
// Encrypt and save file
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, output_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok((passport, recovery_phrase))
}
}
pub struct ImportFromRecoveryUseCase<MG, KD, FE, FS>
where
MG: MnemonicGenerator,
KD: KeyDeriver,
FE: FileEncryptor,
FS: FileStorage,
{
mnemonic_generator: MG,
key_deriver: KD,
file_encryptor: FE,
file_storage: FS,
}
impl<MG, KD, FE, FS> ImportFromRecoveryUseCase<MG, KD, FE, FS>
where
MG: MnemonicGenerator,
KD: KeyDeriver,
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(
mnemonic_generator: MG,
key_deriver: KD,
file_encryptor: FE,
file_storage: FS,
) -> Self {
Self {
mnemonic_generator,
key_deriver,
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
univ_id: &str,
recovery_words: &[String],
password: &str,
output_path: &str,
) -> Result<Passport, ApplicationError> {
// Validate recovery phrase
self.mnemonic_generator
.validate(recovery_words)
.map_err(|e| ApplicationError::UseCaseError(format!("Invalid recovery phrase: {}", e.into())))?;
let recovery_phrase = RecoveryPhrase::new(recovery_words.to_vec());
// Derive seed from mnemonic and universe
let seed = self
.key_deriver
.derive_from_mnemonic(&recovery_phrase, univ_id)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to derive seed: {}", e.into())))?;
// Derive keys from seed
let (public_key, private_key) = self
.key_deriver
.derive_from_seed(&seed)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to derive keys: {}", e.into())))?;
// Create passport (without storing recovery phrase)
let passport = Passport::new(
seed,
public_key,
private_key,
univ_id.to_string(),
);
// Encrypt and save file
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, output_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok(passport)
}
}
pub struct ImportFromFileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
file_encryptor: FE,
file_storage: FS,
}
impl<FE, FS> ImportFromFileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(
file_encryptor: FE,
file_storage: FS,
) -> Self {
Self {
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
file_path: &str,
password: &str,
output_path: Option<&str>,
) -> Result<Passport, ApplicationError> {
// Load encrypted file
let passport_file = self
.file_storage
.load(file_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to load file: {}", e.into())))?;
// Decrypt file
let (seed, public_key, private_key, user_profiles, date_of_birth, default_user_profile_id) = self
.file_encryptor
.decrypt(&passport_file, password)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to decrypt file: {}", e.into())))?;
// Create passport (without storing recovery phrase)
let mut passport = Passport::new(
seed,
public_key,
private_key,
passport_file.univ_id.clone(),
);
passport.user_profiles = user_profiles;
passport.date_of_birth = date_of_birth;
passport.default_user_profile_id = default_user_profile_id;
// Re-encrypt and save if output path provided
if let Some(output_path) = output_path {
let new_passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to re-encrypt file: {}", e.into())))?;
self.file_storage
.save(&new_passport_file, output_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
}
Ok(passport)
}
}
pub struct ExportPassportUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
file_encryptor: FE,
file_storage: FS,
}
impl<FE, FS> ExportPassportUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(file_encryptor: FE, file_storage: FS) -> Self {
Self {
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
passport: &Passport,
password: &str,
output_path: &str,
) -> Result<(), ApplicationError> {
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, output_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok(())
}
}
pub struct SignCardUseCase;
impl SignCardUseCase {
pub fn new() -> Self {
Self
}
pub fn execute(
&self,
passport: &Passport,
message: &str,
) -> Result<Vec<u8>, ApplicationError> {
// Convert the private key bytes to an ed25519_dalek SigningKey
let signing_key = ed25519_dalek::SigningKey::from_bytes(
&passport.private_key.0[..]
.try_into()
.map_err(|_| ApplicationError::UseCaseError("Invalid private key length".to_string()))?
);
// Create universe-bound message to sign
let message_to_sign = format!("u:{}:{}", passport.univ_id, message);
// Sign the universe-bound message
let signature = signing_key.sign(message_to_sign.as_bytes());
// Return the signature as bytes
Ok(signature.to_bytes().to_vec())
}
}
pub struct CreateUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
file_encryptor: FE,
file_storage: FS,
}
impl<FE, FS> CreateUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(file_encryptor: FE, file_storage: FS) -> Self {
Self {
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
passport: &mut Passport,
hub_did: Option<String>,
identity: UserIdentity,
preferences: UserPreferences,
password: &str,
file_path: &str,
) -> Result<(), ApplicationError> {
let profile = UserProfile::new(hub_did, identity, preferences);
passport.add_user_profile(profile)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to add user profile: {}", e)))?;
// Save updated passport
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, file_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok(())
}
}
pub struct UpdateUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
file_encryptor: FE,
file_storage: FS,
}
impl<FE, FS> UpdateUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(file_encryptor: FE, file_storage: FS) -> Self {
Self {
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
passport: &mut Passport,
id: Option<&str>,
hub_did: Option<String>,
identity: UserIdentity,
preferences: UserPreferences,
password: &str,
file_path: &str,
) -> Result<(), ApplicationError> {
// Find existing profile by ID to preserve its ID and created_at
let id = id
.ok_or_else(|| ApplicationError::UseCaseError("Profile ID is required".to_string()))?;
let existing_profile = passport.user_profile_by_id(id)
.ok_or_else(|| ApplicationError::UseCaseError("User profile not found".to_string()))?;
let now = time::now_seconds()
.map_err(|e| ApplicationError::UseCaseError(format!("Time error: {}", e)))?;
// Use provided hub_did or keep existing
let profile = UserProfile {
id: existing_profile.id.clone(),
hub_did: hub_did.or_else(|| existing_profile.hub_did.clone()),
identity,
preferences,
created_at: existing_profile.created_at,
updated_at: now,
};
passport.update_user_profile_by_id(id, profile)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to update user profile: {}", e)))?;
// Save updated passport
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, file_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok(())
}
}
pub struct DeleteUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
file_encryptor: FE,
file_storage: FS,
}
impl<FE, FS> DeleteUserProfileUseCase<FE, FS>
where
FE: FileEncryptor,
FS: FileStorage,
{
pub fn new(file_encryptor: FE, file_storage: FS) -> Self {
Self {
file_encryptor,
file_storage,
}
}
pub fn execute(
&self,
passport: &mut Passport,
id: Option<&str>,
password: &str,
file_path: &str,
) -> Result<(), ApplicationError> {
let id = id
.ok_or_else(|| ApplicationError::UseCaseError("Profile ID is required".to_string()))?;
passport.remove_user_profile_by_id(id)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to remove user profile: {}", e)))?;
// Save updated passport
let passport_file = self
.file_encryptor
.encrypt(
&passport.seed,
password,
&passport.public_key,
&passport.did,
&passport.univ_id,
&passport.user_profiles,
&passport.date_of_birth,
&passport.default_user_profile_id,
)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to encrypt file: {}", e.into())))?;
self.file_storage
.save(&passport_file, file_path)
.map_err(|e| ApplicationError::UseCaseError(format!("Failed to save file: {}", e.into())))?;
Ok(())
}
}