1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 16:12:49 -05:00

[AC-1682] Postgres migrations

This commit is contained in:
Rui Tome
2023-12-20 11:57:36 +00:00
parent c5f1be4d79
commit 86ba89f230
9 changed files with 4752 additions and 63 deletions

View File

@ -1,10 +1,10 @@
-- Create a temporary table to store the groups with AccessAll = true -- Step 1: Create a temporary table to store the groups with AccessAll = 1
CREATE TEMPORARY TABLE TempGroup AS CREATE TEMP TABLE IF NOT EXISTS TempGroup AS
SELECT "Id" AS "GroupId", "OrganizationId" SELECT "Id" AS "GroupId", "OrganizationId"
FROM "Group" FROM "Group"
WHERE "AccessAll" = true; WHERE "AccessAll" = true;
-- Update existing rows in "CollectionGroups" -- Step 2: Update existing rows in "CollectionGroup"
UPDATE "CollectionGroups" CG UPDATE "CollectionGroups" CG
SET SET
"ReadOnly" = false, "ReadOnly" = false,
@ -12,16 +12,16 @@ SET
"Manage" = false "Manage" = false
FROM "CollectionGroups" CGUpdate FROM "CollectionGroups" CGUpdate
INNER JOIN "Collection" C ON CGUpdate."CollectionId" = C."Id" INNER JOIN "Collection" C ON CGUpdate."CollectionId" = C."Id"
INNER JOIN TempGroup TG ON CGUpdate."GroupId" = TG."GroupId" INNER JOIN TempGroup TG ON CGUpdate."GroupId" = TG."GroupId"
WHERE C."OrganizationId" = TG."OrganizationId"; WHERE C."OrganizationId" = TG."OrganizationId";
-- Insert new rows into "CollectionGroups" -- Step 3: Insert new rows into "CollectionGroup"
INSERT INTO "CollectionGroups" ("CollectionId", "GroupId", "ReadOnly", "HidePasswords", "Manage") INSERT INTO "CollectionGroups" ("CollectionId", "GroupId", "ReadOnly", "HidePasswords", "Manage")
SELECT C."Id", TG."GroupId", false, false, false SELECT C."Id", TG."GroupId", false, false, false
FROM "Collection" C FROM "Collection" C
INNER JOIN TempGroup TG ON C."OrganizationId" = TG."OrganizationId" INNER JOIN TempGroup TG ON C."OrganizationId" = TG."OrganizationId"
LEFT JOIN "CollectionGroups" CG ON CG."CollectionId" = C."Id" AND CG."GroupId" = TG."GroupId" LEFT JOIN "CollectionGroups" CG ON CG."CollectionId" = C."Id" AND CG."GroupId" = TG."GroupId"
WHERE CG."CollectionId" IS NULL; WHERE CG."CollectionId" IS NULL;
-- Drop the temporary table -- Step 4: Drop the temporary table
DROP TABLE IF EXISTS TempGroup; DROP TABLE IF EXISTS TempGroup;

View File

@ -1,51 +1,22 @@
-- Step 1: Insert into a temporary table with an additional column for batch processing, update 50 k at a time -- Update existing rows in CollectionUsers
CREATE TEMPORARY TABLE "TempOrgUser" AS UPDATE "CollectionUsers"
SELECT "Id" AS "OrganizationUserId", "OrganizationId", CAST(ROW_NUMBER() OVER(ORDER BY "Id") / 50000 AS INT) AS "Batch"
FROM "OrganizationUser"
WHERE "AccessAll" = true;
-- Step 2: Get the maximum batch number
DO $$
DECLARE
MaxBatch INT;
CurrentBatch INT := 0;
BEGIN
SELECT MAX("Batch") INTO MaxBatch FROM "TempOrgUser";
-- Step 3: Process each batch
WHILE CurrentBatch <= MaxBatch LOOP
-- Update existing rows in "CollectionUsers"
UPDATE "CollectionUsers" AS target
SET SET
"ReadOnly" = false, "ReadOnly" = false,
"HidePasswords" = false, "HidePasswords" = false,
"Manage" = false "Manage" = false
FROM ( FROM "Collection" AS C
SELECT "C"."Id" AS "CollectionId", "T"."OrganizationUserId" INNER JOIN "CollectionUsers" AS CU ON CU."CollectionId" = C."Id"
FROM "Collection" "C" INNER JOIN "OrganizationUser" AS OU ON CU."CollectionId" = C."Id" AND C."OrganizationId" = OU."OrganizationId"
INNER JOIN "TempOrgUser" "T" ON "C"."OrganizationId" = "T"."OrganizationId" AND "T"."Batch" = CurrentBatch WHERE OU."AccessAll" = true;
) AS source
WHERE target."CollectionId" = source."CollectionId" AND target."OrganizationUserId" = source."OrganizationUserId";
-- Insert new rows into "CollectionUsers" -- Insert new rows into CollectionUsers
INSERT INTO "CollectionUsers" ("CollectionId", "OrganizationUserId", "ReadOnly", "HidePasswords", "Manage") INSERT INTO "CollectionUsers" ("CollectionId", "OrganizationUserId", "ReadOnly", "HidePasswords", "Manage")
SELECT source."CollectionId", source."OrganizationUserId", false, false, false SELECT C."Id" AS "CollectionId", OU."Id" AS "OrganizationUserId", false, false, false
FROM ( FROM "Collection" AS C
SELECT "C"."Id" AS "CollectionId", "T"."OrganizationUserId" INNER JOIN "OrganizationUser" AS OU ON C."OrganizationId" = OU."OrganizationId"
FROM "Collection" "C" WHERE OU."AccessAll" = true
INNER JOIN "TempOrgUser" "T" ON "C"."OrganizationId" = "T"."OrganizationId" AND "T"."Batch" = CurrentBatch AND NOT EXISTS (
) AS source
WHERE NOT EXISTS (
SELECT 1 SELECT 1
FROM "CollectionUsers" target FROM "CollectionUsers" AS CU
WHERE target."CollectionId" = source."CollectionId" AND target."OrganizationUserId" = source."OrganizationUserId" WHERE CU."CollectionId" = C."Id" AND CU."OrganizationUserId" = OU."Id"
); );
-- Move to the next batch
CurrentBatch := CurrentBatch + 1;
END LOOP;
END $$;
-- Step 4: Drop the temporary table
DROP TABLE "TempOrgUser";

