save password history changes

This commit is contained in:
Kyle Spearrin 2018-07-30 16:15:36 -04:00
parent 4d08ce90cc
commit a9a33ad71e
5 changed files with 62 additions and 11 deletions

View file

@ -13,7 +13,7 @@ namespace Bit.App.Models.Api
Uris = cipher.Login.Uris?.Select(u => new LoginUriType(u)); Uris = cipher.Login.Uris?.Select(u => new LoginUriType(u));
Username = cipher.Login.Username?.EncryptedString; Username = cipher.Login.Username?.EncryptedString;
Password = cipher.Login.Password?.EncryptedString; Password = cipher.Login.Password?.EncryptedString;
PasswordRevisionDate = cipher.Login.PasswordRevisionDate;
Totp = cipher.Login.Totp?.EncryptedString; Totp = cipher.Login.Totp?.EncryptedString;
} }

View file

@ -20,6 +20,11 @@ namespace Bit.App.Models.Api
Fields = cipher.Fields.Select(f => new FieldType(f)); Fields = cipher.Fields.Select(f => new FieldType(f));
} }
if(cipher.PasswordHistory != null)
{
PasswordHistory = cipher.PasswordHistory.Select(h => new PasswordHistoryRequest(h));
}
switch(Type) switch(Type)
{ {
case CipherType.Login: case CipherType.Login:
@ -46,7 +51,7 @@ namespace Bit.App.Models.Api
public string Name { get; set; } public string Name { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public IEnumerable<FieldType> Fields { get; set; } public IEnumerable<FieldType> Fields { get; set; }
public IEnumerable<PasswordHistoryResponse> PasswordHistory { get; set; } public IEnumerable<PasswordHistoryRequest> PasswordHistory { get; set; }
public LoginType Login { get; set; } public LoginType Login { get; set; }
public CardType Card { get; set; } public CardType Card { get; set; }

View file

@ -2,6 +2,12 @@
{ {
public class PasswordHistoryRequest public class PasswordHistoryRequest
{ {
public PasswordHistoryRequest(PasswordHistory ph)
{
Password = ph.Password?.EncryptedString;
LastUsedDate = ph.LastUsedDate;
}
public string Password { get; set; } public string Password { get; set; }
public System.DateTime LastUsedDate { get; set; } public System.DateTime LastUsedDate { get; set; }
} }

View file

@ -23,6 +23,8 @@ namespace Bit.App.Pages
private readonly IDeviceInfoService _deviceInfo; private readonly IDeviceInfoService _deviceInfo;
private readonly IGoogleAnalyticsService _googleAnalyticsService; private readonly IGoogleAnalyticsService _googleAnalyticsService;
private DateTime? _lastAction; private DateTime? _lastAction;
private string _originalLoginPassword = null;
private List<Tuple<string, string>> _originalHiddenFields = new List<Tuple<string, string>>();
public VaultEditCipherPage(string cipherId) public VaultEditCipherPage(string cipherId)
{ {
@ -169,7 +171,7 @@ namespace Bit.App.Pages
// Types // Types
if(Cipher.Type == CipherType.Login) if(Cipher.Type == CipherType.Login)
{ {
LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey, LoginTotpCell = new FormEntryCell(AppResources.AuthenticatorKey,
button1: _deviceInfo.HasCamera ? "camera.png" : null); button1: _deviceInfo.HasCamera ? "camera.png" : null);
LoginTotpCell.Entry.Text = Cipher.Login?.Totp?.Decrypt(Cipher.OrganizationId); LoginTotpCell.Entry.Text = Cipher.Login?.Totp?.Decrypt(Cipher.OrganizationId);
LoginTotpCell.Entry.DisableAutocapitalize = true; LoginTotpCell.Entry.DisableAutocapitalize = true;
@ -179,7 +181,8 @@ namespace Bit.App.Pages
LoginPasswordCell = new FormEntryCell(AppResources.Password, isPassword: true, LoginPasswordCell = new FormEntryCell(AppResources.Password, isPassword: true,
nextElement: LoginTotpCell.Entry, button1: "eye.png", button2: "refresh_alt.png"); 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.DisableAutocapitalize = true;
LoginPasswordCell.Entry.Autocorrect = false; LoginPasswordCell.Entry.Autocorrect = false;
LoginPasswordCell.Entry.FontFamily = LoginPasswordCell.Entry.FontFamily =
@ -211,7 +214,7 @@ namespace Bit.App.Pages
foreach(var uri in Cipher.Login.Uris) foreach(var uri in Cipher.Login.Uris)
{ {
var value = uri.Uri?.Decrypt(Cipher.OrganizationId); var value = uri.Uri?.Decrypt(Cipher.OrganizationId);
UrisSection.Insert(UrisSection.Count - 1, UrisSection.Insert(UrisSection.Count - 1,
Helpers.MakeUriCell(value, uri.Match, UrisSection, this)); Helpers.MakeUriCell(value, uri.Match, UrisSection, this));
} }
} }
@ -415,6 +418,11 @@ namespace Bit.App.Pages
{ {
FieldsSection.Add(cell); FieldsSection.Add(cell);
} }
if(!string.IsNullOrWhiteSpace(label) && !string.IsNullOrWhiteSpace(value) &&
field.Type == FieldType.Hidden)
{
_originalHiddenFields.Add(new Tuple<string, string>(label, value));
}
} }
} }
AddFieldCell = new ExtendedTextCell AddFieldCell = new ExtendedTextCell
@ -491,7 +499,8 @@ namespace Bit.App.Pages
Cipher.Notes = string.IsNullOrWhiteSpace(NotesCell.Editor.Text) ? null : Cipher.Notes = string.IsNullOrWhiteSpace(NotesCell.Editor.Text) ? null :
NotesCell.Editor.Text.Encrypt(Cipher.OrganizationId); NotesCell.Editor.Text.Encrypt(Cipher.OrganizationId);
Cipher.Favorite = FavoriteCell.On; Cipher.Favorite = FavoriteCell.On;
var passwordHistory = Cipher.PasswordHistory?.ToList() ?? new List<PasswordHistory>();
switch(Cipher.Type) switch(Cipher.Type)
{ {
case CipherType.Login: case CipherType.Login:
@ -505,6 +514,18 @@ namespace Bit.App.Pages
LoginTotpCell.Entry.Text.Encrypt(Cipher.OrganizationId), 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); Helpers.ProcessUrisSectionForSave(UrisSection, Cipher);
break; break;
case CipherType.SecureNote: case CipherType.SecureNote:
@ -639,7 +660,18 @@ namespace Bit.App.Pages
Cipher.FolderId = null; 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); await _deviceActionService.ShowLoadingAsync(AppResources.Saving);
var saveTask = await _cipherService.SaveAsync(Cipher); var saveTask = await _cipherService.SaveAsync(Cipher);
@ -714,7 +746,7 @@ namespace Bit.App.Pages
CardExpYearCell?.InitEvents(); CardExpYearCell?.InitEvents();
CardNameCell?.InitEvents(); CardNameCell?.InitEvents();
CardNumberCell?.InitEvents(); CardNumberCell?.InitEvents();
if (CardCodeCell?.Button1 != null) if(CardCodeCell?.Button1 != null)
{ {
CardCodeCell.Button1.Clicked += CardCodeButton_Clicked; CardCodeCell.Button1.Clicked += CardCodeButton_Clicked;
} }
@ -798,7 +830,7 @@ namespace Bit.App.Pages
CardExpYearCell?.Dispose(); CardExpYearCell?.Dispose();
CardNameCell?.Dispose(); CardNameCell?.Dispose();
CardNumberCell?.Dispose(); CardNumberCell?.Dispose();
if (CardCodeCell?.Button1 != null) if(CardCodeCell?.Button1 != null)
{ {
CardCodeCell.Button1.Clicked -= CardCodeButton_Clicked; CardCodeCell.Button1.Clicked -= CardCodeButton_Clicked;
} }
@ -826,7 +858,7 @@ namespace Bit.App.Pages
default: default:
break; break;
} }
Helpers.DisposeSectionEvents(FieldsSection); Helpers.DisposeSectionEvents(FieldsSection);
Helpers.DisposeSectionEvents(UrisSection); Helpers.DisposeSectionEvents(UrisSection);
} }

