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
535 lines
No EOL
15 KiB
Rust
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(())
|
|
}
|
|
} |