From f0f77f4c3a13b4300ce196b1301dcf2526f36a34 Mon Sep 17 00:00:00 2001 From: Thomas Avery Date: Fri, 5 Jun 2026 10:09:11 -0500 Subject: [PATCH 1/3] Add timestamp for LastPasswordChangeDate when PasswordChangeAndRotateUserAccountKeysAsync changes password --- .../UserKey/Implementations/RotateUserAccountKeysCommand.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Core/KeyManagement/UserKey/Implementations/RotateUserAccountKeysCommand.cs b/src/Core/KeyManagement/UserKey/Implementations/RotateUserAccountKeysCommand.cs index 742c92aeae36..b74e0346b33d 100644 --- a/src/Core/KeyManagement/UserKey/Implementations/RotateUserAccountKeysCommand.cs +++ b/src/Core/KeyManagement/UserKey/Implementations/RotateUserAccountKeysCommand.cs @@ -93,6 +93,7 @@ public async Task PasswordChangeAndRotateUserAccountKeysAsync(Us user.Key = model.MasterPasswordUnlockData.MasterKeyWrappedUserKey; user.MasterPassword = _passwordHasher.HashPassword(user, model.MasterPasswordAuthenticationData.MasterPasswordAuthenticationHash); user.MasterPasswordHint = model.MasterPasswordHint; + user.LastPasswordChangeDate = DateTime.UtcNow; await _userRepository.UpdateUserKeyAndEncryptedDataV2Async(user, saveEncryptedDataActions); From 8961640c1bfc87f702330659fce4f45b99ef0cd8 Mon Sep 17 00:00:00 2001 From: Thomas Avery Date: Fri, 5 Jun 2026 11:25:05 -0500 Subject: [PATCH 2/3] Fix EF --- .../Repositories/UserRepository.cs | 1 + .../UserKey/RotateUserAccountKeysCommandTests.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs index dab556e72f0c..982dcc3f165d 100644 --- a/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs +++ b/src/Infrastructure.EntityFramework/Repositories/UserRepository.cs @@ -249,6 +249,7 @@ public async Task UpdateUserKeyAndEncryptedDataV2Async(Core.Entities.User user, userEntity.MasterPassword = user.MasterPassword; userEntity.MasterPasswordHint = user.MasterPasswordHint; + userEntity.LastPasswordChangeDate = user.LastPasswordChangeDate; userEntity.LastKeyRotationDate = user.LastKeyRotationDate; userEntity.AccountRevisionDate = user.AccountRevisionDate; diff --git a/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs b/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs index 46acfe9e3795..78c2b8d804b0 100644 --- a/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs +++ b/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs @@ -131,8 +131,15 @@ public async Task PasswordChangeAndRotateUserAccountKeysAsync_V1_Success(SutProv sutProvider.GetDependency().CheckPasswordAsync(user, model.OldMasterKeyAuthenticationHash) .Returns(true); + var before = DateTime.UtcNow; var result = await sutProvider.Sut.PasswordChangeAndRotateUserAccountKeysAsync(user, model); + var after = DateTime.UtcNow; + Assert.Equal(IdentityResult.Success, result); + Assert.NotNull(user.LastPasswordChangeDate); + Assert.InRange(user.LastPasswordChangeDate.Value, before, after); + Assert.NotNull(user.LastKeyRotationDate); + Assert.InRange(user.LastKeyRotationDate.Value, before, after); } [Theory, BitAutoData] From 1aa30b82e1d5cfeae89a6d7b8900499ef831fe76 Mon Sep 17 00:00:00 2001 From: Thomas Avery Date: Wed, 10 Jun 2026 14:15:15 -0500 Subject: [PATCH 3/3] Update unit tests --- .../UserKey/RotateUserAccountKeysCommandTests.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs b/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs index 78c2b8d804b0..629496ed50d5 100644 --- a/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs +++ b/test/Core.Test/KeyManagement/UserKey/RotateUserAccountKeysCommandTests.cs @@ -127,6 +127,8 @@ public async Task PasswordChangeAndRotateUserAccountKeysAsync_V1_Success(SutProv var signatureRepository = sutProvider.GetDependency(); SetV1ExistingUser(user, signatureRepository); SetV1ModelUser(model.BaseData); + user.LastPasswordChangeDate = DateTime.UtcNow.AddDays(-1); + user.LastKeyRotationDate = DateTime.UtcNow.AddDays(-1); sutProvider.GetDependency().CheckPasswordAsync(user, model.OldMasterKeyAuthenticationHash) .Returns(true); @@ -154,9 +156,16 @@ public async Task PasswordChangeAndRotateUserAccountKeysAsync_UpgradeV1ToV2_Succ sutProvider.GetDependency().CheckPasswordAsync(user, model.OldMasterKeyAuthenticationHash) .Returns(true); + var before = DateTime.UtcNow; var result = await sutProvider.Sut.PasswordChangeAndRotateUserAccountKeysAsync(user, model); + var after = DateTime.UtcNow; + Assert.Equal(IdentityResult.Success, result); Assert.Equal(user.SecurityState, model.BaseData.AccountKeys.SecurityStateData!.SecurityState); + Assert.NotNull(user.LastPasswordChangeDate); + Assert.InRange(user.LastPasswordChangeDate.Value, before, after); + Assert.NotNull(user.LastKeyRotationDate); + Assert.InRange(user.LastKeyRotationDate.Value, before, after); }