From 7f9d7c0c5d5e04248e03fd5f9f524d71679419b2 Mon Sep 17 00:00:00 2001 From: Todd Martin <106564991+trmartin4@users.noreply.github.com> Date: Thu, 9 May 2024 13:24:02 -0400 Subject: [PATCH] [PM-7029] Remove conditional logic for KeyRotationImprovements feature flag (#4002) * Removed business logic that references flag * Removed using statement. * Undid accidental keystroke. * Removed unused method. * Removed unused imports. --- .../Auth/Controllers/AccountsController.cs | 63 ++----- src/Core/Services/IUserService.cs | 4 - .../Services/Implementations/UserService.cs | 35 ---- .../Vault/Repositories/ICipherRepository.cs | 2 - .../Vault/Repositories/CipherRepository.cs | 164 ------------------ .../Vault/Repositories/CipherRepository.cs | 18 -- 6 files changed, 11 insertions(+), 275 deletions(-) diff --git a/src/Api/Auth/Controllers/AccountsController.cs b/src/Api/Auth/Controllers/AccountsController.cs index 28135c471..da76f3540 100644 --- a/src/Api/Auth/Controllers/AccountsController.cs +++ b/src/Api/Auth/Controllers/AccountsController.cs @@ -43,7 +43,6 @@ using Bit.Core.Utilities; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Repositories; using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.Auth.Controllers; @@ -438,59 +437,19 @@ public class AccountsController : Controller throw new UnauthorizedAccessException(); } - IdentityResult result; - if (_featureService.IsEnabled(FeatureFlagKeys.KeyRotationImprovements)) + var dataModel = new RotateUserKeyData { - var dataModel = new RotateUserKeyData - { - MasterPasswordHash = model.MasterPasswordHash, - Key = model.Key, - PrivateKey = model.PrivateKey, - Ciphers = await _cipherValidator.ValidateAsync(user, model.Ciphers), - Folders = await _folderValidator.ValidateAsync(user, model.Folders), - Sends = await _sendValidator.ValidateAsync(user, model.Sends), - EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.EmergencyAccessKeys), - OrganizationUsers = await _organizationUserValidator.ValidateAsync(user, model.ResetPasswordKeys) - }; - - result = await _rotateUserKeyCommand.RotateUserKeyAsync(user, dataModel); - } - else - { - var ciphers = new List(); - if (model.Ciphers.Any()) - { - var existingCiphers = await _cipherRepository.GetManyByUserIdAsync(user.Id, useFlexibleCollections: UseFlexibleCollections); - ciphers.AddRange(existingCiphers - .Join(model.Ciphers, c => c.Id, c => c.Id, (existing, c) => c.ToCipher(existing))); - } - - var folders = new List(); - if (model.Folders.Any()) - { - var existingFolders = await _folderRepository.GetManyByUserIdAsync(user.Id); - folders.AddRange(existingFolders - .Join(model.Folders, f => f.Id, f => f.Id, (existing, f) => f.ToFolder(existing))); - } - - var sends = new List(); - if (model.Sends?.Any() == true) - { - var existingSends = await _sendRepository.GetManyByUserIdAsync(user.Id); - sends.AddRange(existingSends - .Join(model.Sends, s => s.Id, s => s.Id, (existing, s) => s.ToSend(existing, _sendService))); - } - - result = await _userService.UpdateKeyAsync( - user, - model.MasterPasswordHash, - model.Key, - model.PrivateKey, - ciphers, - folders, - sends); - } + MasterPasswordHash = model.MasterPasswordHash, + Key = model.Key, + PrivateKey = model.PrivateKey, + Ciphers = await _cipherValidator.ValidateAsync(user, model.Ciphers), + Folders = await _folderValidator.ValidateAsync(user, model.Folders), + Sends = await _sendValidator.ValidateAsync(user, model.Sends), + EmergencyAccesses = await _emergencyAccessValidator.ValidateAsync(user, model.EmergencyAccessKeys), + OrganizationUsers = await _organizationUserValidator.ValidateAsync(user, model.ResetPasswordKeys) + }; + var result = await _rotateUserKeyCommand.RotateUserKeyAsync(user, dataModel); if (result.Succeeded) { diff --git a/src/Core/Services/IUserService.cs b/src/Core/Services/IUserService.cs index a8cd537e7..f4751623a 100644 --- a/src/Core/Services/IUserService.cs +++ b/src/Core/Services/IUserService.cs @@ -4,8 +4,6 @@ using Bit.Core.Auth.Models; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Models.Business; -using Bit.Core.Tools.Entities; -using Bit.Core.Vault.Entities; using Fido2NetLib; using Microsoft.AspNetCore.Identity; @@ -39,8 +37,6 @@ public interface IUserService Task UpdateTempPasswordAsync(User user, string newMasterPassword, string key, string hint); Task ChangeKdfAsync(User user, string masterPassword, string newMasterPassword, string key, KdfType kdf, int kdfIterations, int? kdfMemory, int? kdfParallelism); - Task UpdateKeyAsync(User user, string masterPassword, string key, string privateKey, - IEnumerable ciphers, IEnumerable folders, IEnumerable sends); Task RefreshSecurityStampAsync(User user, string masterPasswordHash); Task UpdateTwoFactorProviderAsync(User user, TwoFactorProviderType type, bool setEnabled = true, bool logEvent = true); Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type, diff --git a/src/Core/Services/Implementations/UserService.cs b/src/Core/Services/Implementations/UserService.cs index 80e6aa8ca..148c60a14 100644 --- a/src/Core/Services/Implementations/UserService.cs +++ b/src/Core/Services/Implementations/UserService.cs @@ -14,12 +14,10 @@ using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces; using Bit.Core.Repositories; using Bit.Core.Settings; using Bit.Core.Tokens; -using Bit.Core.Tools.Entities; using Bit.Core.Tools.Enums; using Bit.Core.Tools.Models.Business; using Bit.Core.Tools.Services; using Bit.Core.Utilities; -using Bit.Core.Vault.Entities; using Bit.Core.Vault.Repositories; using Fido2NetLib; using Fido2NetLib.Objects; @@ -862,39 +860,6 @@ public class UserService : UserManager, IUserService, IDisposable return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch()); } - public async Task UpdateKeyAsync(User user, string masterPassword, string key, string privateKey, - IEnumerable ciphers, IEnumerable folders, IEnumerable sends) - { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } - - if (await CheckPasswordAsync(user, masterPassword)) - { - var now = DateTime.UtcNow; - user.RevisionDate = user.AccountRevisionDate = now; - user.LastKeyRotationDate = now; - user.SecurityStamp = Guid.NewGuid().ToString(); - user.Key = key; - user.PrivateKey = privateKey; - if (ciphers.Any() || folders.Any() || sends.Any()) - { - await _cipherRepository.UpdateUserKeysAndCiphersAsync(user, ciphers, folders, sends); - } - else - { - await _userRepository.ReplaceAsync(user); - } - - await _pushService.PushLogOutAsync(user.Id, excludeCurrentContextFromPush: true); - return IdentityResult.Success; - } - - Logger.LogWarning("Update key failed for user {userId}.", user.Id); - return IdentityResult.Failed(_identityErrorDescriber.PasswordMismatch()); - } - public async Task RefreshSecurityStampAsync(User user, string secret) { if (user == null) diff --git a/src/Core/Vault/Repositories/ICipherRepository.cs b/src/Core/Vault/Repositories/ICipherRepository.cs index f801f6f6f..80a1e260d 100644 --- a/src/Core/Vault/Repositories/ICipherRepository.cs +++ b/src/Core/Vault/Repositories/ICipherRepository.cs @@ -1,7 +1,6 @@ using Bit.Core.Auth.UserFeatures.UserKey; using Bit.Core.Entities; using Bit.Core.Repositories; -using Bit.Core.Tools.Entities; using Bit.Core.Vault.Entities; using Bit.Core.Vault.Models.Data; @@ -30,7 +29,6 @@ public interface ICipherRepository : IRepository Task MoveAsync(IEnumerable ids, Guid? folderId, Guid userId, bool useFlexibleCollections); Task DeleteByUserIdAsync(Guid userId); Task DeleteByOrganizationIdAsync(Guid organizationId); - Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable ciphers, IEnumerable folders, IEnumerable sends); Task UpdateCiphersAsync(Guid userId, IEnumerable ciphers); Task CreateAsync(IEnumerable ciphers, IEnumerable folders); Task CreateAsync(IEnumerable ciphers, IEnumerable collections, diff --git a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs index 5bfcac7b5..9e767844e 100644 --- a/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.Dapper/Vault/Repositories/CipherRepository.cs @@ -380,170 +380,6 @@ public class CipherRepository : Repository, ICipherRepository }; } - public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable ciphers, IEnumerable folders, IEnumerable sends) - { - using (var connection = new SqlConnection(ConnectionString)) - { - connection.Open(); - - using (var transaction = connection.BeginTransaction()) - { - try - { - // 1. Update user. - - using (var cmd = new SqlCommand("[dbo].[User_UpdateKeys]", connection, transaction)) - { - cmd.CommandType = CommandType.StoredProcedure; - cmd.Parameters.Add("@Id", SqlDbType.UniqueIdentifier).Value = user.Id; - cmd.Parameters.Add("@SecurityStamp", SqlDbType.NVarChar).Value = user.SecurityStamp; - cmd.Parameters.Add("@Key", SqlDbType.VarChar).Value = user.Key; - - if (string.IsNullOrWhiteSpace(user.PrivateKey)) - { - cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = DBNull.Value; - } - else - { - cmd.Parameters.Add("@PrivateKey", SqlDbType.VarChar).Value = user.PrivateKey; - } - - cmd.Parameters.Add("@RevisionDate", SqlDbType.DateTime2).Value = user.RevisionDate; - cmd.Parameters.Add("@AccountRevisionDate", SqlDbType.DateTime2).Value = user.AccountRevisionDate; - cmd.Parameters.Add("@LastKeyRotationDate", SqlDbType.DateTime2).Value = user.LastKeyRotationDate; - cmd.ExecuteNonQuery(); - } - - // 2. Create temp tables to bulk copy into. - - var sqlCreateTemp = @" - SELECT TOP 0 * - INTO #TempCipher - FROM [dbo].[Cipher] - - SELECT TOP 0 * - INTO #TempFolder - FROM [dbo].[Folder] - - SELECT TOP 0 * - INTO #TempSend - FROM [dbo].[Send]"; - - using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction)) - { - cmd.ExecuteNonQuery(); - } - - // 3. Bulk copy into temp tables. - - if (ciphers.Any()) - { - using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction)) - { - bulkCopy.DestinationTableName = "#TempCipher"; - var dataTable = BuildCiphersTable(bulkCopy, ciphers); - bulkCopy.WriteToServer(dataTable); - } - } - - if (folders.Any()) - { - using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction)) - { - bulkCopy.DestinationTableName = "#TempFolder"; - var dataTable = BuildFoldersTable(bulkCopy, folders); - bulkCopy.WriteToServer(dataTable); - } - } - - if (sends.Any()) - { - using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction)) - { - bulkCopy.DestinationTableName = "#TempSend"; - var dataTable = BuildSendsTable(bulkCopy, sends); - bulkCopy.WriteToServer(dataTable); - } - } - - // 4. Insert into real tables from temp tables and clean up. - - var sql = string.Empty; - - if (ciphers.Any()) - { - sql += @" - UPDATE - [dbo].[Cipher] - SET - [Data] = TC.[Data], - [Attachments] = TC.[Attachments], - [RevisionDate] = TC.[RevisionDate], - [Key] = TC.[Key] - FROM - [dbo].[Cipher] C - INNER JOIN - #TempCipher TC ON C.Id = TC.Id - WHERE - C.[UserId] = @UserId"; - } - - if (folders.Any()) - { - sql += @" - UPDATE - [dbo].[Folder] - SET - [Name] = TF.[Name], - [RevisionDate] = TF.[RevisionDate] - FROM - [dbo].[Folder] F - INNER JOIN - #TempFolder TF ON F.Id = TF.Id - WHERE - F.[UserId] = @UserId"; - } - - if (sends.Any()) - { - sql += @" - UPDATE - [dbo].[Send] - SET - [Key] = TS.[Key], - [RevisionDate] = TS.[RevisionDate] - FROM - [dbo].[Send] S - INNER JOIN - #TempSend TS ON S.Id = TS.Id - WHERE - S.[UserId] = @UserId"; - } - - sql += @" - DROP TABLE #TempCipher - DROP TABLE #TempFolder - DROP TABLE #TempSend"; - - using (var cmd = new SqlCommand(sql, connection, transaction)) - { - cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = user.Id; - cmd.ExecuteNonQuery(); - } - - transaction.Commit(); - } - catch - { - transaction.Rollback(); - throw; - } - } - } - - return Task.FromResult(0); - } - public async Task UpdateCiphersAsync(Guid userId, IEnumerable ciphers) { if (!ciphers.Any()) diff --git a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs index b199fd06e..05f660ca4 100644 --- a/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs +++ b/src/Infrastructure.EntityFramework/Vault/Repositories/CipherRepository.cs @@ -19,7 +19,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using NS = Newtonsoft.Json; using NSL = Newtonsoft.Json.Linq; -using User = Bit.Core.Entities.User; namespace Bit.Infrastructure.EntityFramework.Vault.Repositories; @@ -865,23 +864,6 @@ public class CipherRepository : Repository ciphers, IEnumerable folders, IEnumerable sends) - { - using (var scope = ServiceScopeFactory.CreateScope()) - { - var dbContext = GetDatabaseContext(scope); - await UserUpdateKeys(user); - var cipherEntities = Mapper.Map>(ciphers); - await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, cipherEntities); - var folderEntities = Mapper.Map>(folders); - await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, folderEntities); - var sendEntities = Mapper.Map>(sends); - await dbContext.BulkCopyAsync(base.DefaultBulkCopyOptions, sendEntities); - await dbContext.SaveChangesAsync(); - } - } - public async Task UpsertAsync(CipherDetails cipher) { if (cipher.Id.Equals(default))