View File

@ -1,10 +1,24 @@
-- Update "CollectionUser" with "Manage" = 1 for all users with Manager role or 'EditAssignedCollections' permission -- Update `CollectionUsers` with `Manage` = 1 for all users with Manager role or 'EditAssignedCollections' permission
UPDATE "CollectionUsers" cu UPDATE "CollectionUsers" cu
SET "ReadOnly" = false, SET
"ReadOnly" = false,
"HidePasswords" = false, "HidePasswords" = false,
"Manage" = true "Manage" = true
FROM "OrganizationUser" ou FROM "OrganizationUser" ou
WHERE cu."OrganizationUserId" = ou."Id" WHERE cu."OrganizationUserId" = ou."Id"
AND (ou."Type" = 3 OR AND (ou."Type" = 3 OR
(ou."Permissions" IS NOT NULL AND (ou."Permissions" IS NOT NULL AND
(ou."Permissions"::text)::jsonb->>'editAssignedCollections' = 'true')); ((ou."Permissions"::text)::jsonb->>'editAssignedCollections') = 'true'));
-- Insert rows into CollectionUsers for Managers and users with 'EditAssignedCollections' permission assigned to groups with collection access
INSERT INTO "CollectionUsers" ("CollectionId", "OrganizationUserId", "ReadOnly", "HidePasswords", "Manage")
SELECT cg."CollectionId", ou."Id", false, false, true
FROM "CollectionGroups" cg
INNER JOIN "GroupUser" gu ON cg."GroupId" = gu."GroupId"
INNER JOIN "OrganizationUser" ou ON gu."OrganizationUserId" = ou."Id"
WHERE (ou."Type" = 3 OR
(ou."Permissions" IS NOT NULL AND
((ou."Permissions"::text)::jsonb->>'editAssignedCollections') = 'true'))
AND NOT EXISTS (
SELECT 1 FROM "CollectionUsers" cu
WHERE cu."CollectionId" = cg."CollectionId" AND cu."OrganizationUserId" = ou."Id");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
using Bit.Core.Utilities;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
public partial class FCAccessAllCollectionGroups : Migration
{
private const string _accessAllCollectionGroupsScript = "PostgresMigrations.HelperScripts.2023-12-06_00_AccessAllCollectionGroups.psql";
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_accessAllCollectionGroupsScript));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
throw new Exception("Irreversible migration");
}
}

View File

@ -12,8 +12,8 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
namespace Bit.PostgresMigrations.Migrations namespace Bit.PostgresMigrations.Migrations
{ {
[DbContext(typeof(DatabaseContext))] [DbContext(typeof(DatabaseContext))]
[Migration("20231217125601_FlexibleCollections")] [Migration("20231219154729_FCAccessAllCollectionUsers")]
partial class FlexibleCollections partial class FCAccessAllCollectionUsers
{ {
/// <inheritdoc /> /// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder) protected override void BuildTargetModel(ModelBuilder modelBuilder)

View File

@ -0,0 +1,21 @@
using Bit.Core.Utilities;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Bit.PostgresMigrations.Migrations;
public partial class FCAccessAllCollectionUsers : Migration
{
private const string _accessAllCollectionUsersScript = "PostgresMigrations.HelperScripts.2023-12-06_01_AccessAllCollectionUsers.psql";
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_accessAllCollectionUsersScript));
}
protected override void Down(MigrationBuilder migrationBuilder)
{
throw new Exception("Irreversible migration");
}
}

View File

@ -5,16 +5,12 @@ using Microsoft.EntityFrameworkCore.Migrations;
namespace Bit.PostgresMigrations.Migrations; namespace Bit.PostgresMigrations.Migrations;
public partial class FlexibleCollections : Migration public partial class FCManagersEditAssignedCollectionUsers : Migration
{ {
private const string _accessAllCollectionGroupsScript = "PostgresMigrations.HelperScripts.2023-12-06_00_AccessAllCollectionGroups.psql";
private const string _accessAllCollectionUsersScript = "PostgresMigrations.HelperScripts.2023-12-06_01_AccessAllCollectionUsers.psql";
private const string _managersEditAssignedCollectionUsersScript = "PostgresMigrations.HelperScripts.2023-12-06_02_ManagersEditAssignedCollectionUsers.psql"; private const string _managersEditAssignedCollectionUsersScript = "PostgresMigrations.HelperScripts.2023-12-06_02_ManagersEditAssignedCollectionUsers.psql";
protected override void Up(MigrationBuilder migrationBuilder) protected override void Up(MigrationBuilder migrationBuilder)
{ {
migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_accessAllCollectionGroupsScript));
migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_accessAllCollectionUsersScript));
migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_managersEditAssignedCollectionUsersScript)); migrationBuilder.Sql(CoreHelpers.GetEmbeddedResourceContentsAsync(_managersEditAssignedCollectionUsersScript));
} }