diff --git a/src/App/Models/Page/VaultViewCipherPageModel.cs b/src/App/Models/Page/VaultViewCipherPageModel.cs index e1a698f74..90fc1fd19 100644 --- a/src/App/Models/Page/VaultViewCipherPageModel.cs +++ b/src/App/Models/Page/VaultViewCipherPageModel.cs @@ -1,6 +1,5 @@ using System; using System.ComponentModel; -using Bit.App.Resources; using Xamarin.Forms; using System.Collections.Generic; using Bit.App.Enums; @@ -9,127 +8,40 @@ namespace Bit.App.Models.Page { public class VaultViewCipherPageModel : INotifyPropertyChanged { - private string _name; - private string _username; - private string _password; - private string _uri; - private string _notes; - private string _totpCode; - private int _totpSec = 30; - private bool _revealPassword; + private string _name, _notes; private List _attachments; private List _fields; + // Login + private string _loginUsername, _loginPassword, _loginUri, _loginTotpCode; + private int _loginTotpSec = 30; + private bool _loginRevealPassword; + + // Card + private string _cardName, _cardNumber, _cardBrand, _cardExpMonth, _cardExpYear, _cardCode; + + // Identity + private string _idFirstName, _idLastName, _idMiddleName, _idCompany, _idEmail, _idPhone, _idUsername, + _idPassportNumber, _idLicenseNumber, _idSsn, _idAddress1, _idAddress2, _idAddress3, _idCity, + _idState, _idCountry, _idPostalCode, _idTitle; + public VaultViewCipherPageModel() { } public event PropertyChangedEventHandler PropertyChanged; public string Name { - get { return _name; } + get => _name; set { _name = value; PropertyChanged(this, new PropertyChangedEventArgs(nameof(Name))); } } - public string Username - { - get { return _username; } - set - { - _username = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(Username))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUsername))); - } - } - public bool ShowUsername => !string.IsNullOrWhiteSpace(Username); - - public string Password - { - get { return _password; } - set - { - _password = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(Password))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedPassword))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowPassword))); - } - } - public bool ShowPassword => !string.IsNullOrWhiteSpace(Password); - - public string Uri - { - get { return _uri; } - set - { - _uri = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(Uri))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(UriHost))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowUri))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLaunch))); - } - } - public bool ShowUri => !string.IsNullOrWhiteSpace(Uri); - - public bool ShowLaunch - { - get - { - if(!ShowUri) - { - return false; - } - - if(Device.RuntimePlatform == Device.Android && !Uri.StartsWith("http") && - !Uri.StartsWith("androidapp://")) - { - return false; - } - - if(Device.RuntimePlatform != Device.Android && !Uri.StartsWith("http")) - { - return false; - } - - Uri uri; - if(!System.Uri.TryCreate(Uri, UriKind.Absolute, out uri)) - { - return false; - } - - return true; - } - } - - public string UriHost - { - get - { - if(!ShowUri) - { - return null; - } - - Uri uri; - if(!System.Uri.TryCreate(Uri, UriKind.Absolute, out uri)) - { - return Uri; - } - - string domain; - if(DomainName.TryParseBaseDomain(uri.Host, out domain)) - { - return domain; - } - - return uri.Host; - } - } public string Notes { - get { return _notes; } + get => _notes; set { _notes = value; @@ -138,48 +50,10 @@ namespace Bit.App.Models.Page } } public bool ShowNotes => !string.IsNullOrWhiteSpace(Notes); - public bool RevealPassword - { - get { return _revealPassword; } - set - { - _revealPassword = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevealPassword))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedPassword))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowHideImage))); - } - } - public string MaskedPassword => RevealPassword ? Password : Password == null ? null : new string('●', Password.Length); - public ImageSource ShowHideImage => RevealPassword ? ImageSource.FromFile("eye_slash") : ImageSource.FromFile("eye"); - - public string TotpCode - { - get { return _totpCode; } - set - { - _totpCode = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(TotpCode))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(TotpCodeFormatted))); - } - } - public int TotpSecond - { - get { return _totpSec; } - set - { - _totpSec = value; - PropertyChanged(this, new PropertyChangedEventArgs(nameof(TotpSecond))); - PropertyChanged(this, new PropertyChangedEventArgs(nameof(TotpColor))); - } - } - public bool TotpLow => TotpSecond <= 7; - public Color TotpColor => !string.IsNullOrWhiteSpace(TotpCode) && TotpLow ? Color.Red : Color.Black; - public string TotpCodeFormatted => !string.IsNullOrWhiteSpace(TotpCode) ? - string.Format("{0} {1}", TotpCode.Substring(0, 3), TotpCode.Substring(3)) : null; public List Attachments { - get { return _attachments; } + get => _attachments; set { _attachments = value; @@ -191,7 +65,7 @@ namespace Bit.App.Models.Page public List Fields { - get { return _fields; } + get => _fields; set { _fields = value; @@ -201,12 +75,484 @@ namespace Bit.App.Models.Page } public bool ShowFields => (Fields?.Count ?? 0) > 0; + // Login + public string LoginUsername + { + get => _loginUsername; + set + { + _loginUsername = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUsername))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUsername))); + } + } + public bool ShowLoginUsername => !string.IsNullOrWhiteSpace(LoginUsername); + + public string LoginPassword + { + get => _loginPassword; + set + { + _loginPassword = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginPassword))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedLoginPassword))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginPassword))); + } + } + public bool ShowLoginPassword => !string.IsNullOrWhiteSpace(LoginPassword); + public bool RevealLoginPassword + { + get => _loginRevealPassword; + set + { + _loginRevealPassword = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevealLoginPassword))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedLoginPassword))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginShowHideImage))); + } + } + public string MaskedLoginPassword => RevealLoginPassword ? + LoginPassword : LoginPassword == null ? null : new string('●', LoginPassword.Length); + public ImageSource LoginShowHideImage => RevealLoginPassword ? + ImageSource.FromFile("eye_slash.png") : ImageSource.FromFile("eye.png"); + + public string LoginUri + { + get => _loginUri; + set + { + _loginUri = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUri))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginUriHost))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginUri))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowLoginLaunch))); + } + } + public bool ShowLoginUri => !string.IsNullOrWhiteSpace(LoginUri); + public bool ShowLoginLaunch + { + get + { + if(!ShowLoginUri) + { + return false; + } + + if(Device.RuntimePlatform == Device.Android && !LoginUri.StartsWith("http") && + !LoginUri.StartsWith("androidapp://")) + { + return false; + } + + if(Device.RuntimePlatform != Device.Android && !LoginUri.StartsWith("http")) + { + return false; + } + + if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri)) + { + return false; + } + + return true; + } + } + public string LoginUriHost + { + get + { + if(!ShowLoginUri) + { + return null; + } + + if(!Uri.TryCreate(LoginUri, UriKind.Absolute, out Uri uri)) + { + return LoginUri; + } + + if(DomainName.TryParseBaseDomain(uri.Host, out string domain)) + { + return domain; + } + + return uri.Host; + } + } + + public string LoginTotpCode + { + get => _loginTotpCode; + set + { + _loginTotpCode = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginTotpCode))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginTotpCodeFormatted))); + } + } + public int LoginTotpSecond + { + get => _loginTotpSec; + set + { + _loginTotpSec = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginTotpSecond))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(LoginTotpColor))); + } + } + public bool LoginTotpLow => LoginTotpSecond <= 7; + public Color LoginTotpColor => !string.IsNullOrWhiteSpace(LoginTotpCode) && LoginTotpLow ? + Color.Red : Color.Black; + public string LoginTotpCodeFormatted => !string.IsNullOrWhiteSpace(LoginTotpCode) ? + string.Format("{0} {1}", LoginTotpCode.Substring(0, 3), LoginTotpCode.Substring(3)) : null; + + // Card + public string CardName + { + get => _cardName; + set + { + _cardName = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardName))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardName))); + } + } + public bool ShowCardName => !string.IsNullOrWhiteSpace(CardName); + + public string CardNumber + { + get => _cardNumber; + set + { + _cardNumber = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardNumber))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardNumber))); + } + } + public bool ShowCardNumber => !string.IsNullOrWhiteSpace(CardNumber); + + public string CardBrand + { + get => _cardBrand; + set + { + _cardBrand = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardBrand))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardBrand))); + } + } + public bool ShowCardBrand => !string.IsNullOrWhiteSpace(CardBrand); + + public string CardExpMonth + { + private get => _cardExpMonth; + set + { + _cardExpMonth = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardExpMonth))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardExp))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardExp))); + } + } + + public string CardExpYear + { + private get => _cardExpYear; + set + { + _cardExpYear = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardExpYear))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardExp))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardExp))); + } + } + + public string CardExp + { + get + { + var expMonth = !string.IsNullOrWhiteSpace(CardExpMonth) ? CardExpMonth.PadLeft(2, '0') : "__"; + var expYear = "____"; + if(!string.IsNullOrWhiteSpace(CardExpYear)) + { + expYear = CardExpYear; + } + if(expYear.Length == 2) + { + expYear = "20" + expYear; + } + + return $"{expMonth} / {expYear}"; + } + } + public bool ShowCardExp => !string.IsNullOrWhiteSpace(CardExpMonth) && !string.IsNullOrWhiteSpace(CardExpYear); + + public string CardCode + { + get => _cardCode; + set + { + _cardCode = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(CardCode))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowCardCode))); + } + } + public bool ShowCardCode => !string.IsNullOrWhiteSpace(CardCode); + + // Identity + + public string IdTitle + { + get => _idTitle; + set + { + _idTitle = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdTitle))); + } + } + public string IdFirstName + { + private get => _idFirstName; + set + { + _idFirstName = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdFirstName))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdName))); + } + } + public string IdMiddleName + { + private get => _idMiddleName; + set + { + _idMiddleName = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdMiddleName))); + } + } + public string IdLastName + { + private get => _idLastName; + set + { + _idLastName = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdLastName))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdName))); + } + } + public string IdName + { + get + { + var name = IdTitle; + if(!string.IsNullOrWhiteSpace(IdFirstName)) + { + name += ((!string.IsNullOrWhiteSpace(name) ? " " : string.Empty) + IdFirstName); + } + if(!string.IsNullOrWhiteSpace(IdMiddleName)) + { + name += ((!string.IsNullOrWhiteSpace(name) ? " " : string.Empty) + IdMiddleName); + } + if(!string.IsNullOrWhiteSpace(IdLastName)) + { + name += ((!string.IsNullOrWhiteSpace(name) ? " " : string.Empty) + IdLastName); + } + return name; + } + } + public bool ShowIdName => !string.IsNullOrWhiteSpace(IdFirstName) || !string.IsNullOrWhiteSpace(IdLastName); + + public string IdUsername + { + get => _idUsername; + set + { + _idUsername = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdUsername))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdUsername))); + } + } + public bool ShowIdUsername => !string.IsNullOrWhiteSpace(IdUsername); + + public string IdCompany + { + get => _idCompany; + set + { + _idCompany = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdCompany))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdCompany))); + } + } + public bool ShowIdCompany => !string.IsNullOrWhiteSpace(IdCompany); + + public string IdSsn + { + get => _idSsn; + set + { + _idSsn = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdSsn))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdSsn))); + } + } + public bool ShowIdSsn => !string.IsNullOrWhiteSpace(IdSsn); + + public string IdPassportNumber + { + get => _idPassportNumber; + set + { + _idPassportNumber = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdPassportNumber))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdPassportNumber))); + } + } + public bool ShowIdPassportNumber => !string.IsNullOrWhiteSpace(IdPassportNumber); + + public string IdLicenseNumber + { + get => _idLicenseNumber; + set + { + _idLicenseNumber = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdLicenseNumber))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdLicenseNumber))); + } + } + public bool ShowIdLicenseNumber => !string.IsNullOrWhiteSpace(IdLicenseNumber); + + public string IdEmail + { + get => _idEmail; + set + { + _idEmail = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdEmail))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdEmail))); + } + } + public bool ShowIdEmail => !string.IsNullOrWhiteSpace(IdEmail); + + public string IdPhone + { + get => _idPhone; + set + { + _idPhone = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdPhone))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdPhone))); + } + } + public bool ShowIdPhone => !string.IsNullOrWhiteSpace(IdPhone); + + public string IdAddress1 + { + get => _idAddress1; + set + { + _idAddress1 = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress1))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdAddress))); + } + } + public string IdAddress2 + { + get => _idAddress2; + set + { + _idAddress2 = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress2))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + } + } + public string IdAddress3 + { + get => _idAddress3; + set + { + _idAddress3 = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress3))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + } + } + public string IdCity + { + get => _idCity; + set + { + _idCity = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdCity))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdAddress))); + } + } + public string IdState + { + get => _idState; + set + { + _idState = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdState))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + } + } + public string IdPostalCode + { + get => _idPostalCode; + set + { + _idPostalCode = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdPostalCode))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + } + } + public string IdCountry + { + get => _idCountry; + set + { + _idCountry = value; + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdCountry))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(IdAddress))); + PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowIdAddress))); + } + } + public string IdAddress + { + get + { + var address = IdAddress1; + if(!string.IsNullOrWhiteSpace(IdAddress2)) + { + address += ((!string.IsNullOrWhiteSpace(address) ? "\n" : string.Empty) + IdAddress2); + } + if(!string.IsNullOrWhiteSpace(IdAddress3)) + { + address += ((!string.IsNullOrWhiteSpace(address) ? "\n" : string.Empty) + IdAddress3); + } + if(!string.IsNullOrWhiteSpace(IdCity) || !string.IsNullOrWhiteSpace(IdState) || + !string.IsNullOrWhiteSpace(IdPostalCode)) + { + var cityLine = IdCity + ", "; + cityLine += !string.IsNullOrWhiteSpace(IdState) ? IdState : "-"; + cityLine += " "; + cityLine += !string.IsNullOrWhiteSpace(IdPostalCode) ? IdPostalCode : "-"; + address += ((!string.IsNullOrWhiteSpace(address) ? "\n" : string.Empty) + cityLine); + } + if(!string.IsNullOrWhiteSpace(IdCountry)) + { + address += ((!string.IsNullOrWhiteSpace(address) ? "\n" : string.Empty) + IdCountry); + } + return address; + } + } + public bool ShowIdAddress => !string.IsNullOrWhiteSpace(IdAddress1) || !string.IsNullOrWhiteSpace(IdCity) || + !string.IsNullOrWhiteSpace(IdCountry); + public void Update(Cipher cipher) { Name = cipher.Name?.Decrypt(cipher.OrganizationId); - Username = cipher.Login?.Username?.Decrypt(cipher.OrganizationId); - Password = cipher.Login?.Password?.Decrypt(cipher.OrganizationId); - Uri = cipher.Login?.Uri?.Decrypt(cipher.OrganizationId); Notes = cipher.Notes?.Decrypt(cipher.OrganizationId); if(cipher.Attachments != null) @@ -248,6 +594,45 @@ namespace Bit.App.Models.Page { cipher.Fields = null; } + + switch(cipher.Type) + { + case CipherType.Login: + LoginUsername = cipher.Login.Username?.Decrypt(cipher.OrganizationId); + LoginPassword = cipher.Login.Password?.Decrypt(cipher.OrganizationId); + LoginUri = cipher.Login.Uri?.Decrypt(cipher.OrganizationId); + break; + case CipherType.Card: + CardName = cipher.Card.CardholderName?.Decrypt(cipher.OrganizationId); + CardNumber = cipher.Card.Number?.Decrypt(cipher.OrganizationId); + CardBrand = cipher.Card.Brand?.Decrypt(cipher.OrganizationId); + CardExpMonth = cipher.Card.ExpMonth?.Decrypt(cipher.OrganizationId); + CardExpYear = cipher.Card.ExpYear?.Decrypt(cipher.OrganizationId); + CardCode = cipher.Card.Code?.Decrypt(cipher.OrganizationId); + break; + case CipherType.Identity: + IdTitle = cipher.Identity.Title?.Decrypt(cipher.OrganizationId); + IdFirstName = cipher.Identity.FirstName?.Decrypt(cipher.OrganizationId); + IdMiddleName = cipher.Identity.MiddleName?.Decrypt(cipher.OrganizationId); + IdLastName = cipher.Identity.LastName?.Decrypt(cipher.OrganizationId); + IdCompany = cipher.Identity.Company?.Decrypt(cipher.OrganizationId); + IdUsername = cipher.Identity.Username?.Decrypt(cipher.OrganizationId); + IdSsn = cipher.Identity.SSN?.Decrypt(cipher.OrganizationId); + IdPassportNumber = cipher.Identity.PassportNumber?.Decrypt(cipher.OrganizationId); + IdLicenseNumber = cipher.Identity.LicenseNumber?.Decrypt(cipher.OrganizationId); + IdEmail = cipher.Identity.Email?.Decrypt(cipher.OrganizationId); + IdPhone = cipher.Identity.Phone?.Decrypt(cipher.OrganizationId); + IdAddress1 = cipher.Identity.Address1?.Decrypt(cipher.OrganizationId); + IdAddress2 = cipher.Identity.Address2?.Decrypt(cipher.OrganizationId); + IdAddress3 = cipher.Identity.Address3?.Decrypt(cipher.OrganizationId); + IdCity = cipher.Identity.City?.Decrypt(cipher.OrganizationId); + IdState = cipher.Identity.State?.Decrypt(cipher.OrganizationId); + IdPostalCode = cipher.Identity.PostalCode?.Decrypt(cipher.OrganizationId); + IdCountry = cipher.Identity.Country?.Decrypt(cipher.OrganizationId); + break; + default: + break; + } } public class Attachment diff --git a/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs b/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs index 18e67670a..c8dd38af6 100644 --- a/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs +++ b/src/App/Pages/Vault/VaultAutofillListCiphersPage.cs @@ -267,7 +267,7 @@ namespace Bit.App.Pages if(selection == AppResources.View) { - var page = new VaultViewCipherPage(cipher.Id); + var page = new VaultViewCipherPage(cipher.Type, cipher.Id); await Navigation.PushForDeviceAsync(page); } else if(selection == AppResources.Edit) diff --git a/src/App/Pages/Vault/VaultListCiphersPage.cs b/src/App/Pages/Vault/VaultListCiphersPage.cs index ec4850115..687bd7e90 100644 --- a/src/App/Pages/Vault/VaultListCiphersPage.cs +++ b/src/App/Pages/Vault/VaultListCiphersPage.cs @@ -398,7 +398,7 @@ namespace Bit.App.Pages if(selection == AppResources.View || string.IsNullOrWhiteSpace(Uri)) { - var page = new VaultViewCipherPage(cipher.Id); + var page = new VaultViewCipherPage(cipher.Type, cipher.Id); await Navigation.PushForDeviceAsync(page); } else if(selection == AppResources.Autofill) @@ -453,7 +453,7 @@ namespace Bit.App.Pages if(selection == AppResources.View) { - var page = new VaultViewCipherPage(cipher.Id); + var page = new VaultViewCipherPage(cipher.Type, cipher.Id); await Navigation.PushForDeviceAsync(page); } else if(selection == AppResources.Edit) diff --git a/src/App/Pages/Vault/VaultViewCipherPage.cs b/src/App/Pages/Vault/VaultViewCipherPage.cs index 06b24e426..e3257ef1e 100644 --- a/src/App/Pages/Vault/VaultViewCipherPage.cs +++ b/src/App/Pages/Vault/VaultViewCipherPage.cs @@ -17,6 +17,7 @@ namespace Bit.App.Pages { public class VaultViewCipherPage : ExtendedContentPage { + private readonly CipherType _type; private readonly string _cipherId; private readonly ICipherService _cipherService; private readonly IUserDialogs _userDialogs; @@ -24,8 +25,9 @@ namespace Bit.App.Pages private readonly ITokenService _tokenService; private bool _pageDisappeared = true; - public VaultViewCipherPage(string cipherId) + public VaultViewCipherPage(CipherType type, string cipherId) { + _type = type; _cipherId = cipherId; _cipherService = Resolver.Resolve(); _userDialogs = Resolver.Resolve(); @@ -41,15 +43,35 @@ namespace Bit.App.Pages private TableSection NotesSection { get; set; } private TableSection AttachmentsSection { get; set; } private TableSection FieldsSection { get; set; } - public LabeledValueCell UsernameCell { get; set; } - public LabeledValueCell PasswordCell { get; set; } - public LabeledValueCell UriCell { get; set; } public LabeledValueCell NotesCell { get; set; } - public LabeledValueCell TotpCodeCell { get; set; } private EditCipherToolBarItem EditItem { get; set; } public List FieldsCells { get; set; } public List AttachmentCells { get; set; } + // Login + public LabeledValueCell LoginUsernameCell { get; set; } + public LabeledValueCell LoginPasswordCell { get; set; } + public LabeledValueCell LoginUriCell { get; set; } + public LabeledValueCell LoginTotpCodeCell { get; set; } + + // Card + public LabeledValueCell CardNameCell { get; set; } + public LabeledValueCell CardNumberCell { get; set; } + public LabeledValueCell CardBrandCell { get; set; } + public LabeledValueCell CardExpCell { get; set; } + public LabeledValueCell CardCodeCell { get; set; } + + // Card + public LabeledValueCell IdNameCell { get; set; } + public LabeledValueCell IdUsernameCell { get; set; } + public LabeledValueCell IdCompanyCell { get; set; } + public LabeledValueCell IdSsnCell { get; set; } + public LabeledValueCell IdPassportNumberCell { get; set; } + public LabeledValueCell IdLicenseNumberCell { get; set; } + public LabeledValueCell IdEmailCell { get; set; } + public LabeledValueCell IdPhoneCell { get; set; } + public LabeledValueCell IdAddressCell { get; set; } + private void Init() { EditItem = new EditCipherToolBarItem(this, _cipherId); @@ -59,60 +81,151 @@ namespace Bit.App.Pages ToolbarItems.Add(new DismissModalToolBarItem(this)); } + InitProps(); + Title = AppResources.ViewItem; + Content = Table; + BindingContext = Model; + } + + public void InitProps() + { // Name var nameCell = new LabeledValueCell(AppResources.Name); nameCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.Name)); - // Username - UsernameCell = new LabeledValueCell(AppResources.Username, button1Image: "clipboard.png"); - UsernameCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.Username)); - UsernameCell.Button1.Command = new Command(() => Copy(Model.Username, AppResources.Username)); - UsernameCell.Value.LineBreakMode = LineBreakMode.WordWrap; - - // Password - PasswordCell = new LabeledValueCell(AppResources.Password, button1Image: string.Empty, - button2Image: "clipboard.png"); - PasswordCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.MaskedPassword)); - PasswordCell.Button1.SetBinding(Button.ImageProperty, nameof(VaultViewCipherPageModel.ShowHideImage)); - if(Device.RuntimePlatform == Device.iOS) - { - PasswordCell.Button1.Margin = new Thickness(10, 0); - } - PasswordCell.Button1.Command = new Command(() => Model.RevealPassword = !Model.RevealPassword); - PasswordCell.Button2.Command = new Command(() => Copy(Model.Password, AppResources.Password)); - PasswordCell.Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", WinPhone: "Courier"); - PasswordCell.Value.LineBreakMode = LineBreakMode.WordWrap; - - // URI - UriCell = new LabeledValueCell(AppResources.Website, button1Image: "launch.png"); - UriCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.UriHost)); - UriCell.Button1.SetBinding(IsVisibleProperty, nameof(VaultViewCipherPageModel.ShowLaunch)); - UriCell.Button1.Command = new Command(() => - { - if(Device.RuntimePlatform == Device.Android && Model.Uri.StartsWith("androidapp://")) - { - MessagingCenter.Send(Application.Current, "LaunchApp", Model.Uri); - } - else if(Model.Uri.StartsWith("http://") || Model.Uri.StartsWith("https://")) - { - Device.OpenUri(new Uri(Model.Uri)); - } - }); - - // Totp - TotpCodeCell = new LabeledValueCell(AppResources.VerificationCodeTotp, button1Image: "clipboard.png", subText: "--"); - TotpCodeCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.TotpCodeFormatted)); - TotpCodeCell.Value.SetBinding(Label.TextColorProperty, nameof(VaultViewCipherPageModel.TotpColor)); - TotpCodeCell.Button1.Command = new Command(() => Copy(Model.TotpCode, AppResources.VerificationCodeTotp)); - TotpCodeCell.Sub.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.TotpSecond)); - TotpCodeCell.Sub.SetBinding(Label.TextColorProperty, nameof(VaultViewCipherPageModel.TotpColor)); - TotpCodeCell.Value.FontFamily = Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", WinPhone: "Courier"); - // Notes NotesCell = new LabeledValueCell(); NotesCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.Notes)); NotesCell.Value.LineBreakMode = LineBreakMode.WordWrap; + switch(_type) + { + case CipherType.Login: + // Username + LoginUsernameCell = new LabeledValueCell(AppResources.Username, button1Image: "clipboard.png"); + LoginUsernameCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.LoginUsername)); + LoginUsernameCell.Button1.Command = + new Command(() => Copy(Model.LoginUsername, AppResources.Username)); + LoginUsernameCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + // Password + LoginPasswordCell = new LabeledValueCell(AppResources.Password, button1Image: string.Empty, + button2Image: "clipboard.png"); + LoginPasswordCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.MaskedLoginPassword)); + LoginPasswordCell.Button1.SetBinding(Button.ImageProperty, + nameof(VaultViewCipherPageModel.LoginShowHideImage)); + if(Device.RuntimePlatform == Device.iOS) + { + LoginPasswordCell.Button1.Margin = new Thickness(10, 0); + } + LoginPasswordCell.Button1.Command = + new Command(() => Model.RevealLoginPassword = !Model.RevealLoginPassword); + LoginPasswordCell.Button2.Command = + new Command(() => Copy(Model.LoginPassword, AppResources.Password)); + LoginPasswordCell.Value.FontFamily = + Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", WinPhone: "Courier"); + LoginPasswordCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + // URI + LoginUriCell = new LabeledValueCell(AppResources.Website, button1Image: "launch.png"); + LoginUriCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.LoginUriHost)); + LoginUriCell.Button1.SetBinding(IsVisibleProperty, nameof(VaultViewCipherPageModel.ShowLoginLaunch)); + LoginUriCell.Button1.Command = new Command(() => + { + if(Device.RuntimePlatform == Device.Android && Model.LoginUri.StartsWith("androidapp://")) + { + MessagingCenter.Send(Application.Current, "LaunchApp", Model.LoginUri); + } + else if(Model.LoginUri.StartsWith("http://") || Model.LoginUri.StartsWith("https://")) + { + Device.OpenUri(new Uri(Model.LoginUri)); + } + }); + + // Totp + LoginTotpCodeCell = new LabeledValueCell( + AppResources.VerificationCodeTotp, button1Image: "clipboard.png", subText: "--"); + LoginTotpCodeCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.LoginTotpCodeFormatted)); + LoginTotpCodeCell.Value.SetBinding(Label.TextColorProperty, + nameof(VaultViewCipherPageModel.LoginTotpColor)); + LoginTotpCodeCell.Button1.Command = + new Command(() => Copy(Model.LoginTotpCode, AppResources.VerificationCodeTotp)); + LoginTotpCodeCell.Sub.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.LoginTotpSecond)); + LoginTotpCodeCell.Sub.SetBinding(Label.TextColorProperty, + nameof(VaultViewCipherPageModel.LoginTotpColor)); + LoginTotpCodeCell.Value.FontFamily = + Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", WinPhone: "Courier"); + break; + case CipherType.Card: + CardNameCell = new LabeledValueCell(AppResources.CardholderName); + CardNameCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardName)); + + CardNumberCell = new LabeledValueCell(AppResources.Number, button1Image: "clipboard.png"); + CardNumberCell.Button1.Command = new Command(() => Copy(Model.CardNumber, AppResources.Number)); + CardNumberCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardNumber)); + CardNumberCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + CardBrandCell = new LabeledValueCell(AppResources.Brand); + CardBrandCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardBrand)); + + CardExpCell = new LabeledValueCell(AppResources.Expiration); + CardExpCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardExp)); + + CardCodeCell = new LabeledValueCell(AppResources.SecurityCode, button1Image: "clipboard.png"); + CardCodeCell.Button1.Command = new Command(() => Copy(Model.CardCode, AppResources.SecurityCode)); + CardCodeCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.CardCode)); + break; + case CipherType.Identity: + IdNameCell = new LabeledValueCell(AppResources.Name); + IdNameCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdName)); + IdNameCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + IdUsernameCell = new LabeledValueCell(AppResources.Username, button1Image: "clipboard.png"); + IdUsernameCell.Button1.Command = new Command(() => Copy(Model.IdUsername, AppResources.Username)); + IdUsernameCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdUsername)); + IdUsernameCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + IdCompanyCell = new LabeledValueCell(AppResources.Company); + IdCompanyCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdCompany)); + + IdSsnCell = new LabeledValueCell(AppResources.SSN); + IdSsnCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdSsn)); + + IdPassportNumberCell = new LabeledValueCell(AppResources.PassportNumber, + button1Image: "clipboard.png"); + IdPassportNumberCell.Button1.Command = + new Command(() => Copy(Model.IdPassportNumber, AppResources.PassportNumber)); + IdPassportNumberCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.IdPassportNumber)); + IdPassportNumberCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + IdLicenseNumberCell = new LabeledValueCell(AppResources.LicenseNumber, + button1Image: "clipboard.png"); + IdLicenseNumberCell.Button1.Command = + new Command(() => Copy(Model.IdLicenseNumber, AppResources.LicenseNumber)); + IdLicenseNumberCell.Value.SetBinding(Label.TextProperty, + nameof(VaultViewCipherPageModel.IdLicenseNumber)); + IdLicenseNumberCell.Value.LineBreakMode = LineBreakMode.WordWrap; + + IdEmailCell = new LabeledValueCell(AppResources.Email); + IdEmailCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdEmail)); + + IdPhoneCell = new LabeledValueCell(AppResources.Phone); + IdPhoneCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdPhone)); + + IdAddressCell = new LabeledValueCell(AppResources.Address, button1Image: "clipboard.png"); + IdAddressCell.Button1.Command = new Command(() => Copy(Model.IdAddress, AppResources.Address)); + IdAddressCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.IdAddress)); + IdAddressCell.Value.LineBreakMode = LineBreakMode.WordWrap; + break; + default: + break; + } + ItemInformationSection = new TableSection(AppResources.ItemInformation) { nameCell @@ -140,10 +253,6 @@ namespace Bit.App.Pages Table.RowHeight = -1; Table.EstimatedRowHeight = 70; } - - Title = AppResources.ViewItem; - Content = Table; - BindingContext = Model; } protected async override void OnAppearing() @@ -160,34 +269,20 @@ namespace Bit.App.Pages } Model.Update(cipher); + BuildTable(cipher); + base.OnAppearing(); + } - if(ItemInformationSection.Contains(UriCell)) - { - ItemInformationSection.Remove(UriCell); - } - if(Model.ShowUri) - { - ItemInformationSection.Add(UriCell); - } - - if(ItemInformationSection.Contains(UsernameCell)) - { - ItemInformationSection.Remove(UsernameCell); - } - if(Model.ShowUsername) - { - ItemInformationSection.Add(UsernameCell); - } - - if(ItemInformationSection.Contains(PasswordCell)) - { - ItemInformationSection.Remove(PasswordCell); - } - if(Model.ShowPassword) - { - ItemInformationSection.Add(PasswordCell); - } + protected override void OnDisappearing() + { + _pageDisappeared = true; + NotesCell.Tapped -= NotesCell_Tapped; + EditItem.Dispose(); + CleanupAttachmentCells(); + } + private void BuildTable(Cipher cipher) + { if(Table.Root.Contains(NotesSection)) { Table.Root.Remove(NotesSection); @@ -197,36 +292,7 @@ namespace Bit.App.Pages Table.Root.Add(NotesSection); } - // Totp - if(ItemInformationSection.Contains(TotpCodeCell)) - { - ItemInformationSection.Remove(TotpCodeCell); - } - if(cipher.Login?.Totp != null && (_tokenService.TokenPremium || cipher.OrganizationUseTotp)) - { - var totpKey = cipher.Login?.Totp.Decrypt(cipher.OrganizationId); - if(!string.IsNullOrWhiteSpace(totpKey)) - { - Model.TotpCode = Crypto.Totp(totpKey); - if(!string.IsNullOrWhiteSpace(Model.TotpCode)) - { - TotpTick(totpKey); - Device.StartTimer(new TimeSpan(0, 0, 1), () => - { - if(_pageDisappeared) - { - return false; - } - - TotpTick(totpKey); - return true; - }); - - ItemInformationSection.Add(TotpCodeCell); - } - } - } - + // Attachments CleanupAttachmentCells(); if(Table.Root.Contains(AttachmentsSection)) { @@ -249,6 +315,7 @@ namespace Bit.App.Pages Table.Root.Add(AttachmentsSection); } + // Fields if(Table.Root.Contains(FieldsSection)) { Table.Root.Remove(FieldsSection); @@ -278,15 +345,76 @@ namespace Bit.App.Pages Table.Root.Add(FieldsSection); } - base.OnAppearing(); + // Various types + switch(cipher.Type) + { + case CipherType.Login: + AddSectionCell(LoginUriCell, Model.ShowLoginUri); + AddSectionCell(LoginUsernameCell, Model.ShowLoginUsername); + AddSectionCell(LoginPasswordCell, Model.ShowLoginPassword); + + if(ItemInformationSection.Contains(LoginTotpCodeCell)) + { + ItemInformationSection.Remove(LoginTotpCodeCell); + } + if(cipher.Login?.Totp != null && (_tokenService.TokenPremium || cipher.OrganizationUseTotp)) + { + var totpKey = cipher.Login?.Totp.Decrypt(cipher.OrganizationId); + if(!string.IsNullOrWhiteSpace(totpKey)) + { + Model.LoginTotpCode = Crypto.Totp(totpKey); + if(!string.IsNullOrWhiteSpace(Model.LoginTotpCode)) + { + TotpTick(totpKey); + Device.StartTimer(new TimeSpan(0, 0, 1), () => + { + if(_pageDisappeared) + { + return false; + } + + TotpTick(totpKey); + return true; + }); + + ItemInformationSection.Add(LoginTotpCodeCell); + } + } + } + break; + case CipherType.Card: + AddSectionCell(CardNameCell, Model.ShowCardName); + AddSectionCell(CardNumberCell, Model.ShowCardNumber); + AddSectionCell(CardBrandCell, Model.ShowCardBrand); + AddSectionCell(CardExpCell, Model.ShowCardExp); + AddSectionCell(CardCodeCell, Model.ShowCardCode); + break; + case CipherType.Identity: + AddSectionCell(IdNameCell, Model.ShowIdName); + AddSectionCell(IdUsernameCell, Model.ShowIdUsername); + AddSectionCell(IdCompanyCell, Model.ShowIdCompany); + AddSectionCell(IdSsnCell, Model.ShowIdSsn); + AddSectionCell(IdPassportNumberCell, Model.ShowIdPassportNumber); + AddSectionCell(IdLicenseNumberCell, Model.ShowIdLicenseNumber); + AddSectionCell(IdEmailCell, Model.ShowIdEmail); + AddSectionCell(IdPhoneCell, Model.ShowIdPhone); + AddSectionCell(IdAddressCell, Model.ShowIdAddress); + break; + default: + break; + } } - protected override void OnDisappearing() + private void AddSectionCell(LabeledValueCell cell, bool show) { - _pageDisappeared = true; - NotesCell.Tapped -= NotesCell_Tapped; - EditItem.Dispose(); - CleanupAttachmentCells(); + if(ItemInformationSection.Contains(cell)) + { + ItemInformationSection.Remove(cell); + } + if(show) + { + ItemInformationSection.Add(cell); + } } private void CleanupAttachmentCells() @@ -353,11 +481,11 @@ namespace Bit.App.Pages { var now = Helpers.EpocUtcNow() / 1000; var mod = now % 30; - Model.TotpSecond = (int)(30 - mod); + Model.LoginTotpSecond = (int)(30 - mod); if(mod == 0) { - Model.TotpCode = Crypto.Totp(totpKey); + Model.LoginTotpCode = Crypto.Totp(totpKey); } } @@ -434,7 +562,7 @@ namespace Bit.App.Pages { Button1.Margin = new Thickness(10, 0); } - + Button1.Image = "eye"; Button1.Command = new Command(() => { diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index a1a143bac..fd84fbd62 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -133,6 +133,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Address. + /// + public static string Address { + get { + return ResourceManager.GetString("Address", resourceCulture); + } + } + /// /// Looks up a localized string similar to Address 1. /// @@ -1078,6 +1087,15 @@ namespace Bit.App.Resources { } } + /// + /// Looks up a localized string similar to Expiration. + /// + public static string Expiration { + get { + return ResourceManager.GetString("Expiration", resourceCulture); + } + } + /// /// Looks up a localized string similar to Expiration Month. /// diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index 3d6b01a9c..a91275dcc 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -1170,4 +1170,10 @@ Zip / Postal Code + + Address + + + Expiration + \ No newline at end of file