sync and display custom fields for login

This commit is contained in:
Kyle Spearrin 2017-09-22 17:32:20 -04:00
parent cc12ae7712
commit e126cbf644
14 changed files with 200 additions and 38 deletions

View file

@ -87,6 +87,7 @@
<Compile Include="Controls\PinControl.cs" />
<Compile Include="Controls\VaultAttachmentsViewCell.cs" />
<Compile Include="Controls\VaultListViewCell.cs" />
<Compile Include="Enums\FieldType.cs" />
<Compile Include="Enums\TwoFactorProviderType.cs" />
<Compile Include="Enums\EncryptionType.cs" />
<Compile Include="Enums\OrganizationUserType.cs" />
@ -98,7 +99,8 @@
<Compile Include="Abstractions\Services\ILocalizeService.cs" />
<Compile Include="Models\Api\ApiError.cs" />
<Compile Include="Models\Api\ApiResult.cs" />
<Compile Include="Models\Api\FolderDataModel.cs" />
<Compile Include="Models\Api\CipherDataModel.cs" />
<Compile Include="Models\Api\FieldDataModel.cs" />
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
<Compile Include="Models\Api\Request\FolderRequest.cs" />
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
@ -123,6 +125,7 @@
<Compile Include="Models\CipherString.cs" />
<Compile Include="Models\Data\AttachmentData.cs" />
<Compile Include="Models\Attachment.cs" />
<Compile Include="Models\Field.cs" />
<Compile Include="Models\Page\VaultAttachmentsPageModel.cs" />
<Compile Include="Models\SymmetricCryptoKey.cs" />
<Compile Include="Models\Data\SettingsData.cs" />

View file

@ -4,6 +4,8 @@
{
// Folder deprecated
//Folder = 0,
Login = 1
Login = 1,
SecureNote = 2,
Card = 3
}
}

View file

@ -0,0 +1,9 @@
namespace Bit.App.Enums
{
public enum FieldType : byte
{
Text = 0,
Hidden = 1,
Boolean = 2
}
}

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Bit.App.Models.Api
{
public abstract class CipherDataModel
{
public string Name { get; set; }
public string Notes { get; set; }
public IEnumerable<FieldDataModel> Fields { get; set; }
}
}

View file

@ -0,0 +1,11 @@
using Bit.App.Enums;
namespace Bit.App.Models.Api
{
public class FieldDataModel
{
public FieldType Type { get; set; }
public string Name { get; set; }
public string Value { get; set; }
}
}

View file

@ -1,7 +0,0 @@
namespace Bit.App.Models.Api
{
public class FolderDataModel
{
public string Name { get; set; }
}
}

View file