View file

@ -331,8 +331,9 @@ namespace Bit.App.Utilities
return cell; return cell;
} }
public static void ProcessFieldsSectionForSave(TableSection fieldsSection, Cipher cipher) public static List<Tuple<string, string>> ProcessFieldsSectionForSave(TableSection fieldsSection, Cipher cipher)
{ {
var hiddenFieldValues = new List<Tuple<string, string>>();
if(fieldsSection != null && fieldsSection.Count > 0) if(fieldsSection != null && fieldsSection.Count > 0)
{ {
var fields = new List<Field>(); var fields = new List<Field>();
@ -348,6 +349,12 @@ namespace Bit.App.Utilities
entryCell.Entry.Text.Encrypt(cipher.OrganizationId), entryCell.Entry.Text.Encrypt(cipher.OrganizationId),
Type = entryCell.Entry.IsPassword ? FieldType.Hidden : FieldType.Text Type = entryCell.Entry.IsPassword ? FieldType.Hidden : FieldType.Text
}); });
if(entryCell.Entry.IsPassword && !string.IsNullOrWhiteSpace(entryCell.Label.Text))
{
hiddenFieldValues.Add(new Tuple<string, string>(entryCell.Label.Text,
entryCell.Entry.Text));
}
} }
else if(cell is FormSwitchCell switchCell) else if(cell is FormSwitchCell switchCell)
{ {
@ -368,6 +375,7 @@ namespace Bit.App.Utilities
{ {
cipher.Fields = null; cipher.Fields = null;
} }
return hiddenFieldValues;
} }
public static FormEntryCell MakeUriCell(string value, UriMatchType? match, TableSection urisSection, Page page) public static FormEntryCell MakeUriCell(string value, UriMatchType? match, TableSection urisSection, Page page)