using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; using Bit.App.Abstractions; using Bit.App.Resources; using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Exceptions; using Bit.Core.Utilities; using Xamarin.Forms; namespace Bit.App.Pages { public class ExportVaultPageViewModel : BaseViewModel { private readonly IDeviceActionService _deviceActionService; private readonly IPlatformUtilsService _platformUtilsService; private readonly II18nService _i18nService; private readonly IExportService _exportService; private readonly IPolicyService _policyService; private readonly IKeyConnectorService _keyConnectorService; private readonly IUserVerificationService _userVerificationService; private readonly IApiService _apiService; private readonly ILogger _logger; private int _fileFormatSelectedIndex; private string _exportWarningMessage; private bool _showPassword; private string _secret; private byte[] _exportResult; private string _defaultFilename; private bool _initialized = false; private bool _useOTPVerification = false; private string _secretName; private string _instructionText; public ExportVaultPageViewModel() { _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _i18nService = ServiceContainer.Resolve("i18nService"); _exportService = ServiceContainer.Resolve("exportService"); _policyService = ServiceContainer.Resolve("policyService"); _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); _userVerificationService = ServiceContainer.Resolve("userVerificationService"); _apiService = ServiceContainer.Resolve("apiService"); _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.ExportVault; TogglePasswordCommand = new Command(TogglePassword); ExportVaultCommand = new Command(async () => await ExportVaultAsync()); FileFormatOptions = new List> { new KeyValuePair("json", ".json"), new KeyValuePair("csv", ".csv"), new KeyValuePair("encrypted_json", ".json (Encrypted)") }; } public async Task InitAsync() { _initialized = true; FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json"); DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport); UseOTPVerification = await _keyConnectorService.GetUsesKeyConnector(); if (UseOTPVerification) { InstructionText = _i18nService.T("ExportVaultOTPDescription"); SecretName = _i18nService.T("VerificationCode"); } else { InstructionText = _i18nService.T("ExportVaultMasterPasswordDescription"); SecretName = _i18nService.T("MasterPassword"); } UpdateWarning(); } public List> FileFormatOptions { get; set; } private bool _disabledPrivateVaultPolicyEnabled = false; public bool DisablePrivateVaultPolicyEnabled { get => _disabledPrivateVaultPolicyEnabled; set { SetProperty(ref _disabledPrivateVaultPolicyEnabled, value); } } public int FileFormatSelectedIndex { get => _fileFormatSelectedIndex; set { SetProperty(ref _fileFormatSelectedIndex, value); } } public string ExportWarningMessage { get => _exportWarningMessage; set { SetProperty(ref _exportWarningMessage, value); } } public bool ShowPassword { get => _showPassword; set => SetProperty(ref _showPassword, value, additionalPropertyNames: new string[] { nameof(ShowPasswordIcon) }); } public bool UseOTPVerification { get => _useOTPVerification; set => SetProperty(ref _useOTPVerification, value); } public string Secret { get => _secret; set => SetProperty(ref _secret, value); } public string SecretName { get => _secretName; set => SetProperty(ref _secretName, value); } public string InstructionText { get => _instructionText; set => SetProperty(ref _instructionText, value); } public Command TogglePasswordCommand { get; } public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public void TogglePassword() { ShowPassword = !ShowPassword; (Page as ExportVaultPage).SecretEntry.Focus(); } public Command ExportVaultCommand { get; } public async Task ExportVaultAsync() { bool userConfirmedExport = await _platformUtilsService.ShowDialogAsync(ExportWarningMessage, _i18nService.T("ExportVaultConfirmationTitle"), _i18nService.T("ExportVault"), _i18nService.T("Cancel")); if (!userConfirmedExport) { return; } var verificationType = await _keyConnectorService.GetUsesKeyConnector() ? VerificationType.OTP : VerificationType.MasterPassword; if (!await _userVerificationService.VerifyUser(Secret, verificationType)) { return; } Secret = string.Empty; try { var data = await _exportService.GetExport(FileFormatOptions[FileFormatSelectedIndex].Key); var fileFormat = FileFormatOptions[FileFormatSelectedIndex].Key; fileFormat = fileFormat == "encrypted_json" ? "json" : fileFormat; _defaultFilename = _exportService.GetFileName(null, fileFormat); _exportResult = Encoding.UTF8.GetBytes(data); if (!_deviceActionService.SaveFile(_exportResult, null, _defaultFilename, null)) { ClearResult(); await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure")); } } catch (Exception ex) { ClearResult(); await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure")); System.Diagnostics.Debug.WriteLine(">>> {0}: {1}", ex.GetType(), ex.StackTrace); _logger.Exception(ex); } } public async Task RequestOTP() { try { await _deviceActionService.ShowLoadingAsync(AppResources.Sending); await _apiService.PostAccountRequestOTP(); await _deviceActionService.HideLoadingAsync(); _platformUtilsService.ShowToast("success", null, AppResources.CodeSent); } catch (ApiException e) { await _deviceActionService.HideLoadingAsync(); if (e?.Error != null) { await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(), AppResources.AnErrorHasOccurred); } } } public async void SaveFileSelected(string contentUri, string filename) { if (_deviceActionService.SaveFile(_exportResult, null, filename ?? _defaultFilename, contentUri)) { ClearResult(); _platformUtilsService.ShowToast("success", null, _i18nService.T("ExportVaultSuccess")); return; } ClearResult(); await _platformUtilsService.ShowDialogAsync(_i18nService.T("ExportVaultFailure")); } public void UpdateWarning() { if (!_initialized) { return; } switch (FileFormatOptions[FileFormatSelectedIndex].Key) { case "encrypted_json": ExportWarningMessage = _i18nService.T("EncExportKeyWarning") + "\n\n" + _i18nService.T("EncExportAccountWarning"); break; default: ExportWarningMessage = _i18nService.T("ExportVaultWarning"); break; } } private void ClearResult() { _defaultFilename = null; _exportResult = null; } } }