1
0
mirror of https://github.com/bitwarden/mobile.git synced 2024-11-27 12:26:31 +01:00

add password history and updated dates

This commit is contained in:
Kyle Spearrin 2018-07-30 13:15:53 -04:00
parent 13b9e01604
commit 01d9ccc110
16 changed files with 188 additions and 3 deletions

View File

@ -13,12 +13,14 @@ 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;
Totp = cipher.Login.Totp?.EncryptedString; Totp = cipher.Login.Totp?.EncryptedString;
} }
public IEnumerable<LoginUriType> Uris { get; set; } public IEnumerable<LoginUriType> Uris { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string Password { get; set; } public string Password { get; set; }
public System.DateTime? PasswordRevisionDate { get; set; }
public string Totp { get; set; } public string Totp { get; set; }
} }
} }

View File

@ -46,6 +46,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 LoginType Login { get; set; } public LoginType Login { get; set; }
public CardType Card { get; set; } public CardType Card { get; set; }

View File

@ -0,0 +1,8 @@
namespace Bit.App.Models.Api
{
public class PasswordHistoryRequest
{
public string Password { get; set; }
public System.DateTime LastUsedDate { get; set; }
}
}

View File

@ -22,6 +22,7 @@ namespace Bit.App.Models.Api
public SecureNoteType SecureNote { get; set; } public SecureNoteType SecureNote { get; set; }
public IEnumerable<FieldType> Fields { get; set; } public IEnumerable<FieldType> Fields { get; set; }
public IEnumerable<AttachmentResponse> Attachments { get; set; } public IEnumerable<AttachmentResponse> Attachments { get; set; }
public IEnumerable<PasswordHistoryResponse> PasswordHistory { get; set; }
public IEnumerable<string> CollectionIds { get; set; } public IEnumerable<string> CollectionIds { get; set; }
public DateTime RevisionDate { get; set; } public DateTime RevisionDate { get; set; }
} }

View File

@ -0,0 +1,8 @@
namespace Bit.App.Models.Api
{
public class PasswordHistoryResponse
{
public string Password { get; set; }
public System.DateTime LastUsedDate { get; set; }
}
}

View File

@ -24,6 +24,7 @@ namespace Bit.App.Models
Edit = data.Edit; Edit = data.Edit;
OrganizationUseTotp = data.OrganizationUseTotp; OrganizationUseTotp = data.OrganizationUseTotp;
Attachments = attachments?.Select(a => new Attachment(a)); Attachments = attachments?.Select(a => new Attachment(a));
RevisionDate = data.RevisionDateTime;
switch(Type) switch(Type)
{ {
@ -52,6 +53,17 @@ namespace Bit.App.Models
} }
catch(JsonSerializationException) { } catch(JsonSerializationException) { }
} }
if(!string.IsNullOrWhiteSpace(data.PasswordHistory))
{
try
{
var phModels = JsonConvert.DeserializeObject<IEnumerable<PasswordHistoryDataModel>>(
data.PasswordHistory);
PasswordHistory = phModels?.Select(f => new PasswordHistory(f));
}
catch(JsonSerializationException) { }
}
} }
public string Id { get; set; } public string Id { get; set; }
@ -62,14 +74,19 @@ namespace Bit.App.Models
public CipherString Name { get; set; } public CipherString Name { get; set; }
public CipherString Notes { get; set; } public CipherString Notes { get; set; }
public IEnumerable<Field> Fields { get; set; } public IEnumerable<Field> Fields { get; set; }
public IEnumerable<PasswordHistory> PasswordHistory { get; set; }
public bool Favorite { get; set; } public bool Favorite { get; set; }
public bool Edit { get; set; } public bool Edit { get; set; }
public bool OrganizationUseTotp { get; set; } public bool OrganizationUseTotp { get; set; }
public IEnumerable<Attachment> Attachments { get; set; } public IEnumerable<Attachment> Attachments { get; set; }
public System.DateTime RevisionDate { get; set; }
public Login Login { get; set; } public Login Login { get; set; }
public Identity Identity { get; set; } public Identity Identity { get; set; }
public Card Card { get; set; } public Card Card { get; set; }
public SecureNote SecureNote { get; set; } public SecureNote SecureNote { get; set; }
public System.DateTime? PasswordRevisionDisplayDate =>
Login?.Password == null ? (System.DateTime?)null : Login.PasswordRevisionDate ?? RevisionDate;
} }
} }

View File

