mirror of
https://github.com/bitwarden/server
synced 2025-12-25 04:33:26 +00:00
[PM-3797 Part 3] Add vault domains to key rotation (#3436)
## Type of change <!-- (mark with an `X`) --> ``` - [ ] Bug fix - [ ] New feature development - [x] Tech debt (refactoring, code cleanup, dependency upgrades, etc) - [ ] Build/deploy pipeline (DevOps) - [ ] Other ``` ## Objective <!--Describe what the purpose of this PR is. For example: what bug you're fixing or what new feature you're adding--> Previous PR: #3434 Adds ciphers and folders to the new key rotation. ## Code changes <!--Explain the changes you've made to each file or major component. This should help the reviewer understand your changes--> <!--Also refer to any related changes or PRs in other repositories--> * **file.ext:** Description of what was changed and why ## Before you submit - Please check for formatting errors (`dotnet format --verify-no-changes`) (required) - If making database changes - make sure you also update Entity Framework queries and/or migrations - Please add **unit tests** where it makes sense to do so (encouraged but not required) - If this change requires a **documentation update** - notify the documentation team - If this change has particular **deployment requirements** - notify the DevOps team
This commit is contained in:
33
src/Infrastructure.Dapper/Vault/Helpers/CipherHelpers.cs
Normal file
33
src/Infrastructure.Dapper/Vault/Helpers/CipherHelpers.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using System.Data;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Infrastructure.Dapper.Vault.Helpers;
|
||||
|
||||
public static class CipherHelpers
|
||||
{
|
||||
public static DataTable ToDataTable(this IEnumerable<Cipher> ciphers)
|
||||
{
|
||||
var ciphersTable = new DataTable();
|
||||
ciphersTable.SetTypeName("[dbo].[Cipher]");
|
||||
|
||||
var columnData = new List<(string name, Type type, Func<Cipher, object> getter)>
|
||||
{
|
||||
(nameof(Cipher.Id), typeof(Guid), c => c.Id),
|
||||
(nameof(Cipher.UserId), typeof(Guid), c => c.UserId),
|
||||
(nameof(Cipher.OrganizationId), typeof(Guid), c => c.OrganizationId),
|
||||
(nameof(Cipher.Type), typeof(short), c => c.Type),
|
||||
(nameof(Cipher.Data), typeof(string), c => c.Data),
|
||||
(nameof(Cipher.Favorites), typeof(string), c => c.Favorites),
|
||||
(nameof(Cipher.Folders), typeof(string), c => c.Folders),
|
||||
(nameof(Cipher.Attachments), typeof(string), c => c.Attachments),
|
||||
(nameof(Cipher.CreationDate), typeof(DateTime), c => c.CreationDate),
|
||||
(nameof(Cipher.RevisionDate), typeof(DateTime), c => c.RevisionDate),
|
||||
(nameof(Cipher.DeletedDate), typeof(DateTime), c => c.DeletedDate),
|
||||
(nameof(Cipher.Reprompt), typeof(short), c => c.Reprompt),
|
||||
(nameof(Cipher.Key), typeof(string), c => c.Key),
|
||||
};
|
||||
|
||||
return ciphers.BuildTable(ciphersTable, columnData);
|
||||
}
|
||||
}
|
||||
25
src/Infrastructure.Dapper/Vault/Helpers/FolderHelpers.cs
Normal file
25
src/Infrastructure.Dapper/Vault/Helpers/FolderHelpers.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Data;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Dapper;
|
||||
|
||||
namespace Bit.Infrastructure.Dapper.Vault.Helpers;
|
||||
|
||||
public static class FolderHelpers
|
||||
{
|
||||
public static DataTable ToDataTable(this IEnumerable<Folder> folders)
|
||||
{
|
||||
var foldersTable = new DataTable();
|
||||
foldersTable.SetTypeName("[dbo].[Folder]");
|
||||
|
||||
var columnData = new List<(string name, Type type, Func<Folder, object> getter)>
|
||||
{
|
||||
(nameof(Folder.Id), typeof(Guid), c => c.Id),
|
||||
(nameof(Folder.UserId), typeof(Guid), c => c.UserId),
|
||||
(nameof(Folder.Name), typeof(string), c => c.Name),
|
||||
(nameof(Folder.CreationDate), typeof(DateTime), c => c.CreationDate),
|
||||
(nameof(Folder.RevisionDate), typeof(DateTime), c => c.RevisionDate),
|
||||
};
|
||||
|
||||
return folders.BuildTable(foldersTable, columnData);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Tools.Entities;
|
||||
@@ -7,6 +8,7 @@ using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Models.Data;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
using Bit.Infrastructure.Dapper.Vault.Helpers;
|
||||
using Dapper;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
@@ -308,6 +310,63 @@ public class CipherRepository : Repository<Cipher, Guid>, ICipherRepository
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(
|
||||
Guid userId, IEnumerable<Cipher> ciphers)
|
||||
{
|
||||
return async (SqlConnection connection, SqlTransaction transaction) =>
|
||||
{
|
||||
// Create temp table
|
||||
var sqlCreateTemp = @"
|
||||
SELECT TOP 0 *
|
||||
INTO #TempCipher
|
||||
FROM [dbo].[Cipher]";
|
||||
|
||||
await using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// Bulk copy data into temp table
|
||||
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "#TempCipher";
|
||||
var ciphersTable = ciphers.ToDataTable();
|
||||
foreach (DataColumn col in ciphersTable.Columns)
|
||||
{
|
||||
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
|
||||
}
|
||||
|
||||
ciphersTable.PrimaryKey = new DataColumn[] { ciphersTable.Columns[0] };
|
||||
await bulkCopy.WriteToServerAsync(ciphersTable);
|
||||
}
|
||||
|
||||
// Update cipher table from temp table
|
||||
var 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
|
||||
|
||||
DROP TABLE #TempCipher";
|
||||
|
||||
await using (var cmd = new SqlCommand(sql, connection, transaction))
|
||||
{
|
||||
cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = userId;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public Task UpdateUserKeysAndCiphersAsync(User user, IEnumerable<Cipher> ciphers, IEnumerable<Folder> folders, IEnumerable<Send> sends)
|
||||
{
|
||||
using (var connection = new SqlConnection(ConnectionString))
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using System.Data;
|
||||
using Bit.Core.Auth.UserFeatures.UserKey;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Vault.Entities;
|
||||
using Bit.Core.Vault.Repositories;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
using Bit.Infrastructure.Dapper.Vault.Helpers;
|
||||
using Dapper;
|
||||
using Microsoft.Data.SqlClient;
|
||||
|
||||
@@ -41,4 +43,60 @@ public class FolderRepository : Repository<Folder, Guid>, IFolderRepository
|
||||
return results.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public UpdateEncryptedDataForKeyRotation UpdateForKeyRotation(
|
||||
Guid userId, IEnumerable<Folder> folders)
|
||||
{
|
||||
return async (SqlConnection connection, SqlTransaction transaction) =>
|
||||
{
|
||||
// Create temp table
|
||||
var sqlCreateTemp = @"
|
||||
SELECT TOP 0 *
|
||||
INTO #TempFolder
|
||||
FROM [dbo].[Folder]";
|
||||
|
||||
await using (var cmd = new SqlCommand(sqlCreateTemp, connection, transaction))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
// Bulk copy data into temp table
|
||||
using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepIdentity, transaction))
|
||||
{
|
||||
bulkCopy.DestinationTableName = "#TempFolder";
|
||||
var foldersTable = folders.ToDataTable();
|
||||
foreach (DataColumn col in foldersTable.Columns)
|
||||
{
|
||||
bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
|
||||
}
|
||||
|
||||
foldersTable.PrimaryKey = new DataColumn[] { foldersTable.Columns[0] };
|
||||
await bulkCopy.WriteToServerAsync(foldersTable);
|
||||
}
|
||||
|
||||
// Update folder table from temp table
|
||||
var 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;
|
||||
|
||||
DROP TABLE #TempFolder";
|
||||
|
||||
await using (var cmd = new SqlCommand(sql, connection, transaction))
|
||||
{
|
||||
cmd.Parameters.Add("@UserId", SqlDbType.UniqueIdentifier).Value = userId;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user