From 90b62d61aef64a0ab84ab869d7682f3af8ff10b3 Mon Sep 17 00:00:00 2001
From: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
Date: Tue, 9 Nov 2021 07:34:16 +1000
Subject: [PATCH] [Linked fields] Add Linked Field as a custom field type
(#1563)
* Add linked fields support
* Fix style, don't show linked field if Secure Note
* Finish basic linked fields for Login
* Use Field.LinkedId to store linked field info
* Reset Linked Custom Fields if cipherType changes
* Refactor to use ItemView class
* Use enum for LinkedId
* Detect if no linkedFieldOptions
---
src/App/Pages/Vault/AddEditPage.xaml | 10 ++++
src/App/Pages/Vault/AddEditPageViewModel.cs | 64 ++++++++++++++++++---
src/App/Pages/Vault/ViewPage.xaml | 6 ++
src/App/Pages/Vault/ViewPageViewModel.cs | 28 ++++++++-
src/App/Resources/AppResources.Designer.cs | 16 +++++-
src/App/Resources/AppResources.resx | 6 ++
src/Core/Enums/FieldType.cs | 3 +-
src/Core/Enums/LinkedIdType.cs | 38 ++++++++++++
src/Core/Models/Api/FieldApi.cs | 1 +
src/Core/Models/Data/FieldData.cs | 2 +
src/Core/Models/Domain/Field.cs | 8 ++-
src/Core/Models/Request/CipherRequest.cs | 3 +-
src/Core/Models/View/CardView.cs | 19 +++++-
src/Core/Models/View/CipherView.cs | 18 ++++--
src/Core/Models/View/FieldView.cs | 2 +
src/Core/Models/View/IdentityView.cs | 32 ++++++++++-
src/Core/Models/View/ItemView.cs | 14 +++++
src/Core/Models/View/LoginView.cs | 14 ++++-
src/Core/Models/View/SecureNoteView.cs | 6 +-
src/Core/Services/CipherService.cs | 3 +-
20 files changed, 263 insertions(+), 30 deletions(-)
create mode 100644 src/Core/Enums/LinkedIdType.cs
create mode 100644 src/Core/Models/View/ItemView.cs
diff --git a/src/App/Pages/Vault/AddEditPage.xaml b/src/App/Pages/Vault/AddEditPage.xaml
index 08a99ad20..b630739fc 100644
--- a/src/App/Pages/Vault/AddEditPage.xaml
+++ b/src/App/Pages/Vault/AddEditPage.xaml
@@ -656,6 +656,16 @@
+
+
+
(UriMatchType.Exact, AppResources.Exact),
new KeyValuePair(UriMatchType.Never, AppResources.Never)
};
- private List> _fieldTypeOptions =
- new List>
- {
- new KeyValuePair(FieldType.Text, AppResources.FieldTypeText),
- new KeyValuePair(FieldType.Hidden, AppResources.FieldTypeHidden),
- new KeyValuePair(FieldType.Boolean, AppResources.FieldTypeBoolean)
- };
public AddEditPageViewModel()
{
@@ -667,8 +660,20 @@ namespace Bit.App.Pages
public async void AddField()
{
+ var fieldTypeOptions = new List>
+ {
+ new KeyValuePair(FieldType.Text, AppResources.FieldTypeText),
+ new KeyValuePair(FieldType.Hidden, AppResources.FieldTypeHidden),
+ new KeyValuePair(FieldType.Boolean, AppResources.FieldTypeBoolean),
+ };
+
+ if (Cipher.LinkedFieldOptions != null)
+ {
+ fieldTypeOptions.Add(new KeyValuePair(FieldType.Linked, AppResources.FieldTypeLinked));
+ }
+
var typeSelection = await Page.DisplayActionSheet(AppResources.SelectTypeField, AppResources.Cancel, null,
- _fieldTypeOptions.Select(f => f.Value).ToArray());
+ fieldTypeOptions.Select(f => f.Value).ToArray());
if (typeSelection != null && typeSelection != AppResources.Cancel)
{
var name = await _deviceActionService.DisplayPromptAync(AppResources.CustomFieldName);
@@ -680,7 +685,7 @@ namespace Bit.App.Pages
{
Fields = new ExtendedObservableCollection();
}
- var type = _fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
+ var type = fieldTypeOptions.FirstOrDefault(f => f.Value == typeSelection).Key;
Fields.Add(new AddEditPageFieldViewModel(Cipher, new FieldView
{
Type = type,
@@ -746,6 +751,12 @@ namespace Bit.App.Pages
{
Cipher.Type = TypeOptions[TypeSelectedIndex].Value;
TriggerCipherChanged();
+
+ // Linked Custom Fields only apply to a specific item type
+ foreach (var field in Fields.Where(f => f.IsLinkedType).ToList())
+ {
+ Fields.Remove(field);
+ }
}
}
@@ -850,23 +861,29 @@ namespace Bit.App.Pages
public class AddEditPageFieldViewModel : ExtendedViewModel
{
+ private II18nService _i18nService;
private FieldView _field;
private CipherView _cipher;
private bool _showHiddenValue;
private bool _booleanValue;
+ private int _linkedFieldOptionSelectedIndex;
private string[] _additionalFieldProperties = new string[]
{
nameof(IsBooleanType),
nameof(IsHiddenType),
nameof(IsTextType),
+ nameof(IsLinkedType),
};
public AddEditPageFieldViewModel(CipherView cipher, FieldView field)
{
+ _i18nService = ServiceContainer.Resolve("i18nService");
_cipher = cipher;
Field = field;
ToggleHiddenValueCommand = new Command(ToggleHiddenValue);
BooleanValue = IsBooleanType && field.Value == "true";
+ LinkedFieldOptionSelectedIndex = !Field.LinkedId.HasValue ? 0 :
+ LinkedFieldOptions.FindIndex(lfo => lfo.Value == Field.LinkedId.Value);
}
public FieldView Field
@@ -898,12 +915,32 @@ namespace Bit.App.Pages
}
}
+ public int LinkedFieldOptionSelectedIndex
+ {
+ get => _linkedFieldOptionSelectedIndex;
+ set
+ {
+ if (SetProperty(ref _linkedFieldOptionSelectedIndex, value))
+ {
+ LinkedFieldValueChanged();
+ }
+ }
+ }
+
+ public List> LinkedFieldOptions
+ {
+ get => _cipher.LinkedFieldOptions
+ .Select(kvp => new KeyValuePair(_i18nService.T(kvp.Key), kvp.Value))
+ .ToList();
+ }
+
public Command ToggleHiddenValueCommand { get; set; }
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
public bool IsTextType => _field.Type == FieldType.Text;
public bool IsBooleanType => _field.Type == FieldType.Boolean;
public bool IsHiddenType => _field.Type == FieldType.Hidden;
+ public bool IsLinkedType => _field.Type == FieldType.Linked;
public bool ShowViewHidden => IsHiddenType && (_cipher.ViewPassword || _field.NewField);
public void ToggleHiddenValue()
@@ -920,5 +957,14 @@ namespace Bit.App.Pages
{
TriggerPropertyChanged(nameof(Field), _additionalFieldProperties);
}
+
+ private void LinkedFieldValueChanged()
+ {
+ if (Field != null && LinkedFieldOptionSelectedIndex > -1)
+ {
+ Field.LinkedId = LinkedFieldOptions.Find(lfo =>
+ lfo.Value == LinkedFieldOptions[LinkedFieldOptionSelectedIndex].Value).Value;
+ }
+ }
}
}
diff --git a/src/App/Pages/Vault/ViewPage.xaml b/src/App/Pages/Vault/ViewPage.xaml
index d9607ba19..d106d3c16 100644
--- a/src/App/Pages/Vault/ViewPage.xaml
+++ b/src/App/Pages/Vault/ViewPage.xaml
@@ -561,6 +561,12 @@
Grid.Row="1"
Grid.Column="0"
IsVisible="{Binding IsTextType}" />
+
("i18nService");
_vm = vm;
_cipher = cipher;
Field = field;
@@ -741,16 +743,38 @@ namespace Bit.App.Pages
});
}
+ public string ValueText
+ {
+ get
+ {
+ if (IsBooleanType)
+ {
+ return _field.Value == "true" ? "" : "";
+ }
+ else if (IsLinkedType)
+ {
+ var i18nKey = _cipher.LinkedFieldI18nKey(Field.LinkedId.GetValueOrDefault());
+ return " " + _i18nService.T(i18nKey);
+ }
+ else
+ {
+ return _field.Value;
+ }
+ }
+ }
+
public Command ToggleHiddenValueCommand { get; set; }
- public string ValueText => IsBooleanType ? (_field.Value == "true" ? "" : "") : _field.Value;
public string ShowHiddenValueIcon => _showHiddenValue ? "" : "";
public bool IsTextType => _field.Type == Core.Enums.FieldType.Text;
public bool IsBooleanType => _field.Type == Core.Enums.FieldType.Boolean;
public bool IsHiddenType => _field.Type == Core.Enums.FieldType.Hidden;
+ public bool IsLinkedType => _field.Type == Core.Enums.FieldType.Linked;
public bool ShowViewHidden => IsHiddenType && _cipher.ViewPassword;
public bool ShowCopyButton => _field.Type != Core.Enums.FieldType.Boolean &&
- !string.IsNullOrWhiteSpace(_field.Value) && !(IsHiddenType && !_cipher.ViewPassword);
+ !string.IsNullOrWhiteSpace(_field.Value) &&
+ !(IsHiddenType && !_cipher.ViewPassword) &&
+ _field.Type != FieldType.Linked;
public async void ToggleHiddenValue()
{
diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs
index c2f92a336..6d9c3bd3b 100644
--- a/src/App/Resources/AppResources.Designer.cs
+++ b/src/App/Resources/AppResources.Designer.cs
@@ -1,6 +1,7 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -9,9 +10,10 @@
namespace Bit.App.Resources {
using System;
+ using System.Reflection;
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class AppResources {
@@ -1779,6 +1781,12 @@ namespace Bit.App.Resources {
}
}
+ public static string FullName {
+ get {
+ return ResourceManager.GetString("FullName", resourceCulture);
+ }
+ }
+
public static string LicenseNumber {
get {
return ResourceManager.GetString("LicenseNumber", resourceCulture);
@@ -2025,6 +2033,12 @@ namespace Bit.App.Resources {
}
}
+ public static string FieldTypeLinked {
+ get {
+ return ResourceManager.GetString("FieldTypeLinked", resourceCulture);
+ }
+ }
+
public static string FieldTypeText {
get {
return ResourceManager.GetString("FieldTypeText", resourceCulture);
diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx
index 5a3dd740c..8d0047675 100644
--- a/src/App/Resources/AppResources.resx
+++ b/src/App/Resources/AppResources.resx
@@ -1059,6 +1059,9 @@
Last Name
+
+ Full Name
+
License Number
@@ -1183,6 +1186,9 @@
Hidden
+
+ Linked
+
Text
diff --git a/src/Core/Enums/FieldType.cs b/src/Core/Enums/FieldType.cs
index bf30d4f12..5eef485b7 100644
--- a/src/Core/Enums/FieldType.cs
+++ b/src/Core/Enums/FieldType.cs
@@ -4,6 +4,7 @@
{
Text = 0,
Hidden = 1,
- Boolean = 2
+ Boolean = 2,
+ Linked = 3,
}
}
diff --git a/src/Core/Enums/LinkedIdType.cs b/src/Core/Enums/LinkedIdType.cs
new file mode 100644
index 000000000..32803868c
--- /dev/null
+++ b/src/Core/Enums/LinkedIdType.cs
@@ -0,0 +1,38 @@
+namespace Bit.Core.Enums {
+
+ public enum LinkedIdType: int
+ {
+ // Login
+ Login_Username = 100,
+ Login_Password = 101,
+
+ // Card
+ Card_CardholderName = 300,
+ Card_ExpMonth = 301,
+ Card_ExpYear = 302,
+ Card_Code = 303,
+ Card_Brand = 304,
+ Card_Number = 305,
+
+ // Identity
+ Identity_Title = 400,
+ Identity_MiddleName = 401,
+ Identity_Address1 = 402,
+ Identity_Address2 = 403,
+ Identity_Address3 = 404,
+ Identity_City = 405,
+ Identity_State = 406,
+ Identity_PostalCode = 407,
+ Identity_Country = 408,
+ Identity_Company = 409,
+ Identity_Email = 410,
+ Identity_Phone = 411,
+ Identity_Ssn = 412,
+ Identity_Username = 413,
+ Identity_PassportNumber = 414,
+ Identity_LicenseNumber = 415,
+ Identity_FirstName = 416,
+ Identity_LastName = 417,
+ Identity_FullName = 418,
+ }
+}
diff --git a/src/Core/Models/Api/FieldApi.cs b/src/Core/Models/Api/FieldApi.cs
index 8fca5ac29..524d13ecf 100644
--- a/src/Core/Models/Api/FieldApi.cs
+++ b/src/Core/Models/Api/FieldApi.cs
@@ -7,5 +7,6 @@ namespace Bit.Core.Models.Api
public FieldType Type { get; set; }
public string Name { get; set; }
public string Value { get; set; }
+ public LinkedIdType? LinkedId { get; set; }
}
}
diff --git a/src/Core/Models/Data/FieldData.cs b/src/Core/Models/Data/FieldData.cs
index 9e7bf67e9..bf898e156 100644
--- a/src/Core/Models/Data/FieldData.cs
+++ b/src/Core/Models/Data/FieldData.cs
@@ -12,10 +12,12 @@ namespace Bit.Core.Models.Data
Type = data.Type;
Name = data.Name;
Value = data.Value;
+ LinkedId = data.LinkedId;
}
public FieldType Type { get; set; }
public string Name { get; set; }
public string Value { get; set; }
+ public LinkedIdType? LinkedId { get; set; }
}
}
diff --git a/src/Core/Models/Domain/Field.cs b/src/Core/Models/Domain/Field.cs
index 061a6409c..31f5d4340 100644
--- a/src/Core/Models/Domain/Field.cs
+++ b/src/Core/Models/Domain/Field.cs
@@ -19,12 +19,14 @@ namespace Bit.Core.Models.Domain
public Field(FieldData obj, bool alreadyEncrypted = false)
{
Type = obj.Type;
+ LinkedId = obj.LinkedId;
BuildDomainModel(this, obj, _map, alreadyEncrypted);
}
public EncString Name { get; set; }
public EncString Value { get; set; }
public FieldType Type { get; set; }
+ public LinkedIdType? LinkedId { get; set; }
public Task DecryptAsync(string orgId)
{
@@ -38,10 +40,12 @@ namespace Bit.Core.Models.Domain
{
"Name",
"Value",
- "Type"
+ "Type",
+ "LinkedId"
}, new HashSet
{
- "Type"
+ "Type",
+ "LinkedId"
});
return f;
}
diff --git a/src/Core/Models/Request/CipherRequest.cs b/src/Core/Models/Request/CipherRequest.cs
index 3e6e1f8e8..ca00ee182 100644
--- a/src/Core/Models/Request/CipherRequest.cs
+++ b/src/Core/Models/Request/CipherRequest.cs
@@ -81,7 +81,8 @@ namespace Bit.Core.Models.Request
{
Type = f.Type,
Name = f.Name?.EncryptedString,
- Value = f.Value?.EncryptedString
+ Value = f.Value?.EncryptedString,
+ LinkedId = f.LinkedId,
}).ToList();
PasswordHistory = cipher.PasswordHistory?.Select(ph => new PasswordHistoryRequest
diff --git a/src/Core/Models/View/CardView.cs b/src/Core/Models/View/CardView.cs
index cb92ae161..421d0b712 100644
--- a/src/Core/Models/View/CardView.cs
+++ b/src/Core/Models/View/CardView.cs
@@ -1,9 +1,11 @@
using Bit.Core.Models.Domain;
+using Bit.Core.Enums;
+using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Bit.Core.Models.View
{
- public class CardView : View
+ public class CardView : ItemView
{
private string _brand;
private string _number;
@@ -40,7 +42,7 @@ namespace Bit.Core.Models.View
}
}
- public string SubTitle
+ public override string SubTitle
{
get
{
@@ -82,6 +84,19 @@ namespace Bit.Core.Models.View
}
}
+ public override List> LinkedFieldOptions
+ {
+ get => new List>()
+ {
+ new KeyValuePair("CardholderName", LinkedIdType.Card_CardholderName),
+ new KeyValuePair("ExpirationMonth", LinkedIdType.Card_ExpMonth),
+ new KeyValuePair("ExpirationYear", LinkedIdType.Card_ExpYear),
+ new KeyValuePair("SecurityCode", LinkedIdType.Card_Code),
+ new KeyValuePair("Brand", LinkedIdType.Card_Brand),
+ new KeyValuePair("Number", LinkedIdType.Card_Number),
+ };
+ }
+
private string FormatYear(string year)
{
return year.Length == 2 ? string.Concat("20", year) : year;
diff --git a/src/Core/Models/View/CipherView.cs b/src/Core/Models/View/CipherView.cs
index f28328140..1d238f271 100644
--- a/src/Core/Models/View/CipherView.cs
+++ b/src/Core/Models/View/CipherView.cs
@@ -50,20 +50,20 @@ namespace Bit.Core.Models.View
public DateTime? DeletedDate { get; set; }
public CipherRepromptType Reprompt { get; set; }
- public string SubTitle
+ public ItemView Item
{
get
{
switch (Type)
{
case CipherType.Login:
- return Login.SubTitle;
+ return Login;
case CipherType.SecureNote:
- return SecureNote.SubTitle;
+ return SecureNote;
case CipherType.Card:
- return Card.SubTitle;
+ return Card;
case CipherType.Identity:
- return Identity.SubTitle;
+ return Identity;
default:
break;
}
@@ -71,6 +71,8 @@ namespace Bit.Core.Models.View
}
}
+ public List> LinkedFieldOptions => Item.LinkedFieldOptions;
+ public string SubTitle => Item.SubTitle;
public bool Shared => OrganizationId != null;
public bool HasPasswordHistory => PasswordHistory?.Any() ?? false;
public bool HasAttachments => Attachments?.Any() ?? false;
@@ -102,5 +104,11 @@ namespace Bit.Core.Models.View
}
}
public bool IsDeleted => DeletedDate.HasValue;
+
+ public string LinkedFieldI18nKey(LinkedIdType id)
+ {
+ return LinkedFieldOptions.Find(lfo => lfo.Value == id).Key;
+ }
+
}
}
diff --git a/src/Core/Models/View/FieldView.cs b/src/Core/Models/View/FieldView.cs
index 307112564..2c3e0ba82 100644
--- a/src/Core/Models/View/FieldView.cs
+++ b/src/Core/Models/View/FieldView.cs
@@ -10,6 +10,7 @@ namespace Bit.Core.Models.View
public FieldView(Field f)
{
Type = f.Type;
+ LinkedId = f.LinkedId;
}
public string Name { get; set; }
@@ -17,5 +18,6 @@ namespace Bit.Core.Models.View
public FieldType Type { get; set; }
public string MaskedValue => Value != null ? "••••••••" : null;
public bool NewField { get; set; }
+ public LinkedIdType? LinkedId { get; set; }
}
}
diff --git a/src/Core/Models/View/IdentityView.cs b/src/Core/Models/View/IdentityView.cs
index a6de1f86a..d4dae2922 100644
--- a/src/Core/Models/View/IdentityView.cs
+++ b/src/Core/Models/View/IdentityView.cs
@@ -1,8 +1,10 @@
using Bit.Core.Models.Domain;
+using Bit.Core.Enums;
+using System.Collections.Generic;
namespace Bit.Core.Models.View
{
- public class IdentityView : View
+ public class IdentityView : ItemView
{
private string _firstName;
private string _lastName;
@@ -47,7 +49,7 @@ namespace Bit.Core.Models.View
public string PassportNumber { get; set; }
public string LicenseNumber { get; set; }
- public string SubTitle
+ public override string SubTitle
{
get
{
@@ -141,5 +143,31 @@ namespace Bit.Core.Models.View
return string.Format("{0}, {1}, {2}", city, state, postalCode);
}
}
+
+ public override List> LinkedFieldOptions
+ {
+ get => new List>()
+ {
+ new KeyValuePair("Title", LinkedIdType.Identity_Title),
+ new KeyValuePair("MiddleName", LinkedIdType.Identity_MiddleName),
+ new KeyValuePair("Address1", LinkedIdType.Identity_Address1),
+ new KeyValuePair("Address2", LinkedIdType.Identity_Address2),
+ new KeyValuePair("Address3", LinkedIdType.Identity_Address3),
+ new KeyValuePair("CityTown", LinkedIdType.Identity_City),
+ new KeyValuePair("StateProvince", LinkedIdType.Identity_State),
+ new KeyValuePair("ZipPostalCode", LinkedIdType.Identity_PostalCode),
+ new KeyValuePair("Country", LinkedIdType.Identity_Country),
+ new KeyValuePair("Company", LinkedIdType.Identity_Company),
+ new KeyValuePair("Email", LinkedIdType.Identity_Email),
+ new KeyValuePair("Phone", LinkedIdType.Identity_Phone),
+ new KeyValuePair("SSN", LinkedIdType.Identity_Ssn),
+ new KeyValuePair("Username", LinkedIdType.Identity_Username),
+ new KeyValuePair("PassportNumber", LinkedIdType.Identity_PassportNumber),
+ new KeyValuePair("LicenseNumber", LinkedIdType.Identity_LicenseNumber),
+ new KeyValuePair("FirstName", LinkedIdType.Identity_FirstName),
+ new KeyValuePair("LastName", LinkedIdType.Identity_LastName),
+ new KeyValuePair("FullName", LinkedIdType.Identity_FullName),
+ };
+ }
}
}
diff --git a/src/Core/Models/View/ItemView.cs b/src/Core/Models/View/ItemView.cs
new file mode 100644
index 000000000..e0e6fba13
--- /dev/null
+++ b/src/Core/Models/View/ItemView.cs
@@ -0,0 +1,14 @@
+using System.Collections.Generic;
+using Bit.Core.Enums;
+
+namespace Bit.Core.Models.View
+{
+ public abstract class ItemView : View
+ {
+ public ItemView() { }
+
+ public abstract string SubTitle { get; }
+
+ public abstract List> LinkedFieldOptions { get; }
+ }
+}
diff --git a/src/Core/Models/View/LoginView.cs b/src/Core/Models/View/LoginView.cs
index e0d0ba3b5..595c69928 100644
--- a/src/Core/Models/View/LoginView.cs
+++ b/src/Core/Models/View/LoginView.cs
@@ -1,11 +1,12 @@
using Bit.Core.Models.Domain;
+using Bit.Core.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Bit.Core.Models.View
{
- public class LoginView : View
+ public class LoginView : ItemView
{
public LoginView() { }
@@ -21,9 +22,18 @@ namespace Bit.Core.Models.View
public List Uris { get; set; }
public string Uri => HasUris ? Uris[0].Uri : null;
public string MaskedPassword => Password != null ? "••••••••" : null;
- public string SubTitle => Username;
+ public override string SubTitle => Username;
public bool CanLaunch => HasUris && Uris.Any(u => u.CanLaunch);
public string LaunchUri => HasUris ? Uris.FirstOrDefault(u => u.CanLaunch)?.LaunchUri : null;
public bool HasUris => (Uris?.Count ?? 0) > 0;
+
+ public override List> LinkedFieldOptions
+ {
+ get => new List>()
+ {
+ new KeyValuePair("Username", LinkedIdType.Login_Username),
+ new KeyValuePair("Password", LinkedIdType.Login_Password),
+ };
+ }
}
}
diff --git a/src/Core/Models/View/SecureNoteView.cs b/src/Core/Models/View/SecureNoteView.cs
index 694f8ba6e..67f3818df 100644
--- a/src/Core/Models/View/SecureNoteView.cs
+++ b/src/Core/Models/View/SecureNoteView.cs
@@ -1,9 +1,10 @@
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
+using System.Collections.Generic;
namespace Bit.Core.Models.View
{
- public class SecureNoteView : View
+ public class SecureNoteView : ItemView
{
public SecureNoteView() { }
@@ -13,6 +14,7 @@ namespace Bit.Core.Models.View
}
public SecureNoteType Type { get; set; }
- public string SubTitle => null;
+ public override string SubTitle => null;
+ public override List> LinkedFieldOptions => null;
}
}
diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs
index 72b102eea..a0d145709 100644
--- a/src/Core/Services/CipherService.cs
+++ b/src/Core/Services/CipherService.cs
@@ -1190,7 +1190,8 @@ namespace Bit.Core.Services
{
var field = new Field
{
- Type = model.Type
+ Type = model.Type,
+ LinkedId = model.LinkedId,
};
// normalize boolean type field values
if (model.Type == FieldType.Boolean && model.Value != "true")