@ -64,6 +64,16 @@ namespace Bit.App.Models.Data
} }
catch(JsonSerializationException) { } catch(JsonSerializationException) { }
} }
if(cipher.PasswordHistory != null && cipher.PasswordHistory.Any())
{
try
{
PasswordHistory = JsonConvert.SerializeObject(
cipher.PasswordHistory.Select(h => new PasswordHistoryDataModel(h)));
}
catch(JsonSerializationException) { }
}
} }
[PrimaryKey] [PrimaryKey]
@ -75,6 +85,7 @@ namespace Bit.App.Models.Data
public string Name { get; set; } public string Name { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public string Fields { get; set; } public string Fields { get; set; }
public string PasswordHistory { get; set; }
public string Login { get; set; } public string Login { get; set; }
public string Card { get; set; } public string Card { get; set; }
public string Identity { get; set; } public string Identity { get; set; }

View File

@ -13,10 +13,12 @@ namespace Bit.App.Models.Data
Name = cipher.Name; Name = cipher.Name;
Notes = cipher.Notes; Notes = cipher.Notes;
Fields = cipher.Fields?.Select(f => new FieldDataModel(f)); Fields = cipher.Fields?.Select(f => new FieldDataModel(f));
PasswordHistory = cipher.PasswordHistory?.Select(h => new PasswordHistoryDataModel(h));
} }
public string Name { get; set; } public string Name { get; set; }
public string Notes { get; set; } public string Notes { get; set; }
public IEnumerable<FieldDataModel> Fields { get; set; } public IEnumerable<FieldDataModel> Fields { get; set; }
public IEnumerable<PasswordHistoryDataModel> PasswordHistory { get; set; }
} }
} }

View File

@ -22,6 +22,7 @@ namespace Bit.App.Models.Data
Uris = response.Login.Uris?.Where(u => u != null).Select(u => new LoginUriDataModel(u)); Uris = response.Login.Uris?.Where(u => u != null).Select(u => new LoginUriDataModel(u));
Username = response.Login.Username; Username = response.Login.Username;
Password = response.Login.Password; Password = response.Login.Password;
PasswordRevisionDate = response.Login.PasswordRevisionDate;
Totp = response.Login.Totp; Totp = response.Login.Totp;
} }
@ -33,6 +34,7 @@ namespace Bit.App.Models.Data
public IEnumerable<LoginUriDataModel> Uris { get; set; } public IEnumerable<LoginUriDataModel> Uris { get; set; }
public string Username { get; set; } public string Username { get; set; }
public string Password { get; set; } public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public string Totp { get; set; } public string Totp { get; set; }
} }
} }

View File

@ -0,0 +1,18 @@
using Bit.App.Models.Api;
namespace Bit.App.Models.Data
{
public class PasswordHistoryDataModel
{
public PasswordHistoryDataModel() { }
public PasswordHistoryDataModel(PasswordHistoryResponse h)
{
Password = h.Password;
LastUsedDate = h.LastUsedDate;
}
public string Password { get; set; }
public System.DateTime LastUsedDate { get; set; }
}
}

View File

@ -28,6 +28,7 @@ namespace Bit.App.Models
Username = deserializedData.Username != null ? new CipherString(deserializedData.Username) : null; Username = deserializedData.Username != null ? new CipherString(deserializedData.Username) : null;
Password = deserializedData.Password != null ? new CipherString(deserializedData.Password) : null; Password = deserializedData.Password != null ? new CipherString(deserializedData.Password) : null;
PasswordRevisionDate = deserializedData.PasswordRevisionDate;
Totp = deserializedData.Totp != null ? new CipherString(deserializedData.Totp) : null; Totp = deserializedData.Totp != null ? new CipherString(deserializedData.Totp) : null;
Uris = deserializedData.Uris?.Select(u => new LoginUri(u)); Uris = deserializedData.Uris?.Select(u => new LoginUri(u));
} }
@ -35,6 +36,7 @@ namespace Bit.App.Models
public IEnumerable<LoginUri> Uris { get; set; } public IEnumerable<LoginUri> Uris { get; set; }
public CipherString Username { get; set; } public CipherString Username { get; set; }
public CipherString Password { get; set; } public CipherString Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public CipherString Totp { get; set; } public CipherString Totp { get; set; }
} }
} }

View File

