From a9a33ad71e94a0713b9d8b0576e70f2fcc96f4d6 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Mon, 30 Jul 2018 16:15:36 -0400 Subject: [PATCH] save password history changes --- src/App/Models/Api/LoginType.cs | 2 +- src/App/Models/Api/Request/CipherRequest.cs | 7 ++- .../Api/Request/PasswordHistoryRequest.cs | 6 +++ src/App/Pages/Vault/VaultEditCipherPage.cs | 48 +++++++++++++++---- src/App/Utilities/Helpers.cs | 10 +++- 5 files changed, 62 insertions(+), 11 deletions(-) diff --git a/src/App/Models/Api/LoginType.cs b/src/App/Models/Api/LoginType.cs index 10e3d3158..19b3b778d 100644 --- a/src/App/Models/Api/LoginType.cs +++ b/src/App/Models/Api/LoginType.cs @@ -13,7 +13,7 @@ namespace Bit.App.Models.Api Uris = cipher.Login.Uris?.Select(u => new LoginUriType(u)); Username = cipher.Login.Username?.EncryptedString; Password = cipher.Login.Password?.EncryptedString; - + PasswordRevisionDate = cipher.Login.PasswordRevisionDate; Totp = cipher.Login.Totp?.EncryptedString; } diff --git a/src/App/Models/Api/Request/CipherRequest.cs b/src/App/Models/Api/Request/CipherRequest.cs index 67b3b38e2..2354b3b58 100644 --- a/src/App/Models/Api/Request/CipherRequest.cs +++ b/src/App/Models/Api/Request/CipherRequest.cs @@ -20,6 +20,11 @@ namespace Bit.App.Models.Api Fields = cipher.Fields.Select(f => new FieldType(f)); } + if(cipher.PasswordHistory != null) + { + PasswordHistory = cipher.PasswordHistory.Select(h => new PasswordHistoryRequest(h)); + } + switch(Type) { case CipherType.Login: @@ -46,7 +51,7 @@ namespace Bit.App.Models.Api public string Name { get; set; } public string Notes { get; set; } public IEnumerable Fields { get; set; } - public IEnumerable PasswordHistory { get; set; } + public IEnumerable PasswordHistory { get; set; } public LoginType Login { get; set; } public CardType Card { get; set; } diff --git a/src/App/Models/Api/Request/PasswordHistoryRequest.cs b/src/App/Models/Api/Request/PasswordHistoryRequest.cs index 7fb05faee..1e94ff4e8 100644 --- a/src/App/Models/Api/Request/PasswordHistoryRequest.cs +++ b/src/App/Models/Api/Request/PasswordHistoryRequest.cs @@ -2,6 +2,12 @@ { public class PasswordHistoryRequest { + public PasswordHistoryRequest(PasswordHistory ph) + { + Password = ph.Password?.EncryptedString; + LastUsedDate = ph.LastUsedDate; + } + public string Password { get; set; } public System.DateTime LastUsedDate { get; set; } } diff --git a/src/App/Pages/Vault/VaultEditCipherPage.cs b/src/App/Pages/Vault/VaultEditCipherPage.cs index 67151a3d5..beac296ff 100644 --- a/src/App/Pages/Vault/VaultEditCipherPage.cs +++ b/src/App/Pages/Vault/VaultEditCipherPage.cs @@ -23,6 +23,8 @@ namespace Bit.App.Pages private readonly IDeviceInfoService _deviceInfo; private readonly IGoogleAnalyticsService _googleAnalyticsService; private DateTime? _lastAction; + private string _originalLoginPassword = null; + private List> _originalHiddenFields = new List>(); public VaultEditCipherPage(string cipherId) { @@ -169,7 +171,7 @@ namespace Bit.App.Pages // Types if(Cipher.Type == CipherType.Login) { - LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, + LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, button1: _deviceInfo.HasCamera ? "camera.png" : null); LoginTotpCell.Entry.Text = Cipher.Login?.Totp?.Decrypt(Cipher.OrganizationId); LoginTotpCell.Entry.DisableAutocapitalize = true; @@ -179,7 +181,8 @@ namespace Bit.App.Pages LoginPasswordCell = new FormEntryCell(AppResources.Password, isPassword: true, nextElement: LoginTotpCell.Entry, button1: "eye.png", button2: "refresh_alt.png"); - LoginPasswordCell.Entry.Text = Cipher.Login?.Password?.Decrypt(Cipher.OrganizationId); + LoginPasswordCell.Entry.Text = _originalLoginPassword = + Cipher.Login?.Password?.Decrypt(Cipher.OrganizationId); LoginPasswordCell.Entry.DisableAutocapitalize = true; LoginPasswordCell.Entry.Autocorrect = false; LoginPasswordCell.Entry.FontFamily = @@ -211,7 +214,7 @@ namespace Bit.App.Pages foreach(var uri in Cipher.Login.Uris) { var value = uri.Uri?.Decrypt(Cipher.OrganizationId); - UrisSection.Insert(UrisSection.Count - 1, + UrisSection.Insert(UrisSection.Count - 1, Helpers.MakeUriCell(value, uri.Match, UrisSection, this)); } } @@ -415,6 +418,11 @@ namespace Bit.App.Pages { FieldsSection.Add(cell); } + if(!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(value) && + field.Type == FieldType.Hidden) + { + _originalHiddenFields.Add(new Tuple(label, value)); + } } } AddFieldCell = new ExtendedTextCell @@ -491,7 +499,8 @@ namespace Bit.App.Pages Cipher.Notes = string.IsNullOrWhiteSpace(NotesCell.Editor.Text) ? null : NotesCell.Editor.Text.Encrypt(Cipher.OrganizationId); Cipher.Favorite = FavoriteCell.On; - + + var passwordHistory = Cipher.PasswordHistory?.ToList() ?? new List(); switch(Cipher.Type) { case CipherType.Login: @@ -505,6 +514,18 @@ namespace Bit.App.Pages LoginTotpCell.Entry.Text.Encrypt(Cipher.OrganizationId), }; + if(!string.IsNullOrWhiteSpace(_originalLoginPassword) && + LoginPasswordCell.Entry.Text != _originalLoginPassword) + { + var now = DateTime.UtcNow; + passwordHistory.Insert(0, new PasswordHistory + { + LastUsedDate = now, + Password = _originalLoginPassword.Encrypt(Cipher.OrganizationId), + }); + Cipher.Login.PasswordRevisionDate = now; + } + Helpers.ProcessUrisSectionForSave(UrisSection, Cipher); break; case CipherType.SecureNote: @@ -639,7 +660,18 @@ namespace Bit.App.Pages Cipher.FolderId = null; } - Helpers.ProcessFieldsSectionForSave(FieldsSection, Cipher); + var hiddenFields = Helpers.ProcessFieldsSectionForSave(FieldsSection, Cipher); + var changedFields = _originalHiddenFields.Where(of => + hiddenFields.Any(f => f.Item1 == of.Item1 && f.Item2 != of.Item2)); + foreach(var cf in changedFields) + { + passwordHistory.Insert(0, new PasswordHistory + { + LastUsedDate = DateTime.UtcNow, + Password = (cf.Item1 + ": " + cf.Item2).Encrypt(Cipher.OrganizationId), + }); + } + Cipher.PasswordHistory = (passwordHistory?.Count ?? 0) > 0 ? passwordHistory : null; await _deviceActionService.ShowLoadingAsync(AppResources.Saving); var saveTask = await _cipherService.SaveAsync(Cipher); @@ -714,7 +746,7 @@ namespace Bit.App.Pages CardExpYearCell?.InitEvents(); CardNameCell?.InitEvents(); CardNumberCell?.InitEvents(); - if (CardCodeCell?.Button1 != null) + if(CardCodeCell?.Button1 != null) { CardCodeCell.Button1.Clicked += CardCodeButton_Clicked; } @@ -798,7 +830,7 @@ namespace Bit.App.Pages CardExpYearCell?.Dispose(); CardNameCell?.Dispose(); CardNumberCell?.Dispose(); - if (CardCodeCell?.Button1 != null) + if(CardCodeCell?.Button1 != null) { CardCodeCell.Button1.Clicked -= CardCodeButton_Clicked; } @@ -826,7 +858,7 @@ namespace Bit.App.Pages default: break; } - + Helpers.DisposeSectionEvents(FieldsSection); Helpers.DisposeSectionEvents(UrisSection); } diff --git a/src/App/Utilities/Helpers.cs b/src/App/Utilities/Helpers.cs index f4d28dc3d..5bac1cce3 100644 --- a/src/App/Utilities/Helpers.cs +++ b/src/App/Utilities/Helpers.cs @@ -331,8 +331,9 @@ namespace Bit.App.Utilities return cell; } - public static void ProcessFieldsSectionForSave(TableSection fieldsSection, Cipher cipher) + public static List> ProcessFieldsSectionForSave(TableSection fieldsSection, Cipher cipher) { + var hiddenFieldValues = new List>(); if(fieldsSection != null && fieldsSection.Count > 0) { var fields = new List(); @@ -348,6 +349,12 @@ namespace Bit.App.Utilities entryCell.Entry.Text.Encrypt(cipher.OrganizationId), Type = entryCell.Entry.IsPassword ? FieldType.Hidden : FieldType.Text }); + + if(entryCell.Entry.IsPassword && !string.IsNullOrWhiteSpace(entryCell.Label.Text)) + { + hiddenFieldValues.Add(new Tuple(entryCell.Label.Text, + entryCell.Entry.Text)); + } } else if(cell is FormSwitchCell switchCell) { @@ -368,6 +375,7 @@ namespace Bit.App.Utilities { cipher.Fields = null; } + return hiddenFieldValues; } public static FormEntryCell MakeUriCell(string value, UriMatchType? match, TableSection urisSection, Page page)