diff --git a/src/App/Pages/Vault/CipherAddEditPage.xaml b/src/App/Pages/Vault/CipherAddEditPage.xaml index ec28d128a..7785457b0 100644 --- a/src/App/Pages/Vault/CipherAddEditPage.xaml +++ b/src/App/Pages/Vault/CipherAddEditPage.xaml @@ -12,6 +12,7 @@ xmlns:dts="clr-namespace:Bit.App.Lists.DataTemplateSelectors" xmlns:il="clr-namespace:Bit.App.Lists.ItemLayouts.CustomFields" xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore" + xmlns:appResources="clr-namespace:Bit.App.Resources" x:DataType="pages:CipherAddEditPageViewModel" x:Name="_page" Title="{Binding PageTitle}"> @@ -28,6 +29,8 @@ + + diff --git a/src/App/Pages/Vault/CipherDetailsPage.xaml b/src/App/Pages/Vault/CipherDetailsPage.xaml index e95c2add4..467be7a77 100644 --- a/src/App/Pages/Vault/CipherDetailsPage.xaml +++ b/src/App/Pages/Vault/CipherDetailsPage.xaml @@ -11,6 +11,7 @@ xmlns:dts="clr-namespace:Bit.App.Lists.DataTemplateSelectors" xmlns:il="clr-namespace:Bit.App.Lists.ItemLayouts.CustomFields" xmlns:core="clr-namespace:Bit.Core;assembly=BitwardenCore" + xmlns:appResources="clr-namespace:Bit.App.Resources" x:DataType="pages:CipherDetailsPageViewModel" x:Name="_page" Title="{Binding PageTitle}"> @@ -23,6 +24,7 @@ + + IsVisible="{Binding Cipher.Login.MainFido2Credential, Converter={StaticResource notNull}, FallbackValue=False}" /> diff --git a/src/App/Utilities/DateTimeConverter.cs b/src/App/Utilities/DateTimeConverter.cs index cd8f2cec4..499429028 100644 --- a/src/App/Utilities/DateTimeConverter.cs +++ b/src/App/Utilities/DateTimeConverter.cs @@ -7,6 +7,8 @@ namespace Bit.App.Utilities { public class DateTimeConverter : IValueConverter { + public string Format { get; set; } = "{0} {1}"; + private readonly ILocalizeService _localizeService; public DateTimeConverter() @@ -26,7 +28,7 @@ namespace Bit.App.Utilities return string.Empty; } var d = ((DateTime)value).ToLocalTime(); - return string.Format("{0} {1}", + return string.Format(Format, _localizeService.GetLocaleShortDate(d), _localizeService.GetLocaleShortTime(d)); } diff --git a/src/Core/Models/Api/Fido2CredentialApi.cs b/src/Core/Models/Api/Fido2CredentialApi.cs index 8ebc63dbf..7953e06a1 100644 --- a/src/Core/Models/Api/Fido2CredentialApi.cs +++ b/src/Core/Models/Api/Fido2CredentialApi.cs @@ -1,4 +1,5 @@ -using Bit.Core.Models.Domain; +using System; +using Bit.Core.Models.Domain; namespace Bit.Core.Models.Api { @@ -21,6 +22,7 @@ namespace Bit.Core.Models.Api UserHandle = fido2Key.UserHandle?.EncryptedString; UserName = fido2Key.UserName?.EncryptedString; Counter = fido2Key.Counter?.EncryptedString; + CreationDate = fido2Key.CreationDate; } public string CredentialId { get; set; } @@ -34,5 +36,6 @@ namespace Bit.Core.Models.Api public string UserHandle { get; set; } public string UserName { get; set; } public string Counter { get; set; } + public DateTime CreationDate { get; set; } } } diff --git a/src/Core/Models/Data/Fido2CredentialData.cs b/src/Core/Models/Data/Fido2CredentialData.cs index 2e63e7aa5..846df59f4 100644 --- a/src/Core/Models/Data/Fido2CredentialData.cs +++ b/src/Core/Models/Data/Fido2CredentialData.cs @@ -1,4 +1,5 @@ -using Bit.Core.Models.Api; +using System; +using Bit.Core.Models.Api; namespace Bit.Core.Models.Data { @@ -19,6 +20,7 @@ namespace Bit.Core.Models.Data UserHandle = apiData.UserHandle; UserName = apiData.UserName; Counter = apiData.Counter; + CreationDate = apiData.CreationDate; } public string CredentialId { get; set; } @@ -32,5 +34,6 @@ namespace Bit.Core.Models.Data public string UserHandle { get; set; } public string UserName { get; set; } public string Counter { get; set; } + public DateTime CreationDate { get; set; } } } diff --git a/src/Core/Models/Domain/Fido2Credential.cs b/src/Core/Models/Domain/Fido2Credential.cs index b606aa0dd..7c6928204 100644 --- a/src/Core/Models/Domain/Fido2Credential.cs +++ b/src/Core/Models/Domain/Fido2Credential.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using Bit.Core.Models.Data; using Bit.Core.Models.View; @@ -7,7 +9,7 @@ namespace Bit.Core.Models.Domain { public class Fido2Credential : Domain { - public static HashSet EncryptableProperties => new HashSet + public static HashSet EncryptablePropertiesToMap => new HashSet { nameof(CredentialId), nameof(Discoverable), @@ -22,11 +24,18 @@ namespace Bit.Core.Models.Domain nameof(Counter) }; + public static HashSet NonEncryptablePropertiesToMap => new HashSet + { + nameof(CreationDate) + }; + + public static HashSet AllPropertiesToMap => new HashSet(EncryptablePropertiesToMap.Concat(NonEncryptablePropertiesToMap)); + public Fido2Credential() { } public Fido2Credential(Fido2CredentialData data, bool alreadyEncrypted = false) { - BuildDomainModel(this, data, EncryptableProperties, alreadyEncrypted); + BuildDomainModel(this, data, AllPropertiesToMap, alreadyEncrypted, NonEncryptablePropertiesToMap); } public EncString CredentialId { get; set; } @@ -40,16 +49,17 @@ namespace Bit.Core.Models.Domain public EncString UserHandle { get; set; } public EncString UserName { get; set; } public EncString Counter { get; set; } + public DateTime CreationDate { get; set; } public async Task DecryptAsync(string orgId, SymmetricCryptoKey key = null) { - return await DecryptObjAsync(new Fido2CredentialView(), this, EncryptableProperties, orgId, key); + return await DecryptObjAsync(new Fido2CredentialView(this), this, EncryptablePropertiesToMap, orgId, key); } public Fido2CredentialData ToFido2CredentialData() { var data = new Fido2CredentialData(); - BuildDataModel(this, data, EncryptableProperties); + BuildDataModel(this, data, AllPropertiesToMap, NonEncryptablePropertiesToMap); return data; } } diff --git a/src/Core/Models/View/Fido2CredentialView.cs b/src/Core/Models/View/Fido2CredentialView.cs index 390a3bf7d..049d82047 100644 --- a/src/Core/Models/View/Fido2CredentialView.cs +++ b/src/Core/Models/View/Fido2CredentialView.cs @@ -1,10 +1,21 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using Bit.Core.Enums; +using Bit.Core.Models.Domain; namespace Bit.Core.Models.View { public class Fido2CredentialView : ItemView, ILaunchableView { + public Fido2CredentialView() + { + } + + public Fido2CredentialView(Fido2Credential fido2Credential) + { + CreationDate = fido2Credential.CreationDate; + } + public string CredentialId { get; set; } public string Discoverable { get; set; } public string KeyType { get; set; } = Constants.DefaultFido2CredentialType; @@ -16,6 +27,7 @@ namespace Bit.Core.Models.View public string UserHandle { get; set; } public string UserName { get; set; } public string Counter { get; set; } + public DateTime CreationDate { get; set; } public override string SubTitle => UserName; public override List> LinkedFieldOptions => new List>(); diff --git a/src/Core/Services/CipherService.cs b/src/Core/Services/CipherService.cs index e3d59d195..c17c172e7 100644 --- a/src/Core/Services/CipherService.cs +++ b/src/Core/Services/CipherService.cs @@ -1161,8 +1161,11 @@ namespace Bit.Core.Services cipher.Login.Fido2Credentials = new List(); foreach (var fido2Credential in model.Login.Fido2Credentials) { - var fido2CredentialDomain = new Fido2Credential(); - await EncryptObjPropertyAsync(fido2Credential, fido2CredentialDomain, Fido2Credential.EncryptableProperties, key); + var fido2CredentialDomain = new Fido2Credential + { + CreationDate = fido2Credential.CreationDate + }; + await EncryptObjPropertyAsync(fido2Credential, fido2CredentialDomain, Fido2Credential.EncryptablePropertiesToMap, key); cipher.Login.Fido2Credentials.Add(fido2CredentialDomain); } }