1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

add sproc, refactor tests

This commit is contained in:
Brandon 2024-11-18 12:16:19 -05:00
parent 16bf910255
commit b21563ed1d
No known key found for this signature in database
GPG Key ID: A0E0EF0B207BA40D
4 changed files with 222 additions and 49 deletions

View File

@ -174,12 +174,12 @@ public class UserRepository : Repository<User, Guid>, IUserRepository
} }
public async Task DeleteManyAsync(IEnumerable<User> users) public async Task DeleteManyAsync(IEnumerable<User> users)
{ {
var list = users.Select(user => user.Id); var ids = users.Select(user => user.Id);
using (var connection = new SqlConnection(ConnectionString)) using (var connection = new SqlConnection(ConnectionString))
{ {
await connection.ExecuteAsync( await connection.ExecuteAsync(
$"[{Schema}].[{Table}_DeleteById]", $"[{Schema}].[{Table}_DeleteByIds]",
new { Ids = list.ToGuidIdArrayTVP() }, new { Ids = JsonSerializer.Serialize(ids) },
commandType: CommandType.StoredProcedure, commandType: CommandType.StoredProcedure,
commandTimeout: 180); commandTimeout: 180);
} }

View File

@ -0,0 +1,158 @@
CREATE PROCEDURE [dbo].[User_DeleteByIds]
@Ids NVARCHAR(MAX)
WITH RECOMPILE
AS
BEGIN
SET NOCOUNT ON
-- Declare a table variable to hold the parsed JSON data
DECLARE @ParsedIds TABLE (Id UNIQUEIDENTIFIER);
-- Parse the JSON input into the table variable
INSERT INTO @ParsedIds (Id)
SELECT value
FROM OPENJSON(@Ids);
-- Check if the input table is empty
IF (SELECT COUNT(1) FROM @ParsedIds) < 1
BEGIN
RETURN(-1);
END
DECLARE @BatchSize INT = 100
-- Delete ciphers
WHILE @BatchSize > 0
BEGIN
BEGIN TRANSACTION User_DeleteById_Ciphers
DELETE TOP(@BatchSize)
FROM
[dbo].[Cipher]
WHERE
[UserId] IN (@ParsedIds)
SET @BatchSize = @@ROWCOUNT
COMMIT TRANSACTION User_DeleteById_Ciphers
END
BEGIN TRANSACTION User_DeleteById
-- Delete WebAuthnCredentials
DELETE
FROM
[dbo].[WebAuthnCredential]
WHERE
[UserId] IN (@ParsedIds)
-- Delete folders
DELETE
FROM
[dbo].[Folder]
WHERE
[UserId] IN (@ParsedIds)
-- Delete AuthRequest, must be before Device
DELETE
FROM
[dbo].[AuthRequest]
WHERE
[UserId] IN (@ParsedIds)
-- Delete devices
DELETE
FROM
[dbo].[Device]
WHERE
[UserId] IN (@ParsedIds)
-- Delete collection users
DELETE
CU
FROM
[dbo].[CollectionUser] CU
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = CU.[OrganizationUserId]
WHERE
OU.[UserId] IN (@ParsedIds)
-- Delete group users
DELETE
GU
FROM
[dbo].[GroupUser] GU
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = GU.[OrganizationUserId]
WHERE
OU.[UserId] IN (@ParsedIds)
-- Delete AccessPolicy
DELETE
AP
FROM
[dbo].[AccessPolicy] AP
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.[Id] = AP.[OrganizationUserId]
WHERE
[UserId] IN (@ParsedIds)
-- Delete organization users
DELETE
FROM
[dbo].[OrganizationUser]
WHERE
[UserId] IN (@ParsedIds)
-- Delete provider users
DELETE
FROM
[dbo].[ProviderUser]
WHERE
[UserId] IN (@ParsedIds)
-- Delete SSO Users
DELETE
FROM
[dbo].[SsoUser]
WHERE
[UserId] IN (@ParsedIds)
-- Delete Emergency Accesses
DELETE
FROM
[dbo].[EmergencyAccess]
WHERE
[GrantorId] in (@ParsedIds)
OR
[GranteeId] in (@ParsedIds)
-- Delete Sends
DELETE
FROM
[dbo].[Send]
WHERE
[UserId] IN (@ParsedIds)
-- Delete Notification Status
DELETE
FROM
[dbo].[NotificationStatus]
WHERE
[UserId] IN (@ParsedIds)
-- Delete Notification
DELETE
FROM
[dbo].[Notification]
WHERE
[UserId] IN (@ParsedIds)
-- Finally, delete the user
DELETE
FROM
[dbo].[User]
WHERE
[Id] in (@ParsedIds)
COMMIT TRANSACTION User_DeleteById
END

View File

@ -92,52 +92,6 @@ public class UserRepositoryTests
Assert.True(savedSqlUser == null); Assert.True(savedSqlUser == null);
} }
[CiSkippedTheory, EfUserAutoData]
public async Task DeleteManyAsync_Works_DataMatches(IEnumerable<User> users, List<EfRepo.UserRepository> suts, SqlRepo.UserRepository sqlUserRepo)
{
foreach (var sut in suts)
{
List<User> efUserList = new List<User>();
foreach (var user in users)
{
var postEfUser = await sut.CreateAsync(user);
sut.ClearChangeTracking();
var savedEfUser = await sut.GetByIdAsync(postEfUser.Id);
Assert.True(savedEfUser != null);
sut.ClearChangeTracking();
efUserList.Add(savedEfUser);
}
await sut.DeleteManyAsync(efUserList);
sut.ClearChangeTracking();
foreach (var efUser in efUserList)
{
var savedEfUser = await sut.GetByIdAsync(efUser.Id);
Assert.True(savedEfUser == null);
}
}
List<User> userList = new List<User>();
foreach (var user in users)
{
var postSqlUser = await sqlUserRepo.CreateAsync(user);
var savedSqlUser = await sqlUserRepo.GetByIdAsync(postSqlUser.Id);
Assert.True(savedSqlUser != null);
userList.Add(postSqlUser);
}
await sqlUserRepo.DeleteManyAsync(userList);
foreach (var user in userList)
{
var savedSqlUser = await sqlUserRepo.GetByIdAsync(user.Id);
Assert.True(savedSqlUser == null);
}
}
[CiSkippedTheory, EfUserAutoData] [CiSkippedTheory, EfUserAutoData]
public async Task GetByEmailAsync_Works_DataMatches(User user, UserCompare equalityComparer, public async Task GetByEmailAsync_Works_DataMatches(User user, UserCompare equalityComparer,
List<EfRepo.UserRepository> suts, SqlRepo.UserRepository sqlUserRepo) List<EfRepo.UserRepository> suts, SqlRepo.UserRepository sqlUserRepo)