@ -1,12 +1,10 @@
namespace Bit.App.Models.Api
{
public class LoginDataModel
public class LoginDataModel : CipherDataModel
{
public string Name { get; set; }
public string Uri { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public string Notes { get; set; }
public string Totp { get; set; }
}
}

View file

@ -2,6 +2,8 @@
using SQLite;
using Bit.App.Abstractions;
using Bit.App.Models.Api;
using Newtonsoft.Json;
using System.Linq;
namespace Bit.App.Models.Data
{
@ -11,23 +13,6 @@ namespace Bit.App.Models.Data
public LoginData()
{ }
public LoginData(Login login, string userId)
{
Id = login.Id;
FolderId = login.FolderId;
UserId = userId;
OrganizationId = login.OrganizationId;
Name = login.Name?.EncryptedString;
Uri = login.Uri?.EncryptedString;
Username = login.Username?.EncryptedString;
Password = login.Password?.EncryptedString;
Notes = login.Notes?.EncryptedString;
Totp = login?.Notes?.EncryptedString;
Favorite = login.Favorite;
Edit = login.Edit;
OrganizationUseTotp = login.OrganizationUseTotp;
}
public LoginData(CipherResponse cipher, string userId)
{
if(cipher.Type != Enums.CipherType.Login)
@ -51,6 +36,15 @@ namespace Bit.App.Models.Data
Edit = cipher.Edit;
OrganizationUseTotp = cipher.OrganizationUseTotp;
RevisionDateTime = cipher.RevisionDate;
if(data.Fields != null && data.Fields.Any())
{
try
{
Fields = JsonConvert.SerializeObject(data.Fields);
}
catch(JsonSerializationException) { }
}
}
[PrimaryKey]
@ -65,6 +59,7 @@ namespace Bit.App.Models.Data
public string Password { get; set; }
public string Notes { get; set; }
public string Totp { get; set; }
public string Fields { get; set; }
public bool Favorite { get; set; }
public bool Edit { get; set; }
public bool OrganizationUseTotp { get; set; }

19
src/App/Models/Field.cs Normal file
View file

@ -0,0 +1,19 @@
using Bit.App.Enums;
using Bit.App.Models.Api;
namespace Bit.App.Models
{
public class Field
{
public Field(FieldDataModel model)
{
Type = model.Type;
Name = new CipherString(model.Name);
Value = new CipherString(model.Value);
}
public FieldType Type { get; set; }
public CipherString Name { get; set; }
public CipherString Value { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using Bit.App.Models.Api;
using Bit.App.Models.Data;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
@ -26,6 +27,16 @@ namespace Bit.App.Models
Edit = data.Edit;
OrganizationUseTotp = data.OrganizationUseTotp;
Attachments = attachments?.Select(a => new Attachment(a));
if(!string.IsNullOrWhiteSpace(data.Fields))
{
try
{
var fieldModels = JsonConvert.DeserializeObject<IEnumerable<FieldDataModel>>(data.Fields);
Fields = fieldModels?.Select(f => new Field(f));
}
catch(JsonSerializationException) { }
}
}
public string Id { get; set; }
@ -38,14 +49,10 @@ namespace Bit.App.Models
public CipherString Password { get; set; }
public CipherString Notes { get; set; }
public CipherString Totp { get; set; }
public IEnumerable<Field> Fields { get; set; }
public bool Favorite { get; set; }
public bool Edit { get; set; }
public bool OrganizationUseTotp { get; set; }
public IEnumerable<Attachment> Attachments { get; set; }
public LoginData ToLoginData(string userId)
{
return new LoginData(this, userId);
}
}
}

View file

@ -3,6 +3,7 @@ using System.ComponentModel;
using Bit.App.Resources;
using Xamarin.Forms;
using System.Collections.Generic;
using Bit.App.Enums;
namespace Bit.App.Models.Page
{
@ -17,6 +18,7 @@ namespace Bit.App.Models.Page
private int _totpSec = 30;
private bool _revealPassword;
private List<Attachment> _attachments;
private List<Field> _fields;
public VaultViewLoginPageModel() { }
@ -144,12 +146,10 @@ namespace Bit.App.Models.Page
_revealPassword = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(RevealPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(MaskedPassword)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowHideText)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowHideImage)));
}
}
public string MaskedPassword => RevealPassword ? Password : Password == null ? null : new string('●', Password.Length);
public string ShowHideText => RevealPassword ? AppResources.Hide : AppResources.Show;
public ImageSource ShowHideImage => RevealPassword ? ImageSource.FromFile("eye_slash") : ImageSource.FromFile("eye");
public string TotpCode
@ -189,6 +189,18 @@ namespace Bit.App.Models.Page
}
public bool ShowAttachments => (Attachments?.Count ?? 0) > 0;
public List<Field> Fields
{
get { return _fields; }
set
{
_fields = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Fields)));
PropertyChanged(this, new PropertyChangedEventArgs(nameof(ShowFields)));
}
}
public bool ShowFields => (Fields?.Count ?? 0) > 0;
public void Update(Login login)
{
Name = login.Name?.Decrypt(login.OrganizationId);
@ -217,6 +229,25 @@ namespace Bit.App.Models.Page
{
login.Attachments = null;
}
if(login.Fields != null)
{
var fields = new List<Field>();
foreach(var field in login.Fields)
{
fields.Add(new Field
{
Name = field.Name?.Decrypt(login.OrganizationId),
Value = field.Value?.Decrypt(login.OrganizationId),
Type = field.Type
});
}
Fields = fields;
}
else
{
login.Fields = null;
}
}
public class Attachment
@ -227,5 +258,26 @@ namespace Bit.App.Models.Page
public long Size { get; set; }
public string Url { get; set; }
}
public class Field
{
private string _maskedValue;
public string Name { get; set; }
public string Value { get; set; }
public string MaskedValue
{
get
{
if(_maskedValue == null && Value != null)
{
_maskedValue = new string('●', Value.Length);
}
return _maskedValue;
}
}
public FieldType Type { get; set; }
}
}
}

