mirror of
https://github.com/bitwarden/server
synced 2026-01-28 23:36:12 +00:00
Move transaction up to code layer
This commit is contained in:
@@ -375,6 +375,7 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
|
||||
await using var connection = new SqlConnection(ConnectionString);
|
||||
await connection.OpenAsync();
|
||||
await using var transaction = connection.BeginTransaction();
|
||||
|
||||
try
|
||||
{
|
||||
@@ -386,12 +387,20 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
DefaultCollectionName = defaultCollectionName,
|
||||
OrganizationUserCollectionIds = organizationUserCollectionIds
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
commandType: CommandType.StoredProcedure,
|
||||
transaction: transaction);
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception ex) when (DatabaseExceptionHelpers.IsDuplicateKeyException(ex))
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
throw new DuplicateDefaultCollectionException();
|
||||
}
|
||||
catch
|
||||
{
|
||||
await transaction.RollbackAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task CreateDefaultCollectionsBulkAsync(Guid organizationId, IEnumerable<Guid> organizationUserIds, string defaultCollectionName)
|
||||
@@ -417,16 +426,16 @@ public class CollectionRepository : Repository<Collection, Guid>, ICollectionRep
|
||||
await BulkResourceCreationService.CreateCollectionsAsync(connection, transaction, collections);
|
||||
await BulkResourceCreationService.CreateCollectionsUsersAsync(connection, transaction, collectionUsers);
|
||||
|
||||
transaction.Commit();
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception ex) when (DatabaseExceptionHelpers.IsDuplicateKeyException(ex))
|
||||
{
|
||||
transaction.Rollback();
|
||||
await transaction.RollbackAsync();
|
||||
throw new DuplicateDefaultCollectionException();
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
await transaction.RollbackAsync();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -819,7 +819,6 @@ public class CollectionRepository : Repository<Core.Entities.Collection, Collect
|
||||
await dbContext.BulkCopyAsync(Mapper.Map<IEnumerable<Collection>>(collections));
|
||||
await dbContext.BulkCopyAsync(Mapper.Map<IEnumerable<CollectionUser>>(collectionUsers));
|
||||
|
||||
await dbContext.SaveChangesAsync();
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception ex) when (DatabaseExceptionHelpers.IsDuplicateKeyException(ex))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-- Creates default user collections for organization users
|
||||
-- Uses semaphore table to prevent duplicate default collections at database level
|
||||
-- NOTE: this MUST be executed in a single transaction to obtain semaphore protection
|
||||
CREATE PROCEDURE [dbo].[Collection_CreateDefaultCollections]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@DefaultCollectionName VARCHAR(MAX),
|
||||
@@ -10,70 +11,58 @@ BEGIN
|
||||
|
||||
DECLARE @Now DATETIME2(7) = GETUTCDATE()
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
-- Insert semaphore entries first to obtain the "lock"
|
||||
-- If this fails due to duplicate key, the entire transaction will be rolled back
|
||||
INSERT INTO [dbo].[DefaultCollectionSemaphore]
|
||||
(
|
||||
[OrganizationUserId],
|
||||
[CreationDate]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
@Now
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
BEGIN TRY
|
||||
-- Insert semaphore entries first to obtain the "lock"
|
||||
-- If this fails due to duplicate key, the entire transaction will be rolled back
|
||||
INSERT INTO [dbo].[DefaultCollectionSemaphore]
|
||||
(
|
||||
[OrganizationUserId],
|
||||
[CreationDate]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
@Now
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
-- Insert collections for users who obtained semaphore entries
|
||||
INSERT INTO [dbo].[Collection]
|
||||
(
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[Name],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[Type],
|
||||
[ExternalId],
|
||||
[DefaultUserCollectionEmail]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
@OrganizationId,
|
||||
@DefaultCollectionName,
|
||||
@Now,
|
||||
@Now,
|
||||
1, -- CollectionType.DefaultUserCollection
|
||||
NULL,
|
||||
NULL
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
-- Insert collections for users who obtained semaphore entries
|
||||
INSERT INTO [dbo].[Collection]
|
||||
(
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[Name],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[Type],
|
||||
[ExternalId],
|
||||
[DefaultUserCollectionEmail]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
@OrganizationId,
|
||||
@DefaultCollectionName,
|
||||
@Now,
|
||||
@Now,
|
||||
1, -- CollectionType.DefaultUserCollection
|
||||
NULL,
|
||||
NULL
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
-- Insert collection user mappings
|
||||
INSERT INTO [dbo].[CollectionUser]
|
||||
(
|
||||
[CollectionId],
|
||||
[OrganizationUserId],
|
||||
[ReadOnly],
|
||||
[HidePasswords],
|
||||
[Manage]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
0, -- ReadOnly = false
|
||||
0, -- HidePasswords = false
|
||||
1 -- Manage = true
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0
|
||||
ROLLBACK TRANSACTION;
|
||||
|
||||
THROW;
|
||||
END CATCH
|
||||
-- Insert collection user mappings
|
||||
INSERT INTO [dbo].[CollectionUser]
|
||||
(
|
||||
[CollectionId],
|
||||
[OrganizationUserId],
|
||||
[ReadOnly],
|
||||
[HidePasswords],
|
||||
[Manage]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
0, -- ReadOnly = false
|
||||
0, -- HidePasswords = false
|
||||
1 -- Manage = true
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
END
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
-- Creates default user collections for organization users
|
||||
-- Uses semaphore table to prevent duplicate default collections at database level
|
||||
-- NOTE: this MUST be executed in a single transaction to obtain semaphore protection
|
||||
CREATE OR ALTER PROCEDURE [dbo].[Collection_CreateDefaultCollections]
|
||||
@OrganizationId UNIQUEIDENTIFIER,
|
||||
@DefaultCollectionName VARCHAR(MAX),
|
||||
@@ -10,71 +11,59 @@ BEGIN
|
||||
|
||||
DECLARE @Now DATETIME2(7) = GETUTCDATE()
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
-- Insert semaphore entries first to obtain the "lock"
|
||||
-- If this fails due to duplicate key, the entire transaction will be rolled back
|
||||
INSERT INTO [dbo].[DefaultCollectionSemaphore]
|
||||
(
|
||||
[OrganizationUserId],
|
||||
[CreationDate]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
@Now
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
BEGIN TRY
|
||||
-- Insert semaphore entries first to obtain the "lock"
|
||||
-- If this fails due to duplicate key, the entire transaction will be rolled back
|
||||
INSERT INTO [dbo].[DefaultCollectionSemaphore]
|
||||
(
|
||||
[OrganizationUserId],
|
||||
[CreationDate]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
@Now
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
-- Insert collections for users who obtained semaphore entries
|
||||
INSERT INTO [dbo].[Collection]
|
||||
(
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[Name],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[Type],
|
||||
[ExternalId],
|
||||
[DefaultUserCollectionEmail]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
@OrganizationId,
|
||||
@DefaultCollectionName,
|
||||
@Now,
|
||||
@Now,
|
||||
1, -- CollectionType.DefaultUserCollection
|
||||
NULL,
|
||||
NULL
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
-- Insert collections for users who obtained semaphore entries
|
||||
INSERT INTO [dbo].[Collection]
|
||||
(
|
||||
[Id],
|
||||
[OrganizationId],
|
||||
[Name],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[Type],
|
||||
[ExternalId],
|
||||
[DefaultUserCollectionEmail]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
@OrganizationId,
|
||||
@DefaultCollectionName,
|
||||
@Now,
|
||||
@Now,
|
||||
1, -- CollectionType.DefaultUserCollection
|
||||
NULL,
|
||||
NULL
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
-- Insert collection user mappings
|
||||
INSERT INTO [dbo].[CollectionUser]
|
||||
(
|
||||
[CollectionId],
|
||||
[OrganizationUserId],
|
||||
[ReadOnly],
|
||||
[HidePasswords],
|
||||
[Manage]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
0, -- ReadOnly = false
|
||||
0, -- HidePasswords = false
|
||||
1 -- Manage = true
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
|
||||
COMMIT TRANSACTION;
|
||||
END TRY
|
||||
BEGIN CATCH
|
||||
IF @@TRANCOUNT > 0
|
||||
ROLLBACK TRANSACTION;
|
||||
|
||||
THROW;
|
||||
END CATCH
|
||||
-- Insert collection user mappings
|
||||
INSERT INTO [dbo].[CollectionUser]
|
||||
(
|
||||
[CollectionId],
|
||||
[OrganizationUserId],
|
||||
[ReadOnly],
|
||||
[HidePasswords],
|
||||
[Manage]
|
||||
)
|
||||
SELECT
|
||||
ids.[Id2], -- CollectionId
|
||||
ids.[Id1], -- OrganizationUserId
|
||||
0, -- ReadOnly = false
|
||||
0, -- HidePasswords = false
|
||||
1 -- Manage = true
|
||||
FROM
|
||||
@OrganizationUserCollectionIds ids;
|
||||
END
|
||||
GO
|
||||
|
||||
Reference in New Issue
Block a user