View File

@ -0,0 +1,61 @@
using Bit.Core.Entities;
using Bit.Core.Repositories;
using Xunit;
namespace Bit.Infrastructure.IntegrationTest.Repositories;
public class UserRepositoryTests
{
[DatabaseTheory, DatabaseData]
public async Task DeleteAsync_Works(IUserRepository userRepository)
{
var user = await userRepository.CreateAsync(new User
{
Name = "Test User",
Email = $"test+{Guid.NewGuid()}@example.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
await userRepository.DeleteAsync(user);
var newUser = await userRepository.GetByIdAsync(user.Id);
Assert.NotNull(newUser);
Assert.NotEqual(newUser.AccountRevisionDate, user.AccountRevisionDate);
}
[DatabaseTheory, DatabaseData]
public async Task DeleteManyAsync_Works(IUserRepository userRepository)
{
var user1 = await userRepository.CreateAsync(new User
{
Name = "Test User 1",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
var user2 = await userRepository.CreateAsync(new User
{
Name = "Test User 2",
Email = $"test+{Guid.NewGuid()}@email.com",
ApiKey = "TEST",
SecurityStamp = "stamp",
});
await userRepository.DeleteManyAsync(new List<User>
{
user1,
user2
});
var updatedUser1 = await userRepository.GetByIdAsync(user1.Id);
Assert.NotNull(updatedUser1);
var updatedUser2 = await userRepository.GetByIdAsync(user2.Id);
Assert.NotNull(updatedUser2);
Assert.NotEqual(updatedUser1.AccountRevisionDate, user1.AccountRevisionDate);
Assert.NotEqual(updatedUser2.AccountRevisionDate, user2.AccountRevisionDate);
}
}