View file

@ -11,6 +11,7 @@ using Bit.App.Utilities;
using System.Collections.Generic;
using Bit.App.Models;
using System.Linq;
using Bit.App.Enums;
namespace Bit.App.Pages
{
@ -39,12 +40,14 @@ namespace Bit.App.Pages
private TableSection LoginInformationSection { get; set; }
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 EditLoginToolBarItem EditItem { get; set; }
public List<LabeledValueCell> FieldsCells { get; set; }
public List<AttachmentViewCell> AttachmentCells { get; set; }
private void Init()
@ -255,6 +258,53 @@ namespace Bit.App.Pages
Table.Root.Add(AttachmentsSection);
}
if(Table.Root.Contains(FieldsSection))
{
Table.Root.Remove(FieldsSection);
}
if(Model.ShowFields)
{
FieldsSection = new TableSection(AppResources.CustomFields);
foreach(var field in Model.Fields)
{
LabeledValueCell fieldCell;
ExtendedButton copyButton = null;
switch(field.Type)
{
case FieldType.Text:
fieldCell = new LabeledValueCell(field.Name, field.Value, AppResources.Copy);
copyButton = fieldCell.Button1;
break;
case FieldType.Hidden:
fieldCell = new LabeledValueCell(field.Name, field.MaskedValue, string.Empty, AppResources.Copy);
copyButton = fieldCell.Button2;
fieldCell.Value.FontFamily =
Helpers.OnPlatform(iOS: "Menlo-Regular", Android: "monospace", WinPhone: "Courier");
if(Device.RuntimePlatform == Device.iOS)
{
fieldCell.Button1.Margin = new Thickness(10, 0);
}
// TODO: show/hide image toggle
break;
case FieldType.Boolean:
fieldCell = new LabeledValueCell(field.Name, field.Value == "true" ? "✓" : "-");
break;
default:
continue;
}
fieldCell.Value.LineBreakMode = LineBreakMode.WordWrap;
if(copyButton != null)
{
copyButton.Command = new Command(() => Copy(field.Value, field.Name));
copyButton.WidthRequest = 59;
}
FieldsSection.Add(fieldCell);
}
Table.Root.Add(FieldsSection);
}
base.OnAppearing();
}
@ -322,8 +372,8 @@ namespace Bit.App.Pages
private void Copy(string copyText, string alertLabel)
{
_deviceActionService.CopyToClipboard(copyText);
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
_deviceActionService.CopyToClipboard(copyText);
_userDialogs.Toast(string.Format(AppResources.ValueHasBeenCopied, alertLabel));
}
private void TotpTick(string totpKey)

View file

@ -718,6 +718,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Custom Fields.
/// </summary>
public static string CustomFields {
get {
return ResourceManager.GetString("CustomFields", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Delete.
/// </summary>

View file

@ -1030,4 +1030,7 @@
<data name="BitwardenAutofillServiceNotificationContentOld" xml:space="preserve">
<value>Tap this notification to view logins from your vault.</value>
</data>
<data name="CustomFields" xml:space="preserve">
<value>Custom Fields</value>
</data>
</root>