mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
sync and display custom fields for login
This commit is contained in:
parent
cc12ae7712
commit
e126cbf644
14 changed files with 200 additions and 38 deletions
|
@ -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" />
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
{
|
||||
// Folder deprecated
|
||||
//Folder = 0,
|
||||
Login = 1
|
||||
Login = 1,
|
||||
SecureNote = 2,
|
||||
Card = 3
|
||||
}
|
||||
}
|
||||
|
|
9
src/App/Enums/FieldType.cs
Normal file
9
src/App/Enums/FieldType.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Bit.App.Enums
|
||||
{
|
||||
public enum FieldType : byte
|
||||
{
|
||||
Text = 0,
|
||||
Hidden = 1,
|
||||
Boolean = 2
|
||||
}
|
||||
}
|
11
src/App/Models/Api/CipherDataModel.cs
Normal file
11
src/App/Models/Api/CipherDataModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
11
src/App/Models/Api/FieldDataModel.cs
Normal file
11
src/App/Models/Api/FieldDataModel.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace Bit.App.Models.Api
|
||||
{
|
||||
public class FolderDataModel
|
||||
{
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
19
src/App/Models/Field.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
9
src/App/Resources/AppResources.Designer.cs
generated
9
src/App/Resources/AppResources.Designer.cs
generated
|
@ -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>
|
||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue