Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Changelog

## 28.2.0
- 1Password: added `VaultItem.CreatedAt` and `VaultItem.UpdatedAt` properties

## 28.1.0
- 1Password: fixed incorrect item and vault IDs detection in Client.GetItem
- 1Password: added `NoItem.Inaccessible` for inaccessible vaults and items
Expand Down
6 changes: 6 additions & 0 deletions src/OnePassword/Response.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,12 @@ internal class VaultItem
[JsonProperty("trashed", Required = Required.Always)]
public readonly string Deleted;

[JsonProperty("createdAt")]
public readonly string CreatedAt;

[JsonProperty("updatedAt")]
public readonly string UpdatedAt;

[JsonProperty("itemVersion", Required = Required.Always)]
public readonly int Version;

Expand Down
18 changes: 18 additions & 0 deletions src/OnePassword/VaultItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#nullable enable

using System;
using System.Collections.Generic;
using R = PasswordManagerAccess.OnePassword.Response;

Expand All @@ -17,6 +18,8 @@ public class VaultItem
public string Name => Overview.Title ?? "";
public string Description => Overview.AdditionalInfo ?? "";
public string Note => Details.Note ?? "";
public DateTime CreatedAt => _createdAt ??= ParseDateTime(_itemInfo.CreatedAt);
public DateTime UpdatedAt => _updatedAt ??= ParseDateTime(_itemInfo.UpdatedAt);

public Field[] Fields => _fields ??= ParseFields();

Expand Down Expand Up @@ -72,6 +75,19 @@ internal string FindField(string name)
return "";
}

internal static DateTime ParseDateTime(string dateString)
{
if (string.IsNullOrWhiteSpace(dateString))
return default;

if (DateTime.TryParse(dateString, System.Globalization.CultureInfo.InvariantCulture,
System.Globalization.DateTimeStyles.RoundtripKind, out var result))
return result;

// Return default if parsing fails
return default;
}

//
// Private
//
Expand All @@ -81,4 +97,6 @@ internal string FindField(string name)
private R.VaultItemOverview? _overview;
private R.VaultItemDetails? _details;
private Field[]? _fields;
private DateTime? _createdAt;
private DateTime? _updatedAt;
}
2 changes: 1 addition & 1 deletion src/PasswordManagerAccess.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Company>detunized.net</Company>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<!-- TODO: This needs to be either updated every time we release a new version or set in the CI -->
<AssemblyVersion>28.1.0</AssemblyVersion>
<AssemblyVersion>28.2.0</AssemblyVersion>
</PropertyGroup>

<!-- USE_MITM_PROXY could be set to 1 in the global MSBuild settings in Rider IDE -->
Expand Down
2 changes: 2 additions & 0 deletions test/OnePassword/ClientTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ public void GetItem_returns_account()
// Assert
result.TryPickT0(out var account, out _).ShouldBeTrue();
account.Id.ShouldBe("wm3uxq4xsmb4mghxw6o3s7zrem");
account.CreatedAt.ShouldBe(new DateTime(2016, 8, 4, 13, 15, 10, DateTimeKind.Utc));
account.UpdatedAt.ShouldBe(new DateTime(2016, 8, 4, 13, 16, 7, DateTimeKind.Utc));
}

[Fact]
Expand Down
99 changes: 99 additions & 0 deletions test/OnePassword/ResponseTest.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (C) Dmitry Yakimenko (detunized@gmail.com).
// Licensed under the terms of the MIT license. See LICENCE for details.

using System;
using Newtonsoft.Json;
using PasswordManagerAccess.OnePassword;
using Xunit;
using R = PasswordManagerAccess.OnePassword.Response;

Expand All @@ -18,5 +20,102 @@ public void VaultItemDetails_parses_with_all_types_of_fields()
Assert.Equal(6, details.Sections[0].Fields.Length);
Assert.Equal(6, details.Sections[1].Fields.Length);
}

[Fact]
public void VaultItem_parses_createdAt_and_updatedAt()
{
var json = GetFixture("get-vault-item-response");
var response = JsonConvert.DeserializeObject<R.SingleVaultItem>(json);
Assert.Equal("2016-08-04T13:15:10Z", response.Item.CreatedAt);
Assert.Equal("2016-08-04T13:16:07Z", response.Item.UpdatedAt);
}

[Fact]
public void VaultItem_handles_missing_createdAt_and_updatedAt()
{
var json = @"{
""uuid"": ""test-id"",
""templateUuid"": ""001"",
""trashed"": ""N"",
""itemVersion"": 1,
""encryptedBy"": ""test-key"",
""encOverview"": {
""kid"": ""test-key"",
""enc"": ""A256GCM"",
""cty"": ""b5+jwk+json"",
""iv"": ""test-iv"",
""data"": ""test-data""
},
""encDetails"": {
""kid"": ""test-key"",
""enc"": ""A256GCM"",
""cty"": ""b5+jwk+json"",
""iv"": ""test-iv"",
""data"": ""test-data""
}
}";
var item = JsonConvert.DeserializeObject<R.VaultItem>(json);
Assert.Null(item.CreatedAt);
Assert.Null(item.UpdatedAt);
}

[Fact]
public void VaultItem_handles_invalid_createdAt_and_updatedAt()
{
var json = @"{
""uuid"": ""test-id"",
""templateUuid"": ""001"",
""trashed"": ""N"",
""createdAt"": ""not-a-date"",
""updatedAt"": ""also-not-a-date"",
""itemVersion"": 1,
""encryptedBy"": ""test-key"",
""encOverview"": {
""kid"": ""test-key"",
""enc"": ""A256GCM"",
""cty"": ""b5+jwk+json"",
""iv"": ""test-iv"",
""data"": ""test-data""
},
""encDetails"": {
""kid"": ""test-key"",
""enc"": ""A256GCM"",
""cty"": ""b5+jwk+json"",
""iv"": ""test-iv"",
""data"": ""test-data""
}
}";
var item = JsonConvert.DeserializeObject<R.VaultItem>(json);
Assert.Equal("not-a-date", item.CreatedAt);
Assert.Equal("also-not-a-date", item.UpdatedAt);
}

[Fact]
public void VaultItem_ParseDateTime_parses_valid_date()
{
var result = VaultItem.ParseDateTime("2016-08-04T13:15:10Z");
Assert.Equal(new DateTime(2016, 8, 4, 13, 15, 10, DateTimeKind.Utc), result);
}

[Fact]
public void VaultItem_ParseDateTime_returns_default_for_null()
{
var result = VaultItem.ParseDateTime(null);
Assert.Equal(default(DateTime), result);
}

[Fact]
public void VaultItem_ParseDateTime_returns_default_for_empty()
{
var result = VaultItem.ParseDateTime("");
Assert.Equal(default(DateTime), result);
}

[Fact]
public void VaultItem_ParseDateTime_returns_default_for_invalid()
{
var result = VaultItem.ParseDateTime("not-a-date");
Assert.Equal(default(DateTime), result);
}
}
}