@ -11,7 +11,7 @@ namespace Bit.App.Models.Page
{ {
private const string MaskedPasswordString = "••••••••"; private const string MaskedPasswordString = "••••••••";
private string _name, _notes; private string _name, _notes, _reivisonDate, _passwordReivisonDate;
private List<Attachment> _attachments; private List<Attachment> _attachments;
private List<Field> _fields; private List<Field> _fields;
private List<LoginUri> _loginUris; private List<LoginUri> _loginUris;
@ -44,6 +44,28 @@ namespace Bit.App.Models.Page
} }
} }
public string RevisionDate
{
get => _reivisonDate;
set
{
_reivisonDate = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevisionDate)));
}
}
public string PasswordRevisionDate
{
get => _passwordReivisonDate;
set
{
_passwordReivisonDate = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(PasswordRevisionDate)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowPasswordRevisionDate)));
}
}
public bool ShowPasswordRevisionDate => !string.IsNullOrWhiteSpace(PasswordRevisionDate);
public string Notes public string Notes
{ {
get => _notes; get => _notes;
@ -523,6 +545,20 @@ namespace Bit.App.Models.Page
{ {
Name = cipher.Name?.Decrypt(cipher.OrganizationId); Name = cipher.Name?.Decrypt(cipher.OrganizationId);
Notes = cipher.Notes?.Decrypt(cipher.OrganizationId); Notes = cipher.Notes?.Decrypt(cipher.OrganizationId);
var revisionDate = DateTime.SpecifyKind(cipher.RevisionDate, DateTimeKind.Utc).ToLocalTime();
RevisionDate = revisionDate.ToShortDateString() + " " + revisionDate.ToShortTimeString();
if(cipher.PasswordRevisionDisplayDate.HasValue)
{
var passwordRevisionDate = DateTime.SpecifyKind(
cipher.PasswordRevisionDisplayDate.Value, DateTimeKind.Utc).ToLocalTime();
PasswordRevisionDate = passwordRevisionDate.ToShortDateString() + " " +
passwordRevisionDate.ToShortTimeString();
}
else
{
PasswordRevisionDate = null;
}
if(cipher.Attachments != null) if(cipher.Attachments != null)
{ {
@ -693,7 +729,7 @@ namespace Bit.App.Models.Page
} }
} }
public string Label => IsWebsite ? AppResources.Website : AppResources.URI; public string Label => IsWebsite ? AppResources.Website : AppResources.URI;
public bool IsWebsite => Value == null ? false : public bool IsWebsite => Value == null ? false :
Value.StartsWith("http://") || Value.StartsWith("https://"); Value.StartsWith("http://") || Value.StartsWith("https://");
public bool IsApp => Value == null ? false : Value.StartsWith(Constants.AndroidAppProtocol); public bool IsApp => Value == null ? false : Value.StartsWith(Constants.AndroidAppProtocol);
} }

View File

@ -0,0 +1,18 @@
using Bit.App.Models.Data;
namespace Bit.App.Models
{
public class PasswordHistory
{
public PasswordHistory() { }
public PasswordHistory(PasswordHistoryDataModel model)
{
Password = model.Password != null ? new CipherString(model.Password) : null;
LastUsedDate = model.LastUsedDate;
}
public CipherString Password { get; set; }
public System.DateTime LastUsedDate { get; set; }
}
}

View File

