1
0
mirror of https://github.com/bitwarden/server synced 2026-02-16 16:59:03 +00:00

First pass at semaphore

This commit is contained in:
Thomas Rittson
2025-12-30 14:21:38 +10:00
parent 34b4dc3985
commit f069fafea1
13 changed files with 785 additions and 2 deletions

View File

@@ -0,0 +1,23 @@
-- Create DefaultCollectionSemaphore table
-- Cascade behavior: Organization -> OrganizationUser (CASCADE) -> DefaultCollectionSemaphore (CASCADE)
-- OrganizationId FK has NO ACTION to avoid competing cascade paths
IF OBJECT_ID('[dbo].[DefaultCollectionSemaphore]') IS NULL
BEGIN
CREATE TABLE [dbo].[DefaultCollectionSemaphore]
(
[OrganizationId] UNIQUEIDENTIFIER NOT NULL,
[OrganizationUserId] UNIQUEIDENTIFIER NOT NULL,
[CreationDate] DATETIME2(7) NOT NULL,
CONSTRAINT [PK_DefaultCollectionSemaphore] PRIMARY KEY CLUSTERED
(
[OrganizationId] ASC,
[OrganizationUserId] ASC
),
CONSTRAINT [FK_DefaultCollectionSemaphore_Organization] FOREIGN KEY ([OrganizationId])
REFERENCES [dbo].[Organization] ([Id]), -- NO ACTION to avoid competing cascades
CONSTRAINT [FK_DefaultCollectionSemaphore_OrganizationUser] FOREIGN KEY ([OrganizationUserId])
REFERENCES [dbo].[OrganizationUser] ([Id])
ON DELETE CASCADE -- Cascades from OrganizationUser deletion
);
END
GO

View File

@@ -0,0 +1,83 @@
CREATE OR ALTER PROCEDURE [dbo].[Collection_UpsertDefaultCollections]
@OrganizationId UNIQUEIDENTIFIER,
@DefaultCollectionName VARCHAR(MAX),
@OrganizationUserIdsJson NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON
-- Parse JSON once into table variable with pre-generated collection IDs
DECLARE @OrganizationUserIds TABLE
(
[OrganizationUserId] UNIQUEIDENTIFIER,
[CollectionId] UNIQUEIDENTIFIER
);
INSERT INTO @OrganizationUserIds
(
[OrganizationUserId],
[CollectionId]
)
SELECT
CAST([value] AS UNIQUEIDENTIFIER),
NEWID()
FROM
OPENJSON(@OrganizationUserIdsJson);
-- Insert semaphore entries first to obtain the "lock"
INSERT INTO [dbo].[DefaultCollectionSemaphore]
(
[OrganizationId],
[OrganizationUserId],
[CreationDate]
)
SELECT
@OrganizationId,
ou.[OrganizationUserId],
GETUTCDATE()
FROM
@OrganizationUserIds ou;
-- Insert collections for users who obtained semaphore entries
INSERT INTO [dbo].[Collection]
(
[Id],
[OrganizationId],
[Name],
[CreationDate],
[RevisionDate],
[Type],
[ExternalId],
[DefaultUserCollectionEmail]
)
SELECT
ou.[CollectionId],
@OrganizationId,
@DefaultCollectionName,
GETUTCDATE(),
GETUTCDATE(),
1, -- CollectionType.DefaultUserCollection
NULL,
NULL
FROM
@OrganizationUserIds ou;
-- Insert collection user mappings
INSERT INTO [dbo].[CollectionUser]
(
[CollectionId],
[OrganizationUserId],
[ReadOnly],
[HidePasswords],
[Manage]
)
SELECT
ou.[CollectionId],
ou.[OrganizationUserId],
0, -- ReadOnly = false
0, -- HidePasswords = false
1 -- Manage = true
FROM
@OrganizationUserIds ou;
END
GO

View File

@@ -0,0 +1,29 @@
-- Populate DefaultCollectionSemaphore from existing Type=1 (DefaultUserCollection) collections
-- This migration is idempotent and can be run multiple times safely
INSERT INTO [dbo].[DefaultCollectionSemaphore]
(
[OrganizationId],
[OrganizationUserId],
[CreationDate]
)
SELECT DISTINCT
c.[OrganizationId],
cu.[OrganizationUserId],
c.[CreationDate]
FROM
[dbo].[Collection] c
INNER JOIN
[dbo].[CollectionUser] cu ON c.[Id] = cu.[CollectionId]
WHERE
c.[Type] = 1 -- CollectionType.DefaultUserCollection
AND NOT EXISTS
(
SELECT
1
FROM
[dbo].[DefaultCollectionSemaphore] dcs
WHERE
dcs.[OrganizationId] = c.[OrganizationId]
AND dcs.[OrganizationUserId] = cu.[OrganizationUserId]
);
GO