mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-18 21:40:41 -04:00
Make AliasVault export/import work again (#542)
This commit is contained in:
committed by
Leendert de Borst
parent
14ee466bec
commit
70b7ac6f9f
@@ -15,8 +15,11 @@
|
||||
@code {
|
||||
private async Task<List<ImportedCredential>> ProcessFile(string fileContents)
|
||||
{
|
||||
// TODO: Implement AliasVault import.
|
||||
await Task.Delay(50);
|
||||
return new List<ImportedCredential>();
|
||||
var importedCredentials = await Task.Run(() =>
|
||||
{
|
||||
return AliasVault.ImportExport.CredentialCsvService.ImportCredentialsFromCsv(fileContents);
|
||||
});
|
||||
|
||||
return importedCredentials;
|
||||
}
|
||||
}
|
||||
@@ -84,8 +84,11 @@
|
||||
// Create a memory stream from the byte array.
|
||||
using (MemoryStream memoryStream = new MemoryStream(csvBytes))
|
||||
{
|
||||
// Invoke JavaScript to initiate the download.
|
||||
await JsInteropService.DownloadFileFromStream("aliasvault-client.csv", memoryStream.ToArray());
|
||||
var dateStr = DateTime.UtcNow.ToString("yyyy-MM-dd");
|
||||
var username = await GetUsernameAsync();
|
||||
|
||||
// Invoke JavaScript to initiate the download with date and username in filename
|
||||
await JsInteropService.DownloadFileFromStream($"aliasvault-export-{username}-{dateStr}.csv", memoryStream.ToArray());
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ImportExportTests
|
||||
/// Test case for importing credentials from CSV and ensuring all values are present.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void ImportCredentialsFromCsv()
|
||||
public async Task ImportCredentialsFromCsv()
|
||||
{
|
||||
// Arrange
|
||||
var credential = new Credential
|
||||
@@ -66,7 +66,7 @@ public class ImportExportTests
|
||||
var csvString = System.Text.Encoding.Default.GetString(csvContent);
|
||||
|
||||
// Act
|
||||
var importedCredentials = CredentialCsvService.ImportCredentialsFromCsv(csvString);
|
||||
var importedCredentials = await CredentialCsvService.ImportCredentialsFromCsv(csvString);
|
||||
|
||||
// Assert
|
||||
Assert.That(importedCredentials, Has.Count.EqualTo(1));
|
||||
@@ -75,33 +75,21 @@ public class ImportExportTests
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(importedCredential.Id, Is.EqualTo(credential.Id));
|
||||
Assert.That(importedCredential.ServiceName, Is.EqualTo(credential.Service.Name));
|
||||
Assert.That(importedCredential.ServiceUrl, Is.EqualTo(credential.Service.Url));
|
||||
Assert.That(importedCredential.Username, Is.EqualTo(credential.Username));
|
||||
Assert.That(importedCredential.Notes, Is.EqualTo(credential.Notes));
|
||||
Assert.That(importedCredential.CreatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(credential.CreatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedCredential.UpdatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(credential.UpdatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedCredential.AliasId, Is.EqualTo(credential.AliasId));
|
||||
Assert.That(importedCredential.Alias.Id, Is.EqualTo(credential.Alias.Id));
|
||||
Assert.That(importedCredential.Alias.Gender, Is.EqualTo(credential.Alias.Gender));
|
||||
Assert.That(importedCredential.Alias.FirstName, Is.EqualTo(credential.Alias.FirstName));
|
||||
Assert.That(importedCredential.Alias.LastName, Is.EqualTo(credential.Alias.LastName));
|
||||
Assert.That(importedCredential.Alias.NickName, Is.EqualTo(credential.Alias.NickName));
|
||||
Assert.That(importedCredential.Alias.BirthDate, Is.EqualTo(credential.Alias.BirthDate));
|
||||
Assert.That(importedCredential.Alias.Email, Is.EqualTo(credential.Alias.Email));
|
||||
Assert.That(importedCredential.Alias.CreatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(credential.Alias.CreatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedCredential.Alias.UpdatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(credential.Alias.UpdatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedCredential.ServiceId, Is.EqualTo(credential.ServiceId));
|
||||
Assert.That(importedCredential.Service.Id, Is.EqualTo(credential.Service.Id));
|
||||
Assert.That(importedCredential.Service.Name, Is.EqualTo(credential.Service.Name));
|
||||
Assert.That(importedCredential.Service.Url, Is.EqualTo(credential.Service.Url));
|
||||
Assert.That(importedCredential.Passwords, Has.Count.EqualTo(1));
|
||||
|
||||
var importedPassword = importedCredential.Passwords.First();
|
||||
var originalPassword = credential.Passwords.First();
|
||||
|
||||
Assert.That(importedPassword.Value, Is.EqualTo(originalPassword.Value));
|
||||
Assert.That(importedPassword.CreatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(originalPassword.CreatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedPassword.UpdatedAt.ToString("yyyy-MM-dd"), Is.EqualTo(originalPassword.UpdatedAt.ToString("yyyy-MM-dd")));
|
||||
Assert.That(importedCredential.CreatedAt, Is.EqualTo(credential.CreatedAt));
|
||||
Assert.That(importedCredential.UpdatedAt, Is.EqualTo(credential.UpdatedAt));
|
||||
Assert.That(importedCredential.Alias!.Gender, Is.EqualTo(credential.Alias!.Gender));
|
||||
Assert.That(importedCredential.Alias!.FirstName, Is.EqualTo(credential.Alias!.FirstName));
|
||||
Assert.That(importedCredential.Alias!.LastName, Is.EqualTo(credential.Alias!.LastName));
|
||||
Assert.That(importedCredential.Alias!.NickName, Is.EqualTo(credential.Alias!.NickName));
|
||||
Assert.That(importedCredential.Alias!.BirthDate, Is.EqualTo(credential.Alias!.BirthDate));
|
||||
Assert.That(importedCredential.Alias!.Email, Is.EqualTo(credential.Alias!.Email));
|
||||
Assert.That(importedCredential.Alias!.CreatedAt, Is.EqualTo(credential.Alias!.CreatedAt));
|
||||
Assert.That(importedCredential.Alias!.UpdatedAt, Is.EqualTo(credential.Alias!.UpdatedAt));
|
||||
Assert.That(importedCredential.Password, Is.EqualTo(credential.Passwords.First().Value));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
namespace AliasVault.ImportExport;
|
||||
|
||||
using AliasClientDb;
|
||||
using AliasVault.ImportExport.Models;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using System.Globalization;
|
||||
@@ -17,7 +18,7 @@ using System.Globalization;
|
||||
/// </summary>
|
||||
public static class CredentialCsvService
|
||||
{
|
||||
private const string CsvVersionIdentifier = "1.0.0";
|
||||
private const string CsvVersionIdentifier = "1.5.0";
|
||||
|
||||
/// <summary>
|
||||
/// Export list of credentials to CSV file.
|
||||
@@ -33,24 +34,20 @@ public static class CredentialCsvService
|
||||
var record = new CredentialCsvRecord
|
||||
{
|
||||
Version = CsvVersionIdentifier,
|
||||
Id = credential.Id,
|
||||
Username = credential.Username ?? string.Empty,
|
||||
Notes = credential.Notes ?? string.Empty,
|
||||
CreatedAt = credential.CreatedAt,
|
||||
UpdatedAt = credential.UpdatedAt,
|
||||
AliasId = credential.AliasId,
|
||||
AliasGender = credential.Alias?.Gender ?? string.Empty,
|
||||
AliasFirstName = credential.Alias?.FirstName ?? string.Empty,
|
||||
AliasLastName = credential.Alias?.LastName ?? string.Empty,
|
||||
AliasNickName = credential.Alias?.NickName ?? string.Empty,
|
||||
AliasBirthDate = credential.Alias?.BirthDate,
|
||||
AliasEmail = credential.Alias?.Email ?? string.Empty,
|
||||
AliasCreatedAt = credential.Alias?.CreatedAt,
|
||||
AliasUpdatedAt = credential.Alias?.UpdatedAt,
|
||||
ServiceId = credential.ServiceId,
|
||||
ServiceName = credential.Service?.Name ?? string.Empty,
|
||||
ServiceUrl = credential.Service?.Url ?? string.Empty,
|
||||
CurrentPassword = credential.Passwords.OrderByDescending(p => p.CreatedAt).FirstOrDefault()?.Value ?? string.Empty
|
||||
CurrentPassword = credential.Passwords.OrderByDescending(p => p.CreatedAt).FirstOrDefault()?.Value ?? string.Empty,
|
||||
TwoFactorSecret = credential.TotpCodes.FirstOrDefault()?.SecretKey ?? string.Empty
|
||||
};
|
||||
|
||||
records.Add(record);
|
||||
@@ -69,13 +66,17 @@ public static class CredentialCsvService
|
||||
/// Imports Credential objects from a CSV file.
|
||||
/// </summary>
|
||||
/// <param name="fileContent">The content of the CSV file.</param>
|
||||
/// <returns>The imported list of Credential objects.</returns>
|
||||
public static List<Credential> ImportCredentialsFromCsv(string fileContent)
|
||||
/// <returns>The imported list of ImportedCredential objects.</returns>
|
||||
public static async Task<List<ImportedCredential>> ImportCredentialsFromCsv(string fileContent)
|
||||
{
|
||||
using var reader = new StringReader(fileContent);
|
||||
using var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture));
|
||||
|
||||
var records = csv.GetRecords<CredentialCsvRecord>().ToList();
|
||||
var records = new List<CredentialCsvRecord>();
|
||||
await foreach (var record in csv.GetRecordsAsync<CredentialCsvRecord>())
|
||||
{
|
||||
records.Add(record);
|
||||
}
|
||||
|
||||
if (records.Count == 0)
|
||||
{
|
||||
@@ -87,45 +88,31 @@ public static class CredentialCsvService
|
||||
throw new InvalidOperationException("Invalid CSV file version.");
|
||||
}
|
||||
|
||||
var credentials = new List<Credential>();
|
||||
var credentials = new List<ImportedCredential>();
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
var credential = new Credential
|
||||
var credential = new ImportedCredential
|
||||
{
|
||||
Id = record.Id,
|
||||
ServiceName = record.ServiceName,
|
||||
ServiceUrl = record.ServiceUrl,
|
||||
Username = record.Username,
|
||||
Password = record.CurrentPassword,
|
||||
Email = record.AliasEmail,
|
||||
Notes = record.Notes,
|
||||
CreatedAt = record.CreatedAt,
|
||||
UpdatedAt = record.UpdatedAt,
|
||||
AliasId = record.AliasId,
|
||||
Alias = new Alias
|
||||
Alias = new ImportedAlias
|
||||
{
|
||||
Id = record.AliasId,
|
||||
Gender = record.AliasGender,
|
||||
FirstName = record.AliasFirstName,
|
||||
LastName = record.AliasLastName,
|
||||
NickName = record.AliasNickName,
|
||||
BirthDate = record.AliasBirthDate ?? DateTime.MinValue,
|
||||
Email = record.AliasEmail,
|
||||
CreatedAt = record.AliasCreatedAt ?? DateTime.UtcNow,
|
||||
UpdatedAt = record.AliasUpdatedAt ?? DateTime.UtcNow
|
||||
BirthDate = record.AliasBirthDate,
|
||||
CreatedAt = record.CreatedAt,
|
||||
UpdatedAt = record.UpdatedAt
|
||||
},
|
||||
ServiceId = record.ServiceId,
|
||||
Service = new Service
|
||||
{
|
||||
Id = record.ServiceId,
|
||||
Name = record.ServiceName,
|
||||
Url = record.ServiceUrl
|
||||
},
|
||||
Passwords = [
|
||||
new Password
|
||||
{
|
||||
Value = record.CurrentPassword,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
UpdatedAt = DateTime.UtcNow
|
||||
}
|
||||
]
|
||||
TwoFactorSecret = record.TwoFactorSecret,
|
||||
CreatedAt = record.CreatedAt,
|
||||
UpdatedAt = record.UpdatedAt,
|
||||
};
|
||||
|
||||
credentials.Add(credential);
|
||||
@@ -140,23 +127,19 @@ public static class CredentialCsvService
|
||||
/// </summary>
|
||||
public class CredentialCsvRecord
|
||||
{
|
||||
public string Version { get; set; } = "1.0.0";
|
||||
public Guid Id { get; set; } = Guid.Empty;
|
||||
public string Version { get; set; } = "1.5.0";
|
||||
public string Username { get; set; } = string.Empty;
|
||||
public string Notes { get; set; } = string.Empty;
|
||||
public DateTime CreatedAt { get; set; } = DateTime.MinValue;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.MinValue;
|
||||
public Guid AliasId { get; set; } = Guid.Empty;
|
||||
public string AliasGender { get; set; } = string.Empty;
|
||||
public string AliasFirstName { get; set; } = string.Empty;
|
||||
public string AliasLastName { get; set; } = string.Empty;
|
||||
public string AliasNickName { get; set; } = string.Empty;
|
||||
public DateTime? AliasBirthDate { get; set; } = null;
|
||||
public string AliasEmail { get; set; } = string.Empty;
|
||||
public DateTime? AliasCreatedAt { get; set; } = null;
|
||||
public DateTime? AliasUpdatedAt { get; set; } = null;
|
||||
public Guid ServiceId { get; set; } = Guid.Empty;
|
||||
public string ServiceName { get; set; } = string.Empty;
|
||||
public string ServiceUrl { get; set; } = string.Empty;
|
||||
public string CurrentPassword { get; set; } = string.Empty;
|
||||
public string TwoFactorSecret { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
@@ -31,28 +31,35 @@ public class BaseImporter
|
||||
{
|
||||
Service = new Service { Name = importedCredential.ServiceName, Url = importedCredential.ServiceUrl },
|
||||
Username = importedCredential.Username,
|
||||
Passwords = new List<Password> { new() { Value = importedCredential.Password } },
|
||||
Passwords = [new() { Value = importedCredential.Password }],
|
||||
Notes = importedCredential.Notes,
|
||||
CreatedAt = importedCredential.CreatedAt ?? DateTime.UtcNow,
|
||||
UpdatedAt = importedCredential.ModifiedAt ?? DateTime.UtcNow
|
||||
UpdatedAt = importedCredential.UpdatedAt ?? DateTime.UtcNow,
|
||||
Alias = new Alias
|
||||
{
|
||||
FirstName = importedCredential.Alias?.FirstName,
|
||||
LastName = importedCredential.Alias?.LastName,
|
||||
Gender = importedCredential.Alias?.Gender,
|
||||
// TODO: birth date should be made nullable in client DB as it's not always available.
|
||||
BirthDate = importedCredential.Alias?.BirthDate ?? DateTime.MinValue,
|
||||
Email = importedCredential.Email,
|
||||
NickName = importedCredential.Alias?.NickName,
|
||||
CreatedAt = importedCredential.CreatedAt ?? DateTime.UtcNow,
|
||||
UpdatedAt = importedCredential.UpdatedAt ?? DateTime.UtcNow,
|
||||
}
|
||||
};
|
||||
|
||||
credential.Alias = new Alias
|
||||
{
|
||||
CreatedAt = importedCredential.CreatedAt ?? DateTime.UtcNow,
|
||||
UpdatedAt = importedCredential.ModifiedAt ?? DateTime.UtcNow,
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(importedCredential.Email))
|
||||
{
|
||||
credential.Alias = new Alias { Email = importedCredential.Email };
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(importedCredential.TwoFactorSecret))
|
||||
{
|
||||
credential.TotpCodes = new List<TotpCode>
|
||||
{
|
||||
new() { SecretKey = importedCredential.TwoFactorSecret }
|
||||
new()
|
||||
{
|
||||
Name = "Authenticator",
|
||||
SecretKey = importedCredential.TwoFactorSecret,
|
||||
CreatedAt = importedCredential.CreatedAt ?? DateTime.UtcNow,
|
||||
UpdatedAt = importedCredential.UpdatedAt ?? DateTime.UtcNow,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="ImportedAlias.cs" company="lanedirt">
|
||||
// Copyright (c) lanedirt. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
|
||||
// </copyright>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
namespace AliasVault.ImportExport.Models;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an alias in an intermediary format that is imported from various sources.
|
||||
/// This model is designed to be flexible enough to handle different import formats while
|
||||
/// maintaining all the essential fields needed for AliasVault aliases.
|
||||
/// </summary>
|
||||
public class ImportedAlias
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the gender.
|
||||
/// </summary>
|
||||
public string? Gender { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the first name.
|
||||
/// </summary>
|
||||
public string? FirstName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last name.
|
||||
/// </summary>
|
||||
public string? LastName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the nickname.
|
||||
/// </summary>
|
||||
public string? NickName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the birth date.
|
||||
/// </summary>
|
||||
public DateTime? BirthDate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the creation date.
|
||||
/// </summary>
|
||||
public DateTime? CreatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the last update date.
|
||||
/// </summary>
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
}
|
||||
@@ -57,10 +57,15 @@ public class ImportedCredential
|
||||
/// <summary>
|
||||
/// Gets or sets the last modified date of the credential.
|
||||
/// </summary>
|
||||
public DateTime? ModifiedAt { get; set; }
|
||||
public DateTime? UpdatedAt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the favicon bytes.
|
||||
/// </summary>
|
||||
public byte[]? FaviconBytes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alias information.
|
||||
/// </summary>
|
||||
public ImportedAlias? Alias { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user