mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
Enable Encrypted json export of vaults (#1174)
* Enable Encrypted json export of vaults * Match jslib export of non-org ciphers * Clean up export * Update src/App/Pages/Settings/ExportVaultPage.xaml.cs Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com> Co-authored-by: Matt Gibson <mdgibson@Matts-MBP.lan> Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
parent
6e40b7f25b
commit
3227daddaf
18 changed files with 242 additions and 54 deletions
|
@ -35,6 +35,7 @@
|
||||||
x:Name="_fileFormatPicker"
|
x:Name="_fileFormatPicker"
|
||||||
ItemsSource="{Binding FileFormatOptions, Mode=OneTime}"
|
ItemsSource="{Binding FileFormatOptions, Mode=OneTime}"
|
||||||
SelectedIndex="{Binding FileFormatSelectedIndex}"
|
SelectedIndex="{Binding FileFormatSelectedIndex}"
|
||||||
|
SelectedIndexChanged="FileFormat_Changed"
|
||||||
StyleClass="box-value" />
|
StyleClass="box-value" />
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
<Grid StyleClass="box-row">
|
<Grid StyleClass="box-row">
|
||||||
|
@ -84,7 +85,7 @@
|
||||||
Text="{Binding Converter={StaticResource toUpper}, ConverterParameter={u:I18n Warning}}"
|
Text="{Binding Converter={StaticResource toUpper}, ConverterParameter={u:I18n Warning}}"
|
||||||
FontAttributes="Bold" />
|
FontAttributes="Bold" />
|
||||||
<Span Text=": " FontAttributes="Bold" />
|
<Span Text=": " FontAttributes="Bold" />
|
||||||
<Span Text="{u:I18n ExportVaultWarning}" />
|
<Span Text="{Binding ExportWarningMessage}" />
|
||||||
</FormattedString>
|
</FormattedString>
|
||||||
</Label.FormattedText>
|
</Label.FormattedText>
|
||||||
</Label>
|
</Label>
|
||||||
|
|
|
@ -65,5 +65,10 @@ namespace Bit.App.Pages
|
||||||
await _vm.ExportVaultAsync();
|
await _vm.ExportVaultAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FileFormat_Changed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
_vm?.UpdateWarning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,12 @@ namespace Bit.App.Pages
|
||||||
private readonly IExportService _exportService;
|
private readonly IExportService _exportService;
|
||||||
|
|
||||||
private int _fileFormatSelectedIndex;
|
private int _fileFormatSelectedIndex;
|
||||||
|
private string _exportWarningMessage;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
private byte[] _exportResult;
|
private byte[] _exportResult;
|
||||||
private string _defaultFilename;
|
private string _defaultFilename;
|
||||||
|
private bool _initialized = false;
|
||||||
|
|
||||||
public ExportVaultPageViewModel()
|
public ExportVaultPageViewModel()
|
||||||
{
|
{
|
||||||
|
@ -42,13 +44,16 @@ namespace Bit.App.Pages
|
||||||
FileFormatOptions = new List<KeyValuePair<string, string>>
|
FileFormatOptions = new List<KeyValuePair<string, string>>
|
||||||
{
|
{
|
||||||
new KeyValuePair<string, string>("json", ".json"),
|
new KeyValuePair<string, string>("json", ".json"),
|
||||||
new KeyValuePair<string, string>("csv", ".csv")
|
new KeyValuePair<string, string>("csv", ".csv"),
|
||||||
|
new KeyValuePair<string, string>("encrypted_json", ".json (Encrypted)")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task InitAsync()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
|
_initialized = true;
|
||||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||||
|
UpdateWarning();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<KeyValuePair<string, string>> FileFormatOptions { get; set; }
|
public List<KeyValuePair<string, string>> FileFormatOptions { get; set; }
|
||||||
|
@ -59,6 +64,12 @@ namespace Bit.App.Pages
|
||||||
set { SetProperty(ref _fileFormatSelectedIndex, value); }
|
set { SetProperty(ref _fileFormatSelectedIndex, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ExportWarningMessage
|
||||||
|
{
|
||||||
|
get => _exportWarningMessage;
|
||||||
|
set { SetProperty(ref _exportWarningMessage, value); }
|
||||||
|
}
|
||||||
|
|
||||||
public bool ShowPassword
|
public bool ShowPassword
|
||||||
{
|
{
|
||||||
get => _showPassword;
|
get => _showPassword;
|
||||||
|
@ -140,6 +151,24 @@ namespace Bit.App.Pages
|
||||||
await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure"));
|
await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateWarning()
|
||||||
|
{
|
||||||
|
if (!_initialized)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (FileFormatOptions[FileFormatSelectedIndex].Key)
|
||||||
|
{
|
||||||
|
case "encrypted_json":
|
||||||
|
ExportWarningMessage = _i18nService.T("EncExportVaultWarning");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ExportWarningMessage = _i18nService.T("ExportVaultWarning");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ClearResult()
|
private void ClearResult()
|
||||||
{
|
{
|
||||||
_defaultFilename = null;
|
_defaultFilename = null;
|
||||||
|
|
12
src/App/Resources/AppResources.Designer.cs
generated
12
src/App/Resources/AppResources.Designer.cs
generated
|
@ -1,6 +1,7 @@
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// <auto-generated>
|
// <auto-generated>
|
||||||
// This code was generated by a tool.
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
//
|
//
|
||||||
// Changes to this file may cause incorrect behavior and will be lost if
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
// the code is regenerated.
|
// the code is regenerated.
|
||||||
|
@ -9,6 +10,7 @@
|
||||||
|
|
||||||
namespace Bit.App.Resources {
|
namespace Bit.App.Resources {
|
||||||
using System;
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
|
||||||
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
@ -2816,7 +2818,15 @@ namespace Bit.App.Resources {
|
||||||
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
return ResourceManager.GetString("ExportVaultWarning", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string EncExportVaultWarning
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return ResourceManager.GetString("EncExportVaultWarning", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static string Warning {
|
public static string Warning {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("Warning", resourceCulture);
|
return ResourceManager.GetString("Warning", resourceCulture);
|
||||||
|
|
|
@ -1602,6 +1602,9 @@
|
||||||
<data name="ExportVaultWarning" xml:space="preserve">
|
<data name="ExportVaultWarning" xml:space="preserve">
|
||||||
<value>This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it.</value>
|
<value>This export contains your vault data in an unencrypted format. You should not store or send the exported file over unsecure channels (such as email). Delete it immediately after you are done using it.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="EncExportVaultWarning" xml:space="preserve">
|
||||||
|
<value>This export encrypts your data using your account's encryption key. If you ever rotate your account's encryption key you should export again since you will not be able to decrypt this export file.</value>
|
||||||
|
</data>
|
||||||
<data name="Warning" xml:space="preserve">
|
<data name="Warning" xml:space="preserve">
|
||||||
<value>Warning</value>
|
<value>Warning</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
@ -16,6 +16,16 @@ namespace Bit.Core.Models.Export
|
||||||
Code = obj.Code;
|
Code = obj.Code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Card(Domain.Card obj)
|
||||||
|
{
|
||||||
|
CardholderName = obj.CardholderName?.EncryptedString;
|
||||||
|
Brand = obj.Brand?.EncryptedString;
|
||||||
|
Number = obj.Number?.EncryptedString;
|
||||||
|
ExpMonth = obj.ExpMonth?.EncryptedString;
|
||||||
|
ExpYear = obj.ExpYear?.EncryptedString;
|
||||||
|
Code = obj.Code?.EncryptedString;
|
||||||
|
}
|
||||||
|
|
||||||
public string CardholderName { get; set; }
|
public string CardholderName { get; set; }
|
||||||
public string Brand { get; set; }
|
public string Brand { get; set; }
|
||||||
public string Number { get; set; }
|
public string Number { get; set; }
|
||||||
|
|
|
@ -38,6 +38,34 @@ namespace Bit.Core.Models.Export
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Cipher(Domain.Cipher obj)
|
||||||
|
{
|
||||||
|
OrganizationId = obj.OrganizationId;
|
||||||
|
FolderId = obj.FolderId;
|
||||||
|
Type = obj.Type;
|
||||||
|
Name = obj.Name?.EncryptedString;
|
||||||
|
Notes = obj.Notes?.EncryptedString;
|
||||||
|
Favorite = obj.Favorite;
|
||||||
|
|
||||||
|
Fields = obj.Fields?.Select(f => new Field(f)).ToList();
|
||||||
|
|
||||||
|
switch (obj.Type)
|
||||||
|
{
|
||||||
|
case CipherType.Login:
|
||||||
|
Login = new Login(obj.Login);
|
||||||
|
break;
|
||||||
|
case CipherType.SecureNote:
|
||||||
|
SecureNote = new SecureNote(obj.SecureNote);
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
Card = new Card(obj.Card);
|
||||||
|
break;
|
||||||
|
case CipherType.Identity:
|
||||||
|
Identity = new Identity(obj.Identity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string OrganizationId { get; set; }
|
public string OrganizationId { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
public CipherType Type { get; set; }
|
public CipherType Type { get; set; }
|
||||||
|
|
|
@ -9,7 +9,13 @@ namespace Bit.Core.Models.Export
|
||||||
public CipherWithId(CipherView obj) : base(obj)
|
public CipherWithId(CipherView obj) : base(obj)
|
||||||
{
|
{
|
||||||
Id = obj.Id;
|
Id = obj.Id;
|
||||||
CollectionIds = obj.CollectionIds;
|
CollectionIds = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CipherWithId(Domain.Cipher obj) : base(obj)
|
||||||
|
{
|
||||||
|
Id = obj.Id;
|
||||||
|
CollectionIds = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonProperty(Order = int.MinValue)]
|
[JsonProperty(Order = int.MinValue)]
|
||||||
|
|
|
@ -13,6 +13,13 @@ namespace Bit.Core.Models.Export
|
||||||
ExternalId = obj.ExternalId;
|
ExternalId = obj.ExternalId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection(Domain.Collection obj)
|
||||||
|
{
|
||||||
|
OrganizationId = obj.OrganizationId;
|
||||||
|
Name = obj.Name?.EncryptedString;
|
||||||
|
ExternalId = obj.ExternalId;
|
||||||
|
}
|
||||||
|
|
||||||
public string OrganizationId { get; set; }
|
public string OrganizationId { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string ExternalId { get; set; }
|
public string ExternalId { get; set; }
|
||||||
|
|
|
@ -10,6 +10,11 @@ namespace Bit.Core.Models.Export
|
||||||
Id = obj.Id;
|
Id = obj.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CollectionWithId(Domain.Collection obj): base(obj)
|
||||||
|
{
|
||||||
|
Id = obj.Id;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Order = int.MinValue)]
|
[JsonProperty(Order = int.MinValue)]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,13 @@ namespace Bit.Core.Models.Export
|
||||||
Type = obj.Type;
|
Type = obj.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Field(Domain.Field obj)
|
||||||
|
{
|
||||||
|
Name = obj.Name?.EncryptedString;
|
||||||
|
Value = obj.Value?.EncryptedString;
|
||||||
|
Type = obj.Type;
|
||||||
|
}
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
public FieldType Type { get; set; }
|
public FieldType Type { get; set; }
|
||||||
|
|
|
@ -11,6 +11,11 @@ namespace Bit.Core.Models.Export
|
||||||
Name = obj.Name;
|
Name = obj.Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Folder(Domain.Folder obj)
|
||||||
|
{
|
||||||
|
Name = obj.Name?.EncryptedString;
|
||||||
|
}
|
||||||
|
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
public FolderView ToView(Folder req, FolderView view = null)
|
public FolderView ToView(Folder req, FolderView view = null)
|
||||||
|
|
|
@ -10,6 +10,11 @@ namespace Bit.Core.Models.Export
|
||||||
Id = obj.Id;
|
Id = obj.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public FolderWithId(Domain.Folder obj) : base(obj)
|
||||||
|
{
|
||||||
|
Id = obj.Id;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonProperty(Order = int.MinValue)]
|
[JsonProperty(Order = int.MinValue)]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,28 @@ namespace Bit.Core.Models.Export
|
||||||
LicenseNumber = obj.LicenseNumber;
|
LicenseNumber = obj.LicenseNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Identity(Domain.Identity obj)
|
||||||
|
{
|
||||||
|
Title = obj.Title?.EncryptedString;
|
||||||
|
FirstName = obj.FirstName?.EncryptedString;
|
||||||
|
MiddleName = obj.FirstName?.EncryptedString;
|
||||||
|
LastName = obj.LastName?.EncryptedString;
|
||||||
|
Address1 = obj.Address1?.EncryptedString;
|
||||||
|
Address2 = obj.Address2?.EncryptedString;
|
||||||
|
Address3 = obj.Address3?.EncryptedString;
|
||||||
|
City = obj.City?.EncryptedString;
|
||||||
|
State = obj.State?.EncryptedString;
|
||||||
|
PostalCode = obj.PostalCode?.EncryptedString;
|
||||||
|
Country = obj.Country?.EncryptedString;
|
||||||
|
Company = obj.Company?.EncryptedString;
|
||||||
|
Email = obj.Email?.EncryptedString;
|
||||||
|
Phone = obj.Phone?.EncryptedString;
|
||||||
|
SSN = obj.SSN?.EncryptedString;
|
||||||
|
Username = obj.Username?.EncryptedString;
|
||||||
|
PassportNumber = obj.PassportNumber?.EncryptedString;
|
||||||
|
LicenseNumber = obj.LicenseNumber?.EncryptedString;
|
||||||
|
}
|
||||||
|
|
||||||
public string Title { get; set; }
|
public string Title { get; set; }
|
||||||
public string FirstName { get; set; }
|
public string FirstName { get; set; }
|
||||||
public string MiddleName { get; set; }
|
public string MiddleName { get; set; }
|
||||||
|
|
|
@ -17,6 +17,15 @@ namespace Bit.Core.Models.Export
|
||||||
Totp = obj.Totp;
|
Totp = obj.Totp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Login(Domain.Login obj)
|
||||||
|
{
|
||||||
|
Uris = obj.Uris?.Select(u => new LoginUri(u)).ToList();
|
||||||
|
|
||||||
|
Username = obj.Username?.EncryptedString;
|
||||||
|
Password = obj.Password?.EncryptedString;
|
||||||
|
Totp = obj.Totp?.EncryptedString;
|
||||||
|
}
|
||||||
|
|
||||||
public List<LoginUri> Uris { get; set; }
|
public List<LoginUri> Uris { get; set; }
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
|
|
|
@ -13,6 +13,12 @@ namespace Bit.Core.Models.Export
|
||||||
Uri = obj.Uri;
|
Uri = obj.Uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LoginUri(Domain.LoginUri obj)
|
||||||
|
{
|
||||||
|
Match = obj.Match;
|
||||||
|
Uri = obj.Uri?.EncryptedString;
|
||||||
|
}
|
||||||
|
|
||||||
public UriMatchType? Match { get; set; }
|
public UriMatchType? Match { get; set; }
|
||||||
public string Uri { get; set; }
|
public string Uri { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,11 @@ namespace Bit.Core.Models.Export
|
||||||
Type = obj.Type;
|
Type = obj.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SecureNote(Domain.SecureNote obj)
|
||||||
|
{
|
||||||
|
Type = obj.Type;
|
||||||
|
}
|
||||||
|
|
||||||
public SecureNoteType Type { get; set; }
|
public SecureNoteType Type { get; set; }
|
||||||
|
|
||||||
public SecureNoteView ToView(SecureNote req, SecureNoteView view = null)
|
public SecureNoteView ToView(SecureNote req, SecureNoteView view = null)
|
||||||
|
|
|
@ -10,7 +10,6 @@ using Bit.Core.Models.Export;
|
||||||
using Bit.Core.Models.View;
|
using Bit.Core.Models.View;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using CsvHelper.Configuration;
|
|
||||||
using CsvHelper.Configuration.Attributes;
|
using CsvHelper.Configuration.Attributes;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
@ -22,9 +21,6 @@ namespace Bit.Core.Services
|
||||||
private readonly IFolderService _folderService;
|
private readonly IFolderService _folderService;
|
||||||
private readonly ICipherService _cipherService;
|
private readonly ICipherService _cipherService;
|
||||||
|
|
||||||
private List<FolderView> _decryptedFolders;
|
|
||||||
private List<CipherView> _decryptedCiphers;
|
|
||||||
|
|
||||||
public ExportService(
|
public ExportService(
|
||||||
IFolderService folderService,
|
IFolderService folderService,
|
||||||
ICipherService cipherService)
|
ICipherService cipherService)
|
||||||
|
@ -35,58 +31,19 @@ namespace Bit.Core.Services
|
||||||
|
|
||||||
public async Task<string> GetExport(string format = "csv")
|
public async Task<string> GetExport(string format = "csv")
|
||||||
{
|
{
|
||||||
_decryptedFolders = await _folderService.GetAllDecryptedAsync();
|
if (format == "encrypted_json")
|
||||||
_decryptedCiphers = await _cipherService.GetAllDecryptedAsync();
|
|
||||||
|
|
||||||
if (format == "csv")
|
|
||||||
{
|
{
|
||||||
var foldersMap = _decryptedFolders.Where(f => f.Id != null).ToDictionary(f => f.Id);
|
var folders = (await _folderService.GetAllAsync()).Where(f => f.Id != null).Select(f => new FolderWithId(f));
|
||||||
|
var items = (await _cipherService.GetAllAsync()).Where(c => c.OrganizationId == null).Select(c => new CipherWithId(c));
|
||||||
|
|
||||||
var exportCiphers = new List<ExportCipher>();
|
return ExportEncryptedJson(folders, items);
|
||||||
foreach (var c in _decryptedCiphers)
|
|
||||||
{
|
|
||||||
// only export logins and secure notes
|
|
||||||
if (c.Type != CipherType.Login && c.Type != CipherType.SecureNote)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c.OrganizationId != null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var cipher = new ExportCipher();
|
|
||||||
cipher.Folder = c.FolderId != null && foldersMap.ContainsKey(c.FolderId)
|
|
||||||
? foldersMap[c.FolderId].Name : null;
|
|
||||||
cipher.Favorite = c.Favorite ? "1" : null;
|
|
||||||
BuildCommonCipher(cipher, c);
|
|
||||||
exportCiphers.Add(cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var writer = new StringWriter())
|
|
||||||
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
|
|
||||||
{
|
|
||||||
csv.WriteRecords(exportCiphers);
|
|
||||||
csv.Flush();
|
|
||||||
return writer.ToString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var jsonDoc = new
|
var decryptedFolders = await _folderService.GetAllDecryptedAsync();
|
||||||
{
|
var decryptedCiphers = await _cipherService.GetAllDecryptedAsync();
|
||||||
Folders = _decryptedFolders.Where(f => f.Id != null).Select(f => new FolderWithId(f)),
|
|
||||||
Items = _decryptedCiphers.Where(c => c.OrganizationId == null)
|
|
||||||
.Select(c => new CipherWithId(c) {CollectionIds = null})
|
|
||||||
};
|
|
||||||
|
|
||||||
return CoreHelpers.SerializeJson(jsonDoc,
|
return format == "csv" ? ExportCsv(decryptedFolders, decryptedCiphers) : ExportJson(decryptedFolders, decryptedCiphers);
|
||||||
new JsonSerializerSettings
|
|
||||||
{
|
|
||||||
Formatting = Formatting.Indented,
|
|
||||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +123,74 @@ namespace Bit.Core.Services
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ExportCsv(IEnumerable<FolderView> decryptedFolders, IEnumerable<CipherView> decryptedCiphers)
|
||||||
|
{
|
||||||
|
var foldersMap = decryptedFolders.Where(f => f.Id != null).ToDictionary(f => f.Id);
|
||||||
|
|
||||||
|
var exportCiphers = new List<ExportCipher>();
|
||||||
|
foreach (var c in decryptedCiphers)
|
||||||
|
{
|
||||||
|
// only export logins and secure notes
|
||||||
|
if (c.Type != CipherType.Login && c.Type != CipherType.SecureNote)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.OrganizationId != null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cipher = new ExportCipher();
|
||||||
|
cipher.Folder = c.FolderId != null && foldersMap.ContainsKey(c.FolderId)
|
||||||
|
? foldersMap[c.FolderId].Name : null;
|
||||||
|
cipher.Favorite = c.Favorite ? "1" : null;
|
||||||
|
BuildCommonCipher(cipher, c);
|
||||||
|
exportCiphers.Add(cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var writer = new StringWriter())
|
||||||
|
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
|
||||||
|
{
|
||||||
|
csv.WriteRecords(exportCiphers);
|
||||||
|
csv.Flush();
|
||||||
|
return writer.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExportJson(IEnumerable<FolderView> decryptedFolders, IEnumerable<CipherView> decryptedCiphers)
|
||||||
|
{
|
||||||
|
var jsonDoc = new
|
||||||
|
{
|
||||||
|
Folders = decryptedFolders.Where(f => f.Id != null).Select(f => new FolderWithId(f)),
|
||||||
|
Items = decryptedCiphers.Where(c => c.OrganizationId == null)
|
||||||
|
.Select(c => new CipherWithId(c) { CollectionIds = null })
|
||||||
|
};
|
||||||
|
|
||||||
|
return CoreHelpers.SerializeJson(jsonDoc,
|
||||||
|
new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
Formatting = Formatting.Indented,
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExportEncryptedJson(IEnumerable<FolderWithId> folders, IEnumerable<CipherWithId> ciphers)
|
||||||
|
{
|
||||||
|
var jsonDoc = new
|
||||||
|
{
|
||||||
|
Folders = folders,
|
||||||
|
Items = ciphers,
|
||||||
|
};
|
||||||
|
|
||||||
|
return CoreHelpers.SerializeJson(jsonDoc,
|
||||||
|
new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
Formatting = Formatting.Indented,
|
||||||
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private class ExportCipher
|
private class ExportCipher
|
||||||
{
|
{
|
||||||
[Name("folder")]
|
[Name("folder")]
|
||||||
|
|
Loading…
Reference in a new issue