From 572998bb7c4d356ca0fbffc6a4325d7b8f3bb425 Mon Sep 17 00:00:00 2001 From: Leendert de Borst Date: Mon, 2 Feb 2026 23:12:57 +0100 Subject: [PATCH] Add multi-url import support to all importers (#1590) --- .../Components/ImportServiceCard.razor | 11 +- .../Settings/ImportExport/ImportExport.razor | 1 + .../ImportExport/ImportServices.en.resx | 13 ++ .../TestData/Exports/bitwarden.csv | 2 +- .../Utilities/ImportExportTests.cs | 206 +++++++++++++++--- .../Importers/BaseImporter.cs | 58 ++++- .../Importers/BitwardenImporter.cs | 2 +- .../Importers/ChromeImporter.cs | 2 +- .../Importers/DashlaneImporter.cs | 2 +- .../Importers/DropboxImporter.cs | 17 +- .../Importers/FirefoxImporter.cs | 2 +- .../Importers/GenericCsvImporter.cs | 2 +- .../Importers/KeePassImporter.cs | 2 +- .../Importers/KeePassXcImporter.cs | 2 +- .../Importers/LastPassImporter.cs | 2 +- .../Importers/NordPassImporter.cs | 2 +- .../Importers/OnePasswordImporter.cs | 2 +- .../Importers/ProtonPassImporter.cs | 2 +- .../Importers/StrongboxImporter.cs | 2 +- .../AliasVault.ImportExport/ItemCsvService.cs | 3 +- .../Models/ImportedCredential.cs | 4 +- 21 files changed, 267 insertions(+), 72 deletions(-) diff --git a/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/Components/ImportServiceCard.razor b/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/Components/ImportServiceCard.razor index ae15c9ac3..5e67f3242 100644 --- a/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/Components/ImportServiceCard.razor +++ b/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/Components/ImportServiceCard.razor @@ -411,12 +411,12 @@ FaviconExtractionCancellation = new CancellationTokenSource(); StateHasChanged(); - var credentialsWithUrls = ImportedCredentials.Where(c => !string.IsNullOrEmpty(c.ServiceUrl)).ToList(); + var credentialsWithUrls = ImportedCredentials.Where(c => c.ServiceUrls?.FirstOrDefault() != null).ToList(); // Group credentials by normalized domain to avoid duplicate fetches var processedDomains = new HashSet(); - TotalFaviconsToExtract = credentialsWithUrls.Count; + TotalFaviconsToExtract = credentialsWithUrls.Count(); foreach (var credential in credentialsWithUrls) { @@ -428,7 +428,7 @@ // Extract normalized domain for deduplication try { - var domain = new Uri(credential.ServiceUrl!).Host.ToLowerInvariant(); + var domain = new Uri(credential.ServiceUrls!.First()).Host.ToLowerInvariant(); if (domain.StartsWith("www.")) { domain = domain[4..]; @@ -460,13 +460,14 @@ try { // Extract normalized domain for storage key - var domain = new Uri(credential.ServiceUrl!).Host.ToLowerInvariant(); + var url = credential.ServiceUrls!.First(); + var domain = new Uri(url).Host.ToLowerInvariant(); if (domain.StartsWith("www.")) { domain = domain[4..]; } - var apiReturn = await HttpClient.GetFromJsonAsync($"v1/Favicon/Extract?url={Uri.EscapeDataString(credential.ServiceUrl!)}"); + var apiReturn = await HttpClient.GetFromJsonAsync($"v1/Favicon/Extract?url={Uri.EscapeDataString(url)}"); if (apiReturn?.Image is not null) { // Store by normalized domain for deduplication diff --git a/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/ImportExport.razor b/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/ImportExport.razor index 026ba0b8d..33520f7ff 100644 --- a/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/ImportExport.razor +++ b/apps/server/AliasVault.Client/Main/Pages/Settings/ImportExport/ImportExport.razor @@ -33,6 +33,7 @@ + diff --git a/apps/server/AliasVault.Client/Resources/Components/Main/Settings/ImportExport/ImportServices.en.resx b/apps/server/AliasVault.Client/Resources/Components/Main/Settings/ImportExport/ImportServices.en.resx index eea476a43..4ff397494 100644 --- a/apps/server/AliasVault.Client/Resources/Components/Main/Settings/ImportExport/ImportServices.en.resx +++ b/apps/server/AliasVault.Client/Resources/Components/Main/Settings/ImportExport/ImportServices.en.resx @@ -248,6 +248,19 @@ In order to import your Dropbox Passwords, you need to export them as a CSV file. You can do this by opening Dropbox Passwords, going to 'Account' > 'Export' (to .CSV). Dropbox export instructions part 1 + + + Import passwords from RoboForm + Description for RoboForm import service + + + In order to import your RoboForm passwords, you need to export them as a CSV file. You can do this by opening RoboForm, going to 'RoboForm' menu > 'Options' > 'Account & Data' > 'Export' and selecting CSV format. + RoboForm export instructions part 1 + + + Once you have exported the file, you can upload it below. + RoboForm export instructions part 2 + Once you have exported the file, you can upload it below. diff --git a/apps/server/Tests/AliasVault.UnitTests/TestData/Exports/bitwarden.csv b/apps/server/Tests/AliasVault.UnitTests/TestData/Exports/bitwarden.csv index 33b6580c1..0df58c249 100644 --- a/apps/server/Tests/AliasVault.UnitTests/TestData/Exports/bitwarden.csv +++ b/apps/server/Tests/AliasVault.UnitTests/TestData/Exports/bitwarden.csv @@ -4,6 +4,6 @@ Business,,login,Item for business folder,,,0,,crisply,4CSp43uhSZri8A, ,,login,Test,,,0,,test2,asdasd, Business,,login,TutaNota,,,0,,avtest2@tutamail.com,blabla,otpauth://totp/Strongbox?secret=PLW4SB3PQ7MKVXY2MXF4NEXS6Y&algorithm=SHA1&digits=6&period=30 Business,,login,Aliasvault.net,,,0,https://www.aliasvault.net,root,toor, -Business,,login,TutaNota3,,,0,,avtest3@tutamail.com,blabla,otpauth://totp/Test%20name%3Atest%40test.org?secret=PLW4SB3PQ7MKVXY2MXF4NEXS6Y&issuer=Alias%20Vault +Business,,login,TutaNota3,,,0,"https://www.aliasvault.net,https://app.aliasvault.net,https://downloads.aliasvault.net",avtest3@tutamail.com,blabla,otpauth://totp/Test%20name%3Atest%40test.org?secret=PLW4SB3PQ7MKVXY2MXF4NEXS6Y&issuer=Alias%20Vault Business,,login,TutaNota4,,,0,,avtest4@tutamail.com,blabla,otpauth://totp/my.email@gmail.com?secret=TEST!INVALID&issuer=Alias%20Vault&algorithm=SHA1&digits=6&period=30 Business,,login,TutaNota5,,,0,,avtest5@tutamail.com,blabla,incorrectstring diff --git a/apps/server/Tests/AliasVault.UnitTests/Utilities/ImportExportTests.cs b/apps/server/Tests/AliasVault.UnitTests/Utilities/ImportExportTests.cs index 762d6744f..5e756b6a9 100644 --- a/apps/server/Tests/AliasVault.UnitTests/Utilities/ImportExportTests.cs +++ b/apps/server/Tests/AliasVault.UnitTests/Utilities/ImportExportTests.cs @@ -61,7 +61,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(importedCredential.ServiceName, Is.EqualTo(item.Name)); - Assert.That(importedCredential.ServiceUrl, Is.EqualTo("https://testservice.com")); + Assert.That(importedCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://testservice.com")); Assert.That(importedCredential.Username, Is.EqualTo("testuser")); Assert.That(importedCredential.Notes, Is.EqualTo("Test notes")); Assert.That(importedCredential.CreatedAt?.Date, Is.EqualTo(item.CreatedAt.Date)); @@ -109,10 +109,36 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(aliasVaultCredential.ServiceName, Is.EqualTo("Aliasvault.net")); - Assert.That(aliasVaultCredential.ServiceUrl, Is.EqualTo("https://www.aliasvault.net")); + Assert.That(aliasVaultCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.aliasvault.net")); Assert.That(aliasVaultCredential.Username, Is.EqualTo("root")); Assert.That(aliasVaultCredential.Password, Is.EqualTo("toor")); }); + + // Test entry with multiple URLs (TutaNota3) + var multiUrlCredential = importedCredentials.First(c => c.ServiceName == "TutaNota3"); + Assert.Multiple(() => + { + Assert.That(multiUrlCredential.ServiceName, Is.EqualTo("TutaNota3")); + Assert.That(multiUrlCredential.ServiceUrls, Has.Count.EqualTo(3)); + Assert.That(multiUrlCredential.ServiceUrls![0], Is.EqualTo("https://www.aliasvault.net")); + Assert.That(multiUrlCredential.ServiceUrls[1], Is.EqualTo("https://app.aliasvault.net")); + Assert.That(multiUrlCredential.ServiceUrls[2], Is.EqualTo("https://downloads.aliasvault.net")); + Assert.That(multiUrlCredential.Username, Is.EqualTo("avtest3@tutamail.com")); + }); + + // Verify multiple URLs get converted to multiple FieldValues + var multiUrlItem = convertedItems.First(i => i.Name == "TutaNota3"); + var urlFieldValues = multiUrlItem.FieldValues.Where(fv => fv.FieldKey == FieldKey.LoginUrl).OrderBy(fv => fv.Weight).ToList(); + Assert.Multiple(() => + { + Assert.That(urlFieldValues, Has.Count.EqualTo(3)); + Assert.That(urlFieldValues[0].Value, Is.EqualTo("https://www.aliasvault.net")); + Assert.That(urlFieldValues[1].Value, Is.EqualTo("https://app.aliasvault.net")); + Assert.That(urlFieldValues[2].Value, Is.EqualTo("https://downloads.aliasvault.net")); + Assert.That(urlFieldValues[0].Weight, Is.EqualTo(0)); + Assert.That(urlFieldValues[1].Weight, Is.EqualTo(1)); + Assert.That(urlFieldValues[2].Weight, Is.EqualTo(2)); + }); } /// @@ -146,7 +172,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleCredential.ServiceName, Is.EqualTo("Sample")); - Assert.That(sampleCredential.ServiceUrl, Is.EqualTo("https://strongboxsafe.com")); + Assert.That(sampleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://strongboxsafe.com")); Assert.That(sampleCredential.Username, Is.EqualTo("username")); Assert.That(sampleCredential.Password, Is.EqualTo("&3V_$z?Aiw-_x+nbYj")); }); @@ -183,7 +209,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(onePasswordAccount.ServiceName, Is.EqualTo("1Password Account (dpatel)")); - Assert.That(onePasswordAccount.ServiceUrl, Is.EqualTo("https://my.1password.com")); + Assert.That(onePasswordAccount.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://my.1password.com")); Assert.That(onePasswordAccount.Username, Is.EqualTo("derekpatel@aliasvault.net")); Assert.That(onePasswordAccount.Password, Is.EqualTo("passwordexample")); Assert.That(onePasswordAccount.Notes, Is.EqualTo("You can use this login to sign in to your account on 1password.com.")); @@ -211,7 +237,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(exampleCredential.ServiceName, Is.EqualTo("example.com")); - Assert.That(exampleCredential.ServiceUrl, Is.EqualTo("https://example.com/")); + Assert.That(exampleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://example.com/")); Assert.That(exampleCredential.Username, Is.EqualTo("usernamegoogle")); Assert.That(exampleCredential.Password, Is.EqualTo("passwordgoogle")); Assert.That(exampleCredential.Notes, Is.EqualTo("Note for example password from Google")); @@ -221,7 +247,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(facebookCredential.ServiceName, Is.EqualTo("facebook.com")); - Assert.That(facebookCredential.ServiceUrl, Is.EqualTo("https://facebook.com/")); + Assert.That(facebookCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://facebook.com/")); Assert.That(facebookCredential.Username, Is.EqualTo("facebookuser")); Assert.That(facebookCredential.Password, Is.EqualTo("facebookpass")); Assert.That(facebookCredential.Notes, Is.EqualTo("Facebook comment")); @@ -249,7 +275,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(exampleCredential.ServiceName, Is.EqualTo("example.com")); - Assert.That(exampleCredential.ServiceUrl, Is.EqualTo("https://example.com")); + Assert.That(exampleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://example.com")); Assert.That(exampleCredential.Username, Is.EqualTo("username-example")); Assert.That(exampleCredential.Password, Is.EqualTo("examplepassword")); }); @@ -258,7 +284,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(youtubeCredential.ServiceName, Is.EqualTo("youtube.com")); - Assert.That(youtubeCredential.ServiceUrl, Is.EqualTo("https://youtube.com")); + Assert.That(youtubeCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://youtube.com")); Assert.That(youtubeCredential.Username, Is.EqualTo("youtubeusername")); Assert.That(youtubeCredential.Password, Is.EqualTo("youtubepassword")); }); @@ -285,7 +311,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleEntry.ServiceName, Is.EqualTo("Sample Entry")); - Assert.That(sampleEntry.ServiceUrl, Is.EqualTo("https://keepass.info/")); + Assert.That(sampleEntry.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://keepass.info/")); Assert.That(sampleEntry.Username, Is.EqualTo("User Name")); Assert.That(sampleEntry.Password, Is.EqualTo("Password")); Assert.That(sampleEntry.Notes, Is.EqualTo("Notes")); @@ -295,7 +321,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleEntry2.ServiceName, Is.EqualTo("Sample Entry #2")); - Assert.That(sampleEntry2.ServiceUrl, Is.EqualTo("https://keepass.info/help/kb/testform.html")); + Assert.That(sampleEntry2.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://keepass.info/help/kb/testform.html")); Assert.That(sampleEntry2.Username, Is.EqualTo("Michael321")); Assert.That(sampleEntry2.Password, Is.EqualTo("12345")); Assert.That(sampleEntry2.Notes, Is.Empty); @@ -323,7 +349,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(specialEntry.ServiceName, Is.EqualTo("Entry with \"notes\" special chars")); - Assert.That(specialEntry.ServiceUrl, Is.Empty); + Assert.That(specialEntry.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(specialEntry.Username, Is.Empty); Assert.That(specialEntry.Password, Is.EqualTo("DVfIsb4TGkL7oKCwyiet")); Assert.That(specialEntry.Notes, Does.Contain("\"with quotes\"")); @@ -336,7 +362,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleEntry.ServiceName, Is.EqualTo("Sample Entry")); - Assert.That(sampleEntry.ServiceUrl, Is.EqualTo("https://keepass.info/")); + Assert.That(sampleEntry.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://keepass.info/")); Assert.That(sampleEntry.Username, Is.EqualTo("User Name")); Assert.That(sampleEntry.Password, Is.EqualTo("Password")); Assert.That(sampleEntry.Notes, Is.EqualTo("Notes")); @@ -364,7 +390,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleEntry.ServiceName, Is.EqualTo("Sample Entry")); - Assert.That(sampleEntry.ServiceUrl, Is.EqualTo("https://keepass.info/")); + Assert.That(sampleEntry.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://keepass.info/")); Assert.That(sampleEntry.Username, Is.EqualTo("User Name")); Assert.That(sampleEntry.Password, Is.EqualTo("Password")); Assert.That(sampleEntry.Notes, Is.EqualTo("Notes")); @@ -375,7 +401,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(sampleEntry2.ServiceName, Is.EqualTo("Sample Entry #2")); - Assert.That(sampleEntry2.ServiceUrl, Is.EqualTo("https://keepass.info/help/kb/testform.html")); + Assert.That(sampleEntry2.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://keepass.info/help/kb/testform.html")); Assert.That(sampleEntry2.Username, Is.EqualTo("Michael321")); Assert.That(sampleEntry2.Password, Is.EqualTo("12345")); Assert.That(sampleEntry2.Notes, Is.Empty); @@ -404,7 +430,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(testProton1Credential.ServiceName, Is.EqualTo("Test proton 1")); - Assert.That(testProton1Credential.ServiceUrl, Is.EqualTo("https://www.website.com/")); + Assert.That(testProton1Credential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.website.com/")); Assert.That(testProton1Credential.Username, Is.EqualTo("user1")); Assert.That(testProton1Credential.Password, Is.EqualTo("pass1")); Assert.That(testProton1Credential.TwoFactorSecret, Is.EqualTo("otpauth://totp/Strongbox?secret=PLW4SB3PQ7MKVXY2MXF4NEXS6Y&algorithm=SHA1&digits=6&period=30")); @@ -455,7 +481,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(testCredential.ServiceName, Is.EqualTo("Test")); - Assert.That(testCredential.ServiceUrl, Is.EqualTo("https://Test")); + Assert.That(testCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://Test")); Assert.That(testCredential.Username, Is.EqualTo("Test username")); Assert.That(testCredential.Password, Is.EqualTo("password123")); Assert.That(testCredential.Notes, Is.Null); @@ -465,7 +491,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(googleCredential.ServiceName, Is.EqualTo("Google")); - Assert.That(googleCredential.ServiceUrl, Is.EqualTo("https://www.google.com")); + Assert.That(googleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.google.com")); Assert.That(googleCredential.Username, Is.EqualTo("googleuser")); Assert.That(googleCredential.Password, Is.EqualTo("googlepassword")); Assert.That(googleCredential.Notes, Is.Null); @@ -475,7 +501,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(localCredential.ServiceName, Is.EqualTo("Local")); - Assert.That(localCredential.ServiceUrl, Is.EqualTo("https://www.testwebsite.local")); + Assert.That(localCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.testwebsite.local")); Assert.That(localCredential.Username, Is.EqualTo("testusername")); Assert.That(localCredential.Password, Is.EqualTo("testpassword")); Assert.That(localCredential.Notes, Is.EqualTo("testnote\nAlternative username 1: testusernamealternative")); @@ -503,7 +529,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(exampleCredential.ServiceName, Is.EqualTo("Examplename")); - Assert.That(exampleCredential.ServiceUrl, Is.EqualTo("https://example.com")); + Assert.That(exampleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://example.com")); Assert.That(exampleCredential.Username, Is.EqualTo("Exampleusername")); Assert.That(exampleCredential.Password, Is.EqualTo("examplepassword")); Assert.That(exampleCredential.Notes, Is.EqualTo("Examplenotes")); @@ -515,7 +541,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(userWithoutUrlCredential.ServiceName, Is.EqualTo("Userwithouturlornotes")); - Assert.That(userWithoutUrlCredential.ServiceUrl, Is.Null); + Assert.That(userWithoutUrlCredential.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(userWithoutUrlCredential.Username, Is.EqualTo("userwithouturlornotes")); Assert.That(userWithoutUrlCredential.Password, Is.EqualTo("userpass")); Assert.That(userWithoutUrlCredential.Notes, Is.Empty); @@ -527,7 +553,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(secureNoteCredential.ServiceName, Is.EqualTo("securenote1")); - Assert.That(secureNoteCredential.ServiceUrl, Is.Null); + Assert.That(secureNoteCredential.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(secureNoteCredential.Username, Is.Empty); Assert.That(secureNoteCredential.Password, Is.Empty); Assert.That(secureNoteCredential.Notes, Is.EqualTo("Securenotecontent here")); @@ -539,7 +565,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(creditCardCredential.ServiceName, Is.EqualTo("Paymentcard1")); - Assert.That(creditCardCredential.ServiceUrl, Is.Null); // Should be normalized to null + Assert.That(creditCardCredential.ServiceUrls?.FirstOrDefault(), Is.Null); // Should be normalized to null Assert.That(creditCardCredential.Username, Is.Empty); Assert.That(creditCardCredential.Password, Is.Empty); Assert.That(creditCardCredential.ItemType, Is.EqualTo(ImportedItemType.Creditcard)); @@ -573,7 +599,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(gmailCredential.ServiceName, Is.EqualTo("Gmail")); - Assert.That(gmailCredential.ServiceUrl, Is.EqualTo("https://gmail.com")); + Assert.That(gmailCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://gmail.com")); Assert.That(gmailCredential.Username, Is.EqualTo("your.email@gmail.com")); Assert.That(gmailCredential.Password, Is.EqualTo("your_password")); Assert.That(gmailCredential.Notes, Is.EqualTo("Important email account")); @@ -585,7 +611,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(facebookCredential.ServiceName, Is.EqualTo("Facebook")); - Assert.That(facebookCredential.ServiceUrl, Is.EqualTo("https://facebook.com")); + Assert.That(facebookCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://facebook.com")); Assert.That(facebookCredential.Username, Is.EqualTo("your.username")); Assert.That(facebookCredential.Password, Is.EqualTo("your_password")); Assert.That(facebookCredential.Notes, Is.EqualTo("Social media account")); @@ -597,7 +623,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(githubCredential.ServiceName, Is.EqualTo("GitHub")); - Assert.That(githubCredential.ServiceUrl, Is.EqualTo("https://github.com")); + Assert.That(githubCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://github.com")); Assert.That(githubCredential.Username, Is.EqualTo("developer_username")); Assert.That(githubCredential.Password, Is.EqualTo("your_password")); Assert.That(githubCredential.Notes, Is.EqualTo("Development platform")); @@ -609,7 +635,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(secureNoteCredential.ServiceName, Is.EqualTo("Secure Note")); - Assert.That(secureNoteCredential.ServiceUrl, Is.Null); + Assert.That(secureNoteCredential.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(secureNoteCredential.Username, Is.Empty); Assert.That(secureNoteCredential.Password, Is.Empty); Assert.That(secureNoteCredential.Notes, Is.EqualTo("Important information or notes without login credentials")); @@ -638,7 +664,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(gmailCredential.ServiceName, Is.EqualTo("Gmail")); - Assert.That(gmailCredential.ServiceUrl, Is.EqualTo("https://gmail.com")); + Assert.That(gmailCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://gmail.com")); Assert.That(gmailCredential.Username, Is.EqualTo("testuser@gmail.com")); Assert.That(gmailCredential.Password, Is.EqualTo("gmailpass123")); Assert.That(gmailCredential.Notes, Is.EqualTo("Important email account")); @@ -649,7 +675,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(githubCredential.ServiceName, Is.EqualTo("GitHub")); - Assert.That(githubCredential.ServiceUrl, Is.EqualTo("https://github.com")); + Assert.That(githubCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://github.com")); Assert.That(githubCredential.Username, Is.EqualTo("devuser")); Assert.That(githubCredential.Password, Is.EqualTo("devpass789")); Assert.That(githubCredential.Notes, Is.EqualTo("Development platform")); @@ -660,7 +686,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(secureNoteCredential.ServiceName, Is.EqualTo("Secure Note")); - Assert.That(secureNoteCredential.ServiceUrl, Is.Null); + Assert.That(secureNoteCredential.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(secureNoteCredential.Username, Is.Empty); Assert.That(secureNoteCredential.Password, Is.Empty); Assert.That(secureNoteCredential.Notes, Is.EqualTo("Important information stored securely")); @@ -688,7 +714,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(passwordCredential.ServiceName, Is.EqualTo("Password title")); - Assert.That(passwordCredential.ServiceUrl, Is.EqualTo("http://google.nl")); + Assert.That(passwordCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("http://google.nl")); Assert.That(passwordCredential.Username, Is.EqualTo("email@example.tld")); Assert.That(passwordCredential.Password, Is.EqualTo("password")); Assert.That(passwordCredential.FolderPath, Is.EqualTo("Business")); @@ -701,7 +727,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(secureNote.ServiceName, Is.EqualTo("SecureNote1")); - Assert.That(secureNote.ServiceUrl, Is.Null); + Assert.That(secureNote.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(secureNote.Username, Is.Empty); Assert.That(secureNote.Password, Is.Empty); Assert.That(secureNote.ItemType, Is.EqualTo(ImportedItemType.Note)); @@ -817,7 +843,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(credential3.ServiceName, Is.EqualTo("credential3")); - Assert.That(credential3.ServiceUrl, Is.Empty); + Assert.That(credential3.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(credential3.Username, Is.EqualTo("username3")); Assert.That(credential3.Password, Is.Empty); Assert.That(credential3.Notes, Is.EqualTo("without password")); @@ -836,7 +862,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(service2Credential.ServiceName, Is.EqualTo("service2")); - Assert.That(service2Credential.ServiceUrl, Is.EqualTo("https://service2.com")); + Assert.That(service2Credential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://service2.com")); Assert.That(service2Credential.Username, Is.EqualTo("username2")); Assert.That(service2Credential.Password, Is.EqualTo("password2")); Assert.That(service2Credential.Notes, Is.Empty); @@ -853,7 +879,7 @@ public class ImportExportTests Assert.Multiple(() => { Assert.That(service1Credential.ServiceName, Is.EqualTo("service1")); - Assert.That(service1Credential.ServiceUrl, Is.Empty); + Assert.That(service1Credential.ServiceUrls?.FirstOrDefault(), Is.Null); Assert.That(service1Credential.Username, Is.EqualTo("username1")); Assert.That(service1Credential.Password, Is.EqualTo("password1")); Assert.That(service1Credential.Notes, Is.EqualTo("notes1")); @@ -1126,6 +1152,118 @@ public class ImportExportTests Assert.That(folderNames, Is.Not.Null, "Should return a set (even if empty)"); } + /// + /// Test case for importing credentials from RoboForm CSV and ensuring all values are present. + /// + /// Async task. + [Test] + public async Task ImportCredentialsFromRoboformCsv() + { + // Arrange + var fileContent = await ResourceReaderUtility.ReadEmbeddedResourceStringAsync("AliasVault.UnitTests.TestData.Exports.roboform.csv"); + + // Act + var importedCredentials = await RoboformImporter.ImportFromCsvAsync(fileContent); + + // Assert - Should import 4 records + Assert.That(importedCredentials, Has.Count.EqualTo(4)); + + // Test regular login credential + var comCredential = importedCredentials.First(c => c.ServiceName == "Com"); + Assert.Multiple(() => + { + Assert.That(comCredential.ServiceName, Is.EqualTo("Com")); + Assert.That(comCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.example.com.com")); + Assert.That(comCredential.Username, Is.EqualTo("username1")); + Assert.That(comCredential.Password, Is.EqualTo("password1")); + Assert.That(comCredential.Notes, Is.Null.Or.Empty); + Assert.That(comCredential.FolderPath, Is.Null); + Assert.That(comCredential.ItemType, Is.EqualTo(ImportedItemType.Login)); + }); + + // Test credential with note + var exampleCredential = importedCredentials.First(c => c.ServiceName == "Example"); + Assert.Multiple(() => + { + Assert.That(exampleCredential.ServiceName, Is.EqualTo("Example")); + Assert.That(exampleCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.example.com")); + Assert.That(exampleCredential.Username, Is.EqualTo("exampleusername")); + Assert.That(exampleCredential.Password, Is.EqualTo("examplepassword")); + Assert.That(exampleCredential.Notes, Is.EqualTo("Examplenote")); + Assert.That(exampleCredential.FolderPath, Is.Null); + Assert.That(exampleCredential.ItemType, Is.EqualTo(ImportedItemType.Login)); + }); + + // Test secure note (no URL, login, or password) + var safeNoteCredential = importedCredentials.First(c => c.ServiceName == "Safenotename"); + Assert.Multiple(() => + { + Assert.That(safeNoteCredential.ServiceName, Is.EqualTo("Safenotename")); + Assert.That(safeNoteCredential.ServiceUrls?.FirstOrDefault(), Is.Null); + Assert.That(safeNoteCredential.Username, Is.Null.Or.Empty); + Assert.That(safeNoteCredential.Password, Is.Null.Or.Empty); + Assert.That(safeNoteCredential.Notes, Is.EqualTo("Safenote content example here")); + Assert.That(safeNoteCredential.ItemType, Is.EqualTo(ImportedItemType.Note)); + }); + + // Test credential in folder + var businessCredential = importedCredentials.First(c => c.ServiceName == "Business"); + Assert.Multiple(() => + { + Assert.That(businessCredential.ServiceName, Is.EqualTo("Business")); + Assert.That(businessCredential.ServiceUrls?.FirstOrDefault(), Is.EqualTo("https://www.business.com")); + Assert.That(businessCredential.Username, Is.EqualTo("businessusername")); + Assert.That(businessCredential.Password, Is.EqualTo("businesspassword")); + Assert.That(businessCredential.FolderPath, Is.EqualTo("Business")); + Assert.That(businessCredential.ItemType, Is.EqualTo(ImportedItemType.Login)); + }); + } + + /// + /// Test case for RoboForm folder import. + /// + /// Async task. + [Test] + public async Task RoboformFolderImport() + { + // Arrange + var fileContent = await ResourceReaderUtility.ReadEmbeddedResourceStringAsync("AliasVault.UnitTests.TestData.Exports.roboform.csv"); + + // Act + var importedCredentials = await RoboformImporter.ImportFromCsvAsync(fileContent); + + // Assert - verify folder path is extracted (leading slash removed) + var folderNames = BaseImporter.CollectUniqueFolderNames(importedCredentials); + Assert.That(folderNames, Does.Contain("Business")); + + var credentialWithFolder = importedCredentials.First(c => c.FolderPath == "Business"); + Assert.That(credentialWithFolder.ServiceName, Is.EqualTo("Business")); + } + + /// + /// Test case for RoboForm secure note detection. + /// + /// Async task. + [Test] + public async Task RoboformSecureNoteDetection() + { + // Arrange + var fileContent = await ResourceReaderUtility.ReadEmbeddedResourceStringAsync("AliasVault.UnitTests.TestData.Exports.roboform.csv"); + + // Act + var importedCredentials = await RoboformImporter.ImportFromCsvAsync(fileContent); + var items = BaseImporter.ConvertToItem(importedCredentials); + + // Assert - verify secure note is detected + var secureNoteItem = items.FirstOrDefault(i => i.Name == "Safenotename"); + Assert.That(secureNoteItem, Is.Not.Null, "Should find Safenotename"); + Assert.That(secureNoteItem!.ItemType, Is.EqualTo(ItemType.Note), "Secure note should have Note item type"); + + // Verify the note content is preserved + var notesFieldValue = secureNoteItem.FieldValues.FirstOrDefault(fv => fv.FieldKey == FieldKey.NotesContent); + Assert.That(notesFieldValue?.Value, Is.EqualTo("Safenote content example here")); + } + /// /// Helper method to add a field value to an item. /// diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/BaseImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/BaseImporter.cs index b0cf7ade9..e83ae17cd 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/BaseImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/BaseImporter.cs @@ -138,6 +138,29 @@ public static class BaseImporter return decoded; } + + /// + /// Parses a URL string that may contain multiple comma-separated URLs. + /// Many password managers export multiple URIs as comma-separated values within quotes. + /// + /// The URL string to parse. + /// A list of URLs, or null if the input is empty. + public static List? ParseUrls(string? url) + { + if (string.IsNullOrWhiteSpace(url)) + { + return null; + } + + // Split by comma and filter out empty entries + var urls = url.Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(u => u.Trim()) + .Where(u => !string.IsNullOrWhiteSpace(u)) + .ToList(); + + return urls.Count > 0 ? urls : null; + } + /// /// Converts a list of imported credentials to a list of AliasVault Items. /// @@ -185,7 +208,7 @@ public static class BaseImporter else { // Add standard field values for non-empty fields (Login, Alias, Note types) - AddFieldValueIfNotEmpty(item, FieldKey.LoginUrl, importedCredential.ServiceUrl, createdAt, updatedAt); + AddUrlFieldValues(item, importedCredential.ServiceUrls, createdAt, updatedAt); AddFieldValueIfNotEmpty(item, FieldKey.LoginUsername, importedCredential.Username, createdAt, updatedAt); AddFieldValueIfNotEmpty(item, FieldKey.LoginPassword, importedCredential.Password, createdAt, updatedAt); AddFieldValueIfNotEmpty(item, FieldKey.LoginEmail, importedCredential.Email, createdAt, updatedAt); @@ -354,6 +377,39 @@ public static class BaseImporter AddFieldValueIfNotEmpty(item, FieldKey.CardExpiryYear, card.ExpiryYear, createdAt, updatedAt); } + /// + /// Adds URL field values to an item, supporting multiple URLs with proper weight ordering. + /// + /// The item to add the field values to. + /// The list of URLs to add. + /// The created timestamp. + /// The updated timestamp. + private static void AddUrlFieldValues(Item item, List? urls, DateTime createdAt, DateTime updatedAt) + { + if (urls == null || urls.Count == 0) + { + return; + } + + var weight = 0; + foreach (var url in urls) + { + if (!string.IsNullOrEmpty(url)) + { + item.FieldValues.Add(new FieldValue + { + Id = Guid.NewGuid(), + ItemId = item.Id, + FieldKey = FieldKey.LoginUrl, + Value = url, + Weight = weight++, + CreatedAt = createdAt, + UpdatedAt = updatedAt, + }); + } + } + } + /// /// Adds a field value to an item if the value is not empty. /// diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/BitwardenImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/BitwardenImporter.cs index 4758b3a60..6bbe9f24c 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/BitwardenImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/BitwardenImporter.cs @@ -33,7 +33,7 @@ public static class BitwardenImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = record.URL, + ServiceUrls = BaseImporter.ParseUrls(record.URL), Username = record.Username, Password = record.Password, TwoFactorSecret = record.OTPAuth, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/ChromeImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/ChromeImporter.cs index f1134ba39..f0c039db8 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/ChromeImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/ChromeImporter.cs @@ -33,7 +33,7 @@ public static class ChromeImporter var credential = new ImportedCredential { ServiceName = record.Name, - ServiceUrl = record.Url, + ServiceUrls = BaseImporter.ParseUrls(record.Url), Username = record.Username, Password = record.Password, Notes = record.Note diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/DashlaneImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/DashlaneImporter.cs index d67de23c6..566fcc514 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/DashlaneImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/DashlaneImporter.cs @@ -30,7 +30,7 @@ public static class DashlaneImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = record.URL, + ServiceUrls = BaseImporter.ParseUrls(record.URL), Username = record.Username, Password = record.Password, TwoFactorSecret = record.OTPUrl, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/DropboxImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/DropboxImporter.cs index 998158498..05b72b4f1 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/DropboxImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/DropboxImporter.cs @@ -36,7 +36,7 @@ public static class DropboxImporter var credential = new ImportedCredential { ServiceName = record.Name, - ServiceUrl = NormalizeUrl(record.Url), + ServiceUrls = BaseImporter.ParseUrls(record.Url), Username = record.Username, Password = record.Password, Notes = record.Notes @@ -47,19 +47,4 @@ public static class DropboxImporter return credentials; } - - /// - /// Normalizes URL values from Dropbox CSV format. - /// - /// The URL from the CSV record. - /// The normalized URL or null if it's empty or invalid. - private static string? NormalizeUrl(string? url) - { - if (string.IsNullOrWhiteSpace(url)) - { - return null; - } - - return url; - } } diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/FirefoxImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/FirefoxImporter.cs index 0e86be066..36f884828 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/FirefoxImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/FirefoxImporter.cs @@ -37,7 +37,7 @@ public static class FirefoxImporter var credential = new ImportedCredential { ServiceName = serviceName, - ServiceUrl = record.Url, + ServiceUrls = BaseImporter.ParseUrls(record.Url), Username = record.Username, Password = record.Password }; diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/GenericCsvImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/GenericCsvImporter.cs index 22fc5f60e..b739cd236 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/GenericCsvImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/GenericCsvImporter.cs @@ -66,7 +66,7 @@ public static class GenericCsvImporter var credential = new ImportedCredential { ServiceName = record.ServiceName.Trim(), - ServiceUrl = NormalizeUrl(record.Url), + ServiceUrls = BaseImporter.ParseUrls(NormalizeUrl(record.Url)), Username = record.Username?.Trim(), Password = record.Password?.Trim(), TwoFactorSecret = record.TotpSecret?.Trim(), diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassImporter.cs index 46082baad..e94093d73 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassImporter.cs @@ -81,7 +81,7 @@ public static class KeePassImporter var credential = new ImportedCredential { ServiceName = record.Account ?? string.Empty, - ServiceUrl = record.Website, + ServiceUrls = BaseImporter.ParseUrls(record.Website), Username = record.LoginName, Password = record.Password, Notes = record.Comments diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassXcImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassXcImporter.cs index 04b47a9e4..5faf3f1f5 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassXcImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/KeePassXcImporter.cs @@ -30,7 +30,7 @@ public static class KeePassXcImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = record.URL, + ServiceUrls = BaseImporter.ParseUrls(record.URL), Username = record.Username, Password = record.Password, TwoFactorSecret = record.TOTP, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/LastPassImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/LastPassImporter.cs index 1605d6a34..44f49fd59 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/LastPassImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/LastPassImporter.cs @@ -61,7 +61,7 @@ public static class LastPassImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = normalizedUrl, + ServiceUrls = BaseImporter.ParseUrls(normalizedUrl), Username = record.Username, Password = record.Password, TwoFactorSecret = record.TwoFactorSecret, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/NordPassImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/NordPassImporter.cs index 413ef5715..07df56aeb 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/NordPassImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/NordPassImporter.cs @@ -55,7 +55,7 @@ public static class NordPassImporter var credential = new ImportedCredential { ServiceName = record.Name, - ServiceUrl = string.IsNullOrWhiteSpace(record.Url) ? null : record.Url, + ServiceUrls = BaseImporter.ParseUrls(record.Url), Email = record.Email, Username = record.Username, Password = record.Password, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/OnePasswordImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/OnePasswordImporter.cs index b57238122..7abbfe8b6 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/OnePasswordImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/OnePasswordImporter.cs @@ -30,7 +30,7 @@ public static class OnePasswordImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = record.Url, + ServiceUrls = BaseImporter.ParseUrls(record.Url), Username = record.Username, Password = record.Password, TwoFactorSecret = record.OTPAuth, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/ProtonPassImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/ProtonPassImporter.cs index 98f93a779..94f4955c5 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/ProtonPassImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/ProtonPassImporter.cs @@ -30,7 +30,7 @@ public static class ProtonPassImporter var credential = new ImportedCredential { ServiceName = record.Name, - ServiceUrl = record.Url, + ServiceUrls = BaseImporter.ParseUrls(record.Url), Email = record.Email, Username = record.Username, Password = record.Password, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Importers/StrongboxImporter.cs b/apps/server/Utilities/AliasVault.ImportExport/Importers/StrongboxImporter.cs index 69d669095..ad60ec792 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Importers/StrongboxImporter.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Importers/StrongboxImporter.cs @@ -30,7 +30,7 @@ public static class StrongboxImporter var credential = new ImportedCredential { ServiceName = record.Title, - ServiceUrl = record.URL, + ServiceUrls = BaseImporter.ParseUrls(record.URL), Username = record.Username, Password = record.Password, TwoFactorSecret = record.OTPAuth, diff --git a/apps/server/Utilities/AliasVault.ImportExport/ItemCsvService.cs b/apps/server/Utilities/AliasVault.ImportExport/ItemCsvService.cs index 6733c5c57..5cc3c6a29 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/ItemCsvService.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/ItemCsvService.cs @@ -9,6 +9,7 @@ namespace AliasVault.ImportExport; using AliasClientDb; using AliasClientDb.Models; +using AliasVault.ImportExport.Importers; using AliasVault.ImportExport.Models; using CsvHelper; using CsvHelper.Configuration; @@ -98,7 +99,7 @@ public static class ItemCsvService var credential = new ImportedCredential { ServiceName = record.ServiceName, - ServiceUrl = record.ServiceUrl, + ServiceUrls = BaseImporter.ParseUrls(record.ServiceUrl), Username = record.Username, Password = record.CurrentPassword, Email = record.AliasEmail, diff --git a/apps/server/Utilities/AliasVault.ImportExport/Models/ImportedCredential.cs b/apps/server/Utilities/AliasVault.ImportExport/Models/ImportedCredential.cs index a06fa1262..34d20ba1f 100644 --- a/apps/server/Utilities/AliasVault.ImportExport/Models/ImportedCredential.cs +++ b/apps/server/Utilities/AliasVault.ImportExport/Models/ImportedCredential.cs @@ -20,9 +20,9 @@ public class ImportedCredential public string? ServiceName { get; set; } /// - /// Gets or sets the service URL. + /// Gets or sets the service URLs. /// - public string? ServiceUrl { get; set; } + public List? ServiceUrls { get; set; } /// /// Gets or sets the username or email.