@ -43,6 +43,7 @@ namespace Bit.App.Pages
private TableSection NotesSection { get; set; } private TableSection NotesSection { get; set; }
private TableSection AttachmentsSection { get; set; } private TableSection AttachmentsSection { get; set; }
private TableSection FieldsSection { get; set; } private TableSection FieldsSection { get; set; }
public TableSection OtherSection { get; set; }
public LabeledValueCell NotesCell { get; set; } public LabeledValueCell NotesCell { get; set; }
private EditCipherToolBarItem EditItem { get; set; } private EditCipherToolBarItem EditItem { get; set; }
public List<LabeledValueCell> FieldsCells { get; set; } public List<LabeledValueCell> FieldsCells { get; set; }
@ -51,6 +52,7 @@ namespace Bit.App.Pages
// Login // Login
public LabeledValueCell LoginUsernameCell { get; set; } public LabeledValueCell LoginUsernameCell { get; set; }
public LabeledValueCell LoginPasswordCell { get; set; } public LabeledValueCell LoginPasswordCell { get; set; }
public LabeledValueCell LoginPasswordRevisionDateCell { get; set; }
public LabeledValueCell LoginTotpCodeCell { get; set; } public LabeledValueCell LoginTotpCodeCell { get; set; }
// Card // Card
@ -111,6 +113,10 @@ namespace Bit.App.Pages
NotesCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.Notes)); NotesCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.Notes));
NotesCell.Value.LineBreakMode = LineBreakMode.WordWrap; NotesCell.Value.LineBreakMode = LineBreakMode.WordWrap;
var revisionDateCell = new LabeledValueCell(AppResources.DateUpdated);
revisionDateCell.Value.SetBinding(Label.TextProperty, nameof(VaultViewCipherPageModel.RevisionDate));
revisionDateCell.Value.LineBreakMode = LineBreakMode.WordWrap;
switch(_type) switch(_type)
{ {
case CipherType.Login: case CipherType.Login:
@ -152,6 +158,12 @@ namespace Bit.App.Pages
nameof(VaultViewCipherPageModel.LoginTotpColor)); nameof(VaultViewCipherPageModel.LoginTotpColor));
LoginTotpCodeCell.Value.FontFamily = LoginTotpCodeCell.Value.FontFamily =
Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier"); Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", Windows: "Courier");
// Password Revision Date
LoginPasswordRevisionDateCell = new LabeledValueCell(AppResources.DatePasswordUpdated);
LoginPasswordRevisionDateCell.Value.SetBinding(Label.TextProperty,
nameof(VaultViewCipherPageModel.PasswordRevisionDate));
LoginPasswordRevisionDateCell.Value.LineBreakMode = LineBreakMode.WordWrap;
break; break;
case CipherType.Card: case CipherType.Card:
CardNameCell = new LabeledValueCell(AppResources.CardholderName); CardNameCell = new LabeledValueCell(AppResources.CardholderName);
@ -235,6 +247,11 @@ namespace Bit.App.Pages
NotesCell NotesCell
}; };
OtherSection = new TableSection(AppResources.Other)
{
revisionDateCell
};
Table = new ExtendedTableView Table = new ExtendedTableView
{ {
Intent = TableIntent.Settings, Intent = TableIntent.Settings,
@ -363,10 +380,26 @@ namespace Bit.App.Pages
Table.Root.Add(AttachmentsSection); Table.Root.Add(AttachmentsSection);
} }
// Other
if(Table.Root.Contains(OtherSection))
{
Table.Root.Remove(OtherSection);
}
Table.Root.Add(OtherSection);
// Various types // Various types
switch(cipher.Type) switch(cipher.Type)
{ {
case CipherType.Login: case CipherType.Login:
if(OtherSection.Contains(LoginPasswordRevisionDateCell))
{
OtherSection.Remove(LoginPasswordRevisionDateCell);
}
if(Model.ShowPasswordRevisionDate)
{
OtherSection.Add(LoginPasswordRevisionDateCell);
}
AddSectionCell(LoginUsernameCell, Model.ShowLoginUsername); AddSectionCell(LoginUsernameCell, Model.ShowLoginUsername);
AddSectionCell(LoginPasswordCell, Model.ShowLoginPassword); AddSectionCell(LoginPasswordCell, Model.ShowLoginPassword);

View File

@ -942,6 +942,24 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Password Updated.
/// </summary>
public static string DatePasswordUpdated {
get {
return ResourceManager.GetString("DatePasswordUpdated", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Updated.
/// </summary>
public static string DateUpdated {
get {
return ResourceManager.GetString("DateUpdated", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to December. /// Looks up a localized string similar to December.
/// </summary> /// </summary>

View File

@ -887,7 +887,7 @@
</data> </data>
<data name="YubiKeyTitle" xml:space="preserve"> <data name="YubiKeyTitle" xml:space="preserve">
<value>YubiKey Security Key</value> <value>YubiKey Security Key</value>
<comment>"YubiKey NEO" is the product name and should not be translated.</comment> <comment>"YubiKey" is the product name and should not be translated.</comment>
</data> </data>
<data name="AddNewAttachment" xml:space="preserve"> <data name="AddNewAttachment" xml:space="preserve">
<value>Add New Attachment</value> <value>Add New Attachment</value>
@ -1305,4 +1305,12 @@
<data name="BitwardenAutofillAccessibilityServiceDescription2" xml:space="preserve"> <data name="BitwardenAutofillAccessibilityServiceDescription2" xml:space="preserve">
<value>The accessibility service may be helpful to use when apps do not support the standard auto-fill service.</value> <value>The accessibility service may be helpful to use when apps do not support the standard auto-fill service.</value>
</data> </data>
<data name="DatePasswordUpdated" xml:space="preserve">
<value>Password Updated</value>
<comment>ex. Date this password was updated</comment>
</data>
<data name="DateUpdated" xml:space="preserve">
<value>Updated</value>
<comment>ex. Date this item was updated</comment>
</data>
</root> </root>