Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fa46919
Initial PoC of seeder API
Hinton Oct 7, 2025
d0d6bfb
Extract logic into service
Hinton Oct 7, 2025
2b340b7
Reformat
Hinton Oct 7, 2025
79f5d8f
Add support for destroying seeded data
Hinton Oct 7, 2025
92f2555
Refactor to track entities rather than manually writing destroy
Hinton Oct 7, 2025
f4342e2
Add SingleUserRecipe for seeding a single known user
MGibson1 Oct 9, 2025
d93cd50
Fix launch settings
MGibson1 Oct 9, 2025
431a708
Delete all seeded data
MGibson1 Oct 9, 2025
44aef68
Merge branch 'arch/seeder-sdk' of github.com:bitwarden/server into arโ€ฆ
Hinton Oct 9, 2025
3d863c0
Fixup device table user FK cascade
MGibson1 Oct 9, 2025
9c4c88f
Wire up SDK
Hinton Oct 10, 2025
13d666a
Fixup user mangling
MGibson1 Oct 10, 2025
ec82894
Prefer Repositories for Delete of entities
MGibson1 Oct 10, 2025
a7340c9
Add delete batch for playwright worker cleanup
MGibson1 Oct 10, 2025
fac2804
Force ef
Hinton Oct 10, 2025
99fb70a
use named parameters
MGibson1 Oct 10, 2025
45ba8f8
Add support for setting email verified and premium in seed
Hinton Oct 10, 2025
1137857
Add recipe for getting emergency access invites
Hinton Oct 11, 2025
39cdb65
Add cargo as enabled manager in renovate
Hinton Oct 16, 2025
bb226ff
Delete Sdk2
Hinton Oct 16, 2025
58c620e
Merge branch 'arch/seeder-sdk' of github.com:bitwarden/server into arโ€ฆ
Hinton Oct 16, 2025
b9dcac4
Initial cleanup
Hinton Oct 16, 2025
f6fe7a9
Change to primary constructor
Hinton Oct 16, 2025
fd41332
Refactor recipies into scenes
Hinton Oct 17, 2025
1daf9ad
Add queries, rename seed to scene
Hinton Oct 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,17 @@
},
"preLaunchTask": "buildSso",
},
{
"name": "Seeder API",
"configurations": [
"run-SeederAPI"
],
"presentation": {
"hidden": false,
"group": "cloud",
},
"preLaunchTask": "buildSeederAPI",
},
{
"name": "Admin Self Host",
"configurations": [
Expand Down Expand Up @@ -270,6 +281,17 @@
},
"preLaunchTask": "buildSso",
},
{
"name": "Seeder API Self Host",
"configurations": [
"run-SeederAPI-SelfHost"
],
"presentation": {
"hidden": false,
"group": "self-host",
},
"preLaunchTask": "buildSeederAPI",
}
],
"configurations": [
// Configurations represent run-only scenarios so that they can be used in multiple compounds
Expand Down Expand Up @@ -311,6 +333,25 @@
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": "run-SeederAPI",
"presentation": {
"hidden": true,
},
"requireExactSource": true,
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/util/SeederApi/bin/Debug/net8.0/SeederApi.dll",
"args": [],
"cwd": "${workspaceFolder}/util/SeederApi",
"stopAtEntry": false,
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": "run-Billing",
"presentation": {
Expand Down Expand Up @@ -488,6 +529,27 @@
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": "run-SeederAPI-SelfHost",
"presentation": {
"hidden": true,
},
"requireExactSource": true,
"type": "coreclr",
"request": "launch",
"program": "${workspaceFolder}/util/SeederApi/bin/Debug/net8.0/SeederApi.dll",
"args": [],
"cwd": "${workspaceFolder}/util/SeederApi",
"stopAtEntry": false,
"env": {
"ASPNETCORE_ENVIRONMENT": "Development",
"ASPNETCORE_URLS": "http://localhost:5048",
"developSelfHosted": "true",
},
"sourceFileMap": {
"/Views": "${workspaceFolder}/Views"
}
},
{
"name": "run-Admin-SelfHost",
"presentation": {
Expand Down
19 changes: 18 additions & 1 deletion .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"buildSso",
"buildIcons",
"buildBilling",
"buildNotifications",
"buildNotifications"
],
},
{
Expand Down Expand Up @@ -186,6 +186,23 @@
"isDefault": true
}
},
{
"label": "buildSeederAPI",
"hide": true,
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/util/SeederApi/SeederApi.csproj",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "buildNotifications",
"hide": true,
Expand Down
7 changes: 7 additions & 0 deletions bitwarden-server.sln
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RustSdk", "util\RustSdk\RustSdk.csproj", "{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharedWeb.Test", "test\SharedWeb.Test\SharedWeb.Test.csproj", "{AD59537D-5259-4B7A-948F-0CF58E80B359}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SeederApi", "util\SeederApi\SeederApi.csproj", "{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -348,6 +350,10 @@ Global
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AD59537D-5259-4B7A-948F-0CF58E80B359}.Release|Any CPU.Build.0 = Release|Any CPU
{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -404,6 +410,7 @@ Global
{17A89266-260A-4A03-81AE-C0468C6EE06E} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{D1513D90-E4F5-44A9-9121-5E46E3E4A3F7} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
{AD59537D-5259-4B7A-948F-0CF58E80B359} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F}
{9F08DFBB-482B-4C9D-A5F4-6BDA6EC2E68F} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E01CBF68-2E20-425F-9EDB-E0A6510CA92F}
Expand Down
23 changes: 12 additions & 11 deletions dev/setup_secrets.ps1
100644 โ†’ 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Helper script for applying the same user secrets to each project
param (
[switch]$clear,
[Parameter(ValueFromRemainingArguments = $true, Position=1)]
[Parameter(ValueFromRemainingArguments = $true, Position = 1)]
$cmdArgs
)

Expand All @@ -16,17 +16,18 @@ if ($clear -eq $true) {
}

$projects = @{
Admin = "../src/Admin"
Api = "../src/Api"
Billing = "../src/Billing"
Events = "../src/Events"
EventsProcessor = "../src/EventsProcessor"
Icons = "../src/Icons"
Identity = "../src/Identity"
Notifications = "../src/Notifications"
Sso = "../bitwarden_license/src/Sso"
Scim = "../bitwarden_license/src/Scim"
Admin = "../src/Admin"
Api = "../src/Api"
Billing = "../src/Billing"
Events = "../src/Events"
EventsProcessor = "../src/EventsProcessor"
Icons = "../src/Icons"
Identity = "../src/Identity"
Notifications = "../src/Notifications"
Sso = "../bitwarden_license/src/Sso"
Scim = "../bitwarden_license/src/Scim"
IntegrationTests = "../test/Infrastructure.IntegrationTest"
SeederApi = "../util/SeederApi"
}

foreach ($key in $projects.keys) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public EmergencyAccess ToEmergencyAccess(EmergencyAccess existingEmergencyAccess
existingEmergencyAccess.KeyEncrypted = KeyEncrypted;
}
existingEmergencyAccess.Type = Type;
existingEmergencyAccess.WaitTimeDays = WaitTimeDays;
existingEmergencyAccess.WaitTimeDays = (short)WaitTimeDays;
return existingEmergencyAccess;
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Core/Auth/Entities/EmergencyAccess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class EmergencyAccess : ITableObject<Guid>
public string KeyEncrypted { get; set; }
public EmergencyAccessType Type { get; set; }
public EmergencyAccessStatusType Status { get; set; }
public int WaitTimeDays { get; set; }
public short WaitTimeDays { get; set; }
public DateTime? RecoveryInitiatedDate { get; set; }
public DateTime? LastNotificationDate { get; set; }
public DateTime CreationDate { get; set; } = DateTime.UtcNow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public async Task<EmergencyAccess> InviteAsync(User grantorUser, string emergenc
Email = emergencyContactEmail.ToLowerInvariant(),
Status = EmergencyAccessStatusType.Invited,
Type = accessType,
WaitTimeDays = waitTime,
WaitTimeDays = (short)waitTime,
CreationDate = DateTime.UtcNow,
RevisionDate = DateTime.UtcNow,
};
Expand Down
12 changes: 12 additions & 0 deletions src/Infrastructure.EntityFramework/Models/SeededData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Bit.Infrastructure.EntityFramework.Models;

public class SeededData
{
public Guid Id { get; set; }
public required string RecipeName { get; set; }
/// <summary>
/// JSON blob containing all
/// </summary>
public required string Data { get; set; }
public DateTime CreationDate { get; set; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public DatabaseContext(DbContextOptions<DatabaseContext> options)
public DbSet<OrganizationInstallation> OrganizationInstallations { get; set; }
public DbSet<OrganizationReport> OrganizationReports { get; set; }
public DbSet<OrganizationApplication> OrganizationApplications { get; set; }
public DbSet<SeededData> SeededData { get; set; }

protected override void OnModelCreating(ModelBuilder builder)
{
Expand Down
4 changes: 2 additions & 2 deletions src/SharedWeb/Utilities/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ namespace Bit.SharedWeb.Utilities;

public static class ServiceCollectionExtensions
{
public static SupportedDatabaseProviders AddDatabaseRepositories(this IServiceCollection services, GlobalSettings globalSettings)
public static SupportedDatabaseProviders AddDatabaseRepositories(this IServiceCollection services, GlobalSettings globalSettings, bool forceEf = false)
{
var (provider, connectionString) = GetDatabaseProvider(globalSettings);
services.SetupEntityFramework(connectionString, provider);

if (provider != SupportedDatabaseProviders.SqlServer)
if (provider != SupportedDatabaseProviders.SqlServer && !forceEf)
{
services.AddPasswordManagerEFRepositories(globalSettings.SelfHosted);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Sql/dbo/Tables/Device.sql
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
[EncryptedPrivateKey] VARCHAR (MAX) NULL,
[Active] BIT NOT NULL CONSTRAINT [DF_Device_Active] DEFAULT (1),
CONSTRAINT [PK_Device] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_Device_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
CONSTRAINT [FK_Device_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MGibson1 did we want to revert this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was undecided. I believe we don't need it anymore with the full repository injection we're using, but it's not wrong.

@rkac-bw @withinfocus Care to comment on using cascading delete here vs needing to understand delete through sprocs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a fan of cascades personally and try to avoid them, but they were here when I showed up so I am working with it. Our approach -- and this could use documentation on our revamped SQL code style page -- is to manage deletions that touch critical tables explicitly e.g. Organization_DeleteById, and for smaller children of those tables to allow cascades so as to keep the coding pattern simple enough.

One example: OrganizationIntegration, being linked to the critical Organization table, is deleted via the above proc. Its child table OrganizationIntegrationConfiguration has a cascade delete on it.

);

GO
Expand Down
6 changes: 6 additions & 0 deletions src/Sql/dbo/Tables/SeededData.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE [dbo].[SeededData] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[RecipeName] NVARCHAR (MAX) NOT NULL,
[Data] NVARCHAR (MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
);
10 changes: 10 additions & 0 deletions util/Migrator/DbScripts/2025-10-07_00_SeededData.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
IF OBJECT_ID('dbo.SeededData') IS NULL
BEGIN
CREATE TABLE [dbo].[SeededData] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[RecipeName] NVARCHAR (MAX) NOT NULL,
[Data] NVARCHAR (MAX) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
);
END
GO
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
IF OBJECT_ID('[dbo].[FK_Device_User]', 'F') IS NOT NULL
BEGIN
ALTER TABLE [dbo].[Device]
DROP CONSTRAINT [FK_Device_User]
END
GO

ALTER TABLE [dbo].[Device]
ADD CONSTRAINT [FK_Device_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) ON DELETE CASCADE
GO
Loading
Loading