mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-16 12:29:01 -04:00
Add Bitwarden importer scaffolding (#542)
This commit is contained in:
committed by
Leendert de Borst
parent
e93b0575ff
commit
634fc281a2
@@ -1,3 +1,4 @@
|
||||
@using AliasVault.ImportExport.Importers
|
||||
@inject ILogger<ImportServiceBitwarden> Logger
|
||||
|
||||
<ImportServiceCard
|
||||
@@ -5,19 +6,33 @@
|
||||
Description="Import passwords from your Bitwarden vault"
|
||||
LogoUrl="img/importers/bitwarden.svg"
|
||||
OnImportComplete="RefreshVault"
|
||||
>
|
||||
@ref="_importServiceCard">
|
||||
<div class="mb-4">
|
||||
<p class="mb-4 text-gray-700 dark:text-gray-300">Upload your Bitwarden JSON export file:</p>
|
||||
<p class="mb-4 text-gray-700 dark:text-gray-300">Upload your Bitwarden CSV export file:</p>
|
||||
</div>
|
||||
<InputFile OnChange="HandleFileUpload" />
|
||||
</ImportServiceCard>
|
||||
|
||||
@code {
|
||||
private ImportServiceCard _importServiceCard = null!;
|
||||
|
||||
private async Task HandleFileUpload(InputFileChangeEventArgs e)
|
||||
{
|
||||
// Bitwarden specific file upload and parsing logic
|
||||
await Task.Delay(500);
|
||||
Logger.LogInformation($"Processing Bitwarden file: {e.File.Name}");
|
||||
try
|
||||
{
|
||||
await using var stream = e.File.OpenReadStream();
|
||||
using var reader = new StreamReader(stream);
|
||||
var fileContents = await reader.ReadToEndAsync();
|
||||
|
||||
var importCredentials = await BitwardenImporter.ImportFromCsvAsync(fileContents);
|
||||
_importServiceCard.SetImportedCredentials(importCredentials);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogError(ex, "Error processing KeePass CSV file");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void RefreshVault()
|
||||
@@ -25,4 +40,4 @@
|
||||
// Refresh the vault
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
using var stream = e.File.OpenReadStream();
|
||||
await using var stream = e.File.OpenReadStream();
|
||||
using var reader = new StreamReader(stream);
|
||||
var fileContents = await reader.ReadToEndAsync();
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="BitwardenImporter.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.Importers;
|
||||
|
||||
using AliasVault.ImportExport.Models;
|
||||
using AliasVault.ImportExport.Models.Imports;
|
||||
using CsvHelper;
|
||||
using CsvHelper.Configuration;
|
||||
using System.Globalization;
|
||||
|
||||
/// <summary>
|
||||
/// Imports credentials from Bitwarden.
|
||||
/// </summary>
|
||||
public class BitwardenImporter
|
||||
{
|
||||
/// <summary>
|
||||
/// Imports Bitwarden CSV file and converts contents to list of ImportedCredential model objects.
|
||||
/// </summary>
|
||||
/// <param name="fileContent">The content of the CSV file.</param>
|
||||
/// <returns>The imported list of ImportedCredential objects.</returns>
|
||||
public static async Task<List<ImportedCredential>> ImportFromCsvAsync(string fileContent)
|
||||
{
|
||||
using var reader = new StringReader(fileContent);
|
||||
using var csv = new CsvReader(reader, new CsvConfiguration(CultureInfo.InvariantCulture));
|
||||
|
||||
var credentials = new List<ImportedCredential>();
|
||||
await foreach (var record in csv.GetRecordsAsync<BitwardenCsvRecord>())
|
||||
{
|
||||
// TODO: OTPAuth should be converted from URL to a secret key during import or during
|
||||
// the actual creation of the imported credential object.
|
||||
var credential = new ImportedCredential
|
||||
{
|
||||
ServiceName = record.Title,
|
||||
ServiceUrl = record.URL,
|
||||
Username = record.Username,
|
||||
Password = record.Password,
|
||||
TwoFactorSecret = record.OTPAuth,
|
||||
Notes = record.Notes
|
||||
};
|
||||
|
||||
credentials.Add(credential);
|
||||
}
|
||||
|
||||
if (credentials.Count == 0)
|
||||
{
|
||||
throw new InvalidOperationException("No records found in the CSV file.");
|
||||
}
|
||||
|
||||
return credentials;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//-----------------------------------------------------------------------
|
||||
// <copyright file="BitwardenCsvRecord.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>
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
using CsvHelper.Configuration.Attributes;
|
||||
|
||||
namespace AliasVault.ImportExport.Models.Imports;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a Bitwarden CSV record that is being imported from a Bitwarden CSV export file.
|
||||
/// </summary>
|
||||
public class BitwardenCsvRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the folder name.
|
||||
/// </summary>
|
||||
[Name("folder")]
|
||||
public string Folder { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the item is favorited.
|
||||
/// </summary>
|
||||
[Name("favorite")]
|
||||
public bool Favorite { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of the item.
|
||||
/// </summary>
|
||||
[Name("type")]
|
||||
public string Type { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title/service name (e.g., "Facebook", "Gmail").
|
||||
/// </summary>
|
||||
[Name("name")]
|
||||
public string Title { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets any additional notes.
|
||||
/// </summary>
|
||||
[Name("notes")]
|
||||
public string? Notes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets custom fields.
|
||||
/// </summary>
|
||||
[Name("fields")]
|
||||
public string? Fields { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether to reprompt for the master password.
|
||||
/// </summary>
|
||||
[Name("reprompt")]
|
||||
public bool Reprompt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the service URL.
|
||||
/// </summary>
|
||||
[Name("login_uri")]
|
||||
public string? URL { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the username.
|
||||
/// </summary>
|
||||
[Name("login_username")]
|
||||
public string? Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the password.
|
||||
/// </summary>
|
||||
[Name("login_password")]
|
||||
public string? Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the OTP (One-Time Password) authentication secret.
|
||||
/// </summary>
|
||||
[Name("login_totp")]
|
||||
public string? OTPAuth { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user