bitwarden-android/src/App/Pages/Vault/ViewPageViewModel.cs

365 lines
13 KiB
C#
Raw Normal View History

2019-04-24 18:23:03 +03:00
using Bit.App.Abstractions;
2019-04-27 04:53:39 +03:00
using Bit.App.Models;
2019-04-24 18:23:03 +03:00
using Bit.App.Resources;
2019-04-27 06:58:15 +03:00
using Bit.App.Utilities;
2019-04-24 18:23:03 +03:00
using Bit.Core.Abstractions;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
2019-04-26 07:26:09 +03:00
using System;
2019-04-27 04:53:39 +03:00
using System.Collections.Generic;
using System.Linq;
2019-04-24 18:23:03 +03:00
using System.Threading.Tasks;
2019-04-26 07:26:09 +03:00
using Xamarin.Forms;
2019-04-24 18:23:03 +03:00
namespace Bit.App.Pages
{
public class ViewPageViewModel : BaseViewModel
{
private readonly IDeviceActionService _deviceActionService;
private readonly ICipherService _cipherService;
private readonly IUserService _userService;
2019-04-26 07:26:09 +03:00
private readonly ITotpService _totpService;
private readonly IPlatformUtilsService _platformUtilsService;
2019-04-27 07:19:44 +03:00
private readonly IAuditService _auditService;
2019-04-24 18:23:03 +03:00
private CipherView _cipher;
2019-04-29 20:56:36 +03:00
private List<ViewPageFieldViewModel> _fields;
2019-04-24 18:23:03 +03:00
private bool _canAccessPremium;
2019-04-27 06:37:21 +03:00
private bool _showPassword;
private bool _showCardCode;
2019-04-26 07:26:09 +03:00
private string _totpCode;
private string _totpCodeFormatted;
private string _totpSec;
private bool _totpLow;
private DateTime? _totpInterval = null;
2019-04-24 18:23:03 +03:00
public ViewPageViewModel()
{
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
_cipherService = ServiceContainer.Resolve<ICipherService>("cipherService");
_userService = ServiceContainer.Resolve<IUserService>("userService");
2019-04-26 07:26:09 +03:00
_totpService = ServiceContainer.Resolve<ITotpService>("totpService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
2019-04-27 07:19:44 +03:00
_auditService = ServiceContainer.Resolve<IAuditService>("auditService");
2019-04-26 23:58:20 +03:00
CopyCommand = new Command<string>((id) => CopyAsync(id, null));
2019-04-27 07:19:44 +03:00
CopyUriCommand = new Command<LoginUriView>(CopyUri);
2019-04-29 20:51:05 +03:00
CopyFieldCommand = new Command<FieldView>(CopyField);
2019-04-27 07:19:44 +03:00
LaunchUriCommand = new Command<LoginUriView>(LaunchUri);
2019-04-27 06:37:21 +03:00
TogglePasswordCommand = new Command(TogglePassword);
ToggleCardCodeCommand = new Command(ToggleCardCode);
2019-04-27 07:19:44 +03:00
CheckPasswordCommand = new Command(CheckPasswordAsync);
2019-04-24 18:23:03 +03:00
PageTitle = AppResources.ViewItem;
}
2019-04-26 07:26:09 +03:00
public Command CopyCommand { get; set; }
2019-04-26 23:58:20 +03:00
public Command CopyUriCommand { get; set; }
2019-04-29 20:51:05 +03:00
public Command CopyFieldCommand { get; set; }
2019-04-26 23:58:20 +03:00
public Command LaunchUriCommand { get; set; }
2019-04-27 06:37:21 +03:00
public Command TogglePasswordCommand { get; set; }
public Command ToggleCardCodeCommand { get; set; }
2019-04-27 07:19:44 +03:00
public Command CheckPasswordCommand { get; set; }
2019-04-26 07:26:09 +03:00
public string CipherId { get; set; }
2019-04-24 18:23:03 +03:00
public CipherView Cipher
{
get => _cipher;
2019-04-26 07:26:09 +03:00
set => SetProperty(ref _cipher, value,
additionalPropertyNames: new string[]
{
nameof(IsLogin),
nameof(IsIdentity),
nameof(IsCard),
2019-04-26 23:58:20 +03:00
nameof(IsSecureNote),
nameof(ShowUris),
nameof(ShowFields),
2019-04-27 06:37:21 +03:00
nameof(ShowTotp),
2019-04-27 06:58:15 +03:00
nameof(ColoredPassword),
2019-04-29 17:20:29 +03:00
nameof(ShowIdentityAddress),
2019-04-26 07:26:09 +03:00
});
2019-04-24 18:23:03 +03:00
}
2019-04-29 20:56:36 +03:00
public List<ViewPageFieldViewModel> Fields
2019-04-27 04:53:39 +03:00
{
get => _fields;
set => SetProperty(ref _fields, value);
}
2019-04-24 18:23:03 +03:00
public bool CanAccessPremium
{
get => _canAccessPremium;
set => SetProperty(ref _canAccessPremium, value);
}
2019-04-27 06:37:21 +03:00
public bool ShowPassword
{
get => _showPassword;
set => SetProperty(ref _showPassword, value,
additionalPropertyNames: new string[]
{
nameof(ShowPasswordIcon)
});
}
public bool ShowCardCode
{
get => _showCardCode;
set => SetProperty(ref _showCardCode, value,
additionalPropertyNames: new string[]
{
nameof(ShowCardCodeIcon)
});
}
2019-04-26 07:26:09 +03:00
public bool IsLogin => _cipher?.Type == Core.Enums.CipherType.Login;
public bool IsIdentity => _cipher?.Type == Core.Enums.CipherType.Identity;
public bool IsCard => _cipher?.Type == Core.Enums.CipherType.Card;
public bool IsSecureNote => _cipher?.Type == Core.Enums.CipherType.SecureNote;
2019-04-27 06:58:15 +03:00
public FormattedString ColoredPassword => PasswordFormatter.FormatPassword(_cipher.Login.Password);
2019-04-26 23:58:20 +03:00
public bool ShowUris => IsLogin && _cipher.Login.HasUris;
2019-04-29 17:20:29 +03:00
public bool ShowIdentityAddress => IsIdentity && (
!string.IsNullOrWhiteSpace(_cipher.Identity.Address1) ||
!string.IsNullOrWhiteSpace(_cipher.Identity.City) ||
!string.IsNullOrWhiteSpace(_cipher.Identity.Country));
2019-04-26 23:58:20 +03:00
public bool ShowFields => _cipher.HasFields;
2019-04-27 06:37:21 +03:00
public bool ShowTotp => IsLogin && !string.IsNullOrWhiteSpace(_cipher.Login.Totp) &&
2019-04-26 23:58:20 +03:00
!string.IsNullOrWhiteSpace(TotpCodeFormatted);
2019-04-27 06:37:21 +03:00
public string ShowPasswordIcon => _showPassword ? "" : "";
public string ShowCardCodeIcon => _showCardCode ? "" : "";
2019-04-26 07:26:09 +03:00
public string TotpCodeFormatted
{
get => _totpCodeFormatted;
2019-04-26 23:58:20 +03:00
set => SetProperty(ref _totpCodeFormatted, value,
additionalPropertyNames: new string[]
{
nameof(ShowTotp)
});
2019-04-26 07:26:09 +03:00
}
public string TotpSec
{
get => _totpSec;
set => SetProperty(ref _totpSec, value);
}
public bool TotpLow
{
get => _totpLow;
set
{
SetProperty(ref _totpLow, value);
Page.Resources["textTotp"] = Application.Current.Resources[value ? "text-danger" : "text-default"];
}
2019-04-26 07:26:09 +03:00
}
2019-04-24 18:23:03 +03:00
public async Task LoadAsync()
{
2019-04-26 07:26:09 +03:00
CleanUp();
2019-04-24 18:23:03 +03:00
var cipher = await _cipherService.GetAsync(CipherId);
Cipher = await cipher.DecryptAsync();
CanAccessPremium = await _userService.CanAccessPremiumAsync();
2019-04-29 20:56:36 +03:00
Fields = Cipher.Fields?.Select(f => new ViewPageFieldViewModel(f)).ToList();
2019-04-24 18:23:03 +03:00
2019-04-26 07:26:09 +03:00
if(Cipher.Type == Core.Enums.CipherType.Login && !string.IsNullOrWhiteSpace(Cipher.Login.Totp) &&
(Cipher.OrganizationUseTotp || CanAccessPremium))
{
await TotpUpdateCodeAsync();
var interval = _totpService.GetTimeInterval(Cipher.Login.Totp);
await TotpTickAsync(interval);
_totpInterval = DateTime.UtcNow;
Device.StartTimer(new TimeSpan(0, 0, 1), () =>
{
if(_totpInterval == null)
{
return false;
}
var task = TotpTickAsync(interval);
return true;
});
}
}
public void CleanUp()
{
_totpInterval = null;
}
2019-04-27 06:37:21 +03:00
public void TogglePassword()
{
ShowPassword = !ShowPassword;
}
public void ToggleCardCode()
{
ShowCardCode = !ShowCardCode;
}
2019-04-26 07:26:09 +03:00
private async Task TotpUpdateCodeAsync()
{
if(Cipher == null || Cipher.Type != Core.Enums.CipherType.Login || Cipher.Login.Totp == null)
{
_totpInterval = null;
return;
}
_totpCode = await _totpService.GetCodeAsync(Cipher.Login.Totp);
if(_totpCode != null)
{
if(_totpCode.Length > 4)
{
var half = (int)Math.Floor(_totpCode.Length / 2M);
TotpCodeFormatted = string.Format("{0} {1}", _totpCode.Substring(0, half),
_totpCode.Substring(half));
}
else
{
TotpCodeFormatted = _totpCode;
}
}
else
{
TotpCodeFormatted = null;
_totpInterval = null;
}
}
private async Task TotpTickAsync(int intervalSeconds)
{
var epoc = CoreHelpers.EpocUtcNow() / 1000;
var mod = epoc % intervalSeconds;
var totpSec = intervalSeconds - mod;
TotpSec = totpSec.ToString();
TotpLow = totpSec < 7;
if(mod == 0)
{
await TotpUpdateCodeAsync();
}
}
2019-04-27 07:19:44 +03:00
private async void CheckPasswordAsync()
{
if(string.IsNullOrWhiteSpace(Cipher.Login?.Password))
{
return;
}
await _deviceActionService.ShowLoadingAsync(AppResources.CheckingPassword);
var matches = await _auditService.PasswordLeakedAsync(Cipher.Login.Password);
await _deviceActionService.HideLoadingAsync();
if(matches > 0)
{
await _platformUtilsService.ShowDialogAsync(string.Format(AppResources.PasswordExposed, matches));
}
else
{
await _platformUtilsService.ShowDialogAsync(AppResources.PasswordSafe);
}
}
2019-04-26 23:58:20 +03:00
private async void CopyAsync(string id, string text = null)
2019-04-26 07:26:09 +03:00
{
string name = null;
if(id == "LoginUsername")
{
text = Cipher.Login.Username;
name = AppResources.Username;
}
else if(id == "LoginPassword")
{
text = Cipher.Login.Password;
name = AppResources.Password;
}
else if(id == "LoginTotp")
{
text = _totpCode;
name = AppResources.VerificationCodeTotp;
}
2019-04-26 23:58:20 +03:00
else if(id == "LoginUri")
{
name = AppResources.URI;
}
2019-04-29 20:51:05 +03:00
else if(id == "FieldValue")
{
name = AppResources.Value;
}
2019-04-29 17:20:29 +03:00
else if(id == "CardNumber")
{
text = Cipher.Card.Number;
name = AppResources.Number;
}
else if(id == "CardCode")
{
text = Cipher.Card.Code;
name = AppResources.SecurityCode;
}
2019-04-26 07:26:09 +03:00
if(text != null)
{
await _platformUtilsService.CopyToClipboardAsync(text);
if(!string.IsNullOrWhiteSpace(name))
{
_platformUtilsService.ShowToast("info", null, string.Format(AppResources.ValueHasBeenCopied, name));
}
}
2019-04-24 18:23:03 +03:00
}
2019-04-26 23:58:20 +03:00
2019-04-27 07:19:44 +03:00
private void CopyUri(LoginUriView uri)
2019-04-26 23:58:20 +03:00
{
CopyAsync("LoginUri", uri.Uri);
}
2019-04-29 20:51:05 +03:00
private void CopyField(FieldView field)
{
CopyAsync("FieldValue", field.Value);
}
2019-04-27 07:19:44 +03:00
private void LaunchUri(LoginUriView uri)
2019-04-26 23:58:20 +03:00
{
if(uri.CanLaunch)
{
_platformUtilsService.LaunchUri(uri.LaunchUri);
}
}
2019-04-24 18:23:03 +03:00
}
2019-04-29 20:56:36 +03:00
public class ViewPageFieldViewModel : BaseViewModel
{
private FieldView _field;
private bool _showHiddenValue;
public ViewPageFieldViewModel(FieldView field)
{
Field = field;
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
}
public FieldView Field
{
get => _field;
set => SetProperty(ref _field, value,
additionalPropertyNames: new string[]
{
nameof(ValueText),
nameof(IsBooleanType),
nameof(IsHiddenType),
nameof(IsTextType),
nameof(ShowCopyButton),
});
}
public bool ShowHiddenValue
{
get => _showHiddenValue;
set => SetProperty(ref _showHiddenValue, value,
additionalPropertyNames: new string[]
{
nameof(ShowHiddenValueIcon)
});
}
public Command ToggleHiddenValueCommand { get; set; }
public string ValueText => IsBooleanType ? (_field.Value == "true" ? "" : "") : _field.Value;
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
public bool IsTextType => _field.Type == Core.Enums.FieldType.Text;
public bool IsBooleanType => _field.Type == Core.Enums.FieldType.Boolean;
public bool IsHiddenType => _field.Type == Core.Enums.FieldType.Hidden;
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
!string.IsNullOrWhiteSpace(_field.Value);
public void ToggleHiddenValue()
{
ShowHiddenValue = !ShowHiddenValue;
}
}
2019-04-24 18:23:03 +03:00
}