1
0
mirror of https://github.com/bitwarden/server synced 2025-12-15 15:53:59 +00:00

Add dedicated functions instead of updating existing

This commit is contained in:
Hinton
2025-10-09 09:59:14 -07:00
parent 185fbd04c6
commit 12ad758f72
5 changed files with 52 additions and 35 deletions

View File

@@ -1,9 +1,7 @@
using System.Net; using System.Net;
using System.Net.Http.Headers; using System.Net.Http.Headers;
using Bit.Api.IntegrationTest.Factories; using Bit.Api.IntegrationTest.Factories;
using Bit.Core.Entities;
using Bit.Seeder.Recipes; using Bit.Seeder.Recipes;
using Microsoft.AspNetCore.Identity;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
@@ -11,7 +9,7 @@ namespace Bit.Api.IntegrationTest.AdminConsole.Controllers;
public class OrganizationUsersControllerPerformanceTest(ITestOutputHelper testOutputHelper) public class OrganizationUsersControllerPerformanceTest(ITestOutputHelper testOutputHelper)
{ {
[Theory()] [Theory(Skip = "Performance test")]
[InlineData(100)] [InlineData(100)]
[InlineData(60000)] [InlineData(60000)]
public async Task GetAsync(int seats) public async Task GetAsync(int seats)
@@ -20,12 +18,11 @@ public class OrganizationUsersControllerPerformanceTest(ITestOutputHelper testOu
var client = factory.CreateClient(); var client = factory.CreateClient();
var db = factory.GetDatabaseContext(); var db = factory.GetDatabaseContext();
var passwordHasher = factory.Services.CreateScope().ServiceProvider.GetService<IPasswordHasher<User>>(); var seeder = new OrganizationWithUsersRecipe(db);
var seeder = new OrganizationWithUsersRecipe(db, passwordHasher);
var orgId = seeder.Seed("Org", seats, "large.test"); var orgId = seeder.Seed("Org", seats, "large.test");
var tokens = await factory.LoginAsync("admin@large.test", "z0Cvc58Q/lLhEDgtGnuPIFS/INOZP2qFlzaqzvTNEJI="); var tokens = await factory.LoginAsync("admin@large.test", "c55hlJ/cfdvTd4awTXUqow6X3cOQCfGwn11o3HblnPs=");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tokens.Token);
var stopwatch = System.Diagnostics.Stopwatch.StartNew(); var stopwatch = System.Diagnostics.Stopwatch.StartNew();

View File

@@ -37,9 +37,8 @@ public class Program
using var scope = serviceProvider.CreateScope(); using var scope = serviceProvider.CreateScope();
var scopedServices = scope.ServiceProvider; var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<DatabaseContext>(); var db = scopedServices.GetRequiredService<DatabaseContext>();
var passwordHasher = scopedServices.GetRequiredService<IPasswordHasher<User>>();
var recipe = new OrganizationWithUsersRecipe(db, passwordHasher); var recipe = new OrganizationWithUsersRecipe(db);
recipe.Seed(name, users, domain); recipe.Seed(name, users, domain);
} }
} }

View File

@@ -2,19 +2,14 @@
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Infrastructure.EntityFramework.AdminConsole.Models; using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
using Bit.Infrastructure.EntityFramework.Models; using Bit.Infrastructure.EntityFramework.Models;
using Bit.RustSDK;
namespace Bit.Seeder.Factories; namespace Bit.Seeder.Factories;
public class OrganizationSeeder public class OrganizationSeeder
{ {
public static (Organization organization, string key) CreateEnterprise(string name, string domain, int seats) public static Organization CreateEnterprise(string name, string domain, int seats)
{ {
var nativeService = RustSdkServiceFactory.CreateSingleton(); return new Organization
var keys = nativeService.GenerateOrganizationKeys();
var organization = new Organization
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
Name = name, Name = name,
@@ -25,28 +20,37 @@ public class OrganizationSeeder
// Currently hardcoded to the values from https://github.com/bitwarden/sdk-internal/blob/main/crates/bitwarden-core/src/client/test_accounts.rs. // Currently hardcoded to the values from https://github.com/bitwarden/sdk-internal/blob/main/crates/bitwarden-core/src/client/test_accounts.rs.
// TODO: These should be dynamically generated by the SDK. // TODO: These should be dynamically generated by the SDK.
PublicKey = keys.PublicKey, PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmIJbGMk6eZqVE7UxhZ46Weu2jKciqOiOkSVYtGvs61rfe9AXxtLaaZEKN4d4DmkZcF6dna2eXNxZmb7U4pwlttye8ksqISe6IUAZQox7auBpjopdCEPhKRg3BD/u8ks9UxSxgWe+fpebjt6gd5hsl1/5HOObn7SeU6EEU04cp3/eH7a4OTdXxB8oN62HGV9kM/ubM1goILgjoSJDbihMK0eb7b8hPHwcA/YOgKKiu/N3FighccdSMD5Pk+HfjacsFNZQa2EsqW09IvvSZ+iL6HQeZ1vwc/6TO1J7EOfJZFQcjoEL9LVI693efYoMZSmrPEWziZ4PvwpOOGo6OObyMQIDAQAB",
PrivateKey = keys.PrivateKey, PrivateKey = "2.6FggyKVyaKQsfohi5yqgbg==|UU2JeafOB41L5UscGmf4kq15JGDf3Bkf67KECiehTODzbWctVLTgyDk0Qco8/6CMN6nZGXjxR2A4r5ExhmwRNsNxd77G+MprkmiJz+7w33ROZ1ouQO5XjD3wbQ3ssqNiTKId6yAUPBvuAZRixVApauTuADc8QWGixqCQcqZzmU7YSBBIPf652/AEYr4Tk64YihoE39pHiK8MRbTLdRt3EF4LSMugPAPM24vCgUv3w1TD3Fj6sDg/6oi3flOV9SJZX4vCiUXbDNEuD/p2aQrEXVbaxweFOHjTe7F4iawjXw3nG3SO8rUBHcxbhDDVx5rjYactbW5QvHWiyla6uLb6o8WHBneg2EjTEwAHOZE/rBjcqmAJb2sVp1E0Kwq8ycGmL69vmqJPC1GqVTohAQvmEkaxIPpfq24Yb9ZPrADA7iEXBKuAQ1FphFUVgJBJGJbd60sOV1Rz1T+gUwS4wCNQ4l3LG1S22+wzUVlEku5DXFnT932tatqTyWEthqPqLCt6dL1+qa94XLpeHagXAx2VGe8n8IlcADtxqS+l8xQ4heT12WO9kC316vqvg1mnsI56faup9hb3eT9ZpKyxSBGYOphlTWfV1Y/v64f5PYvTo4aL0IYHyLY/9Qi72vFmOpPeHBYgD5t3j+H2CsiU1PkYsBggOmD7xW8FDuT6HWVvwhEJqeibVPK0Lhyj6tgvlSIAvFUaSMFPlmwFNmwfj/AHUhr9KuTfsBFTZ10yy9TZVgf+EofwnrxHBaWUgdD40aHoY1VjfG33iEuajb6buxG3pYFyPNhJNzeLZisUKIDRMQpUHrsE22EyrFFran3tZGdtcyIEK4Q1F0ULYzJ6T9iY25/ZgPy3pEAAMZCtqo3s+GjX295fWIHfMcnjMgNUHPjExjWBHa+ggK9iQXkFpBVyYB1ga/+0eiIhiek3PlgtvpDrqF7TsLK+ROiBw2GJ7uaO3EEXOj2GpNBuEJ5CdodhZkwzhwMcSatgDHkUuNVu0iVbF6/MxVdOxWXKO+jCYM6PZk/vAhLYqpPzu2T2Uyz4nkDs2Tiq61ez6FoCrzdHIiyIxVTzUQH8G9FgSmtaZ7GCbqlhnurYgcMciwPzxg0hpAQT+NZw1tVEii9vFSpJJbGJqNhORKfKh/Mu1P/9LOQq7Y0P2FIR3x/eUVEQ7CGv2jVtO5ryGSmKeq/P9Fr54wTPaNiqN2K+leACUznCdUWw8kZo/AsBcrOe4OkRX6k8LC3oeJXy06DEToatxEvPYemUauhxiXRw8nfNMqc4LyJq2bbT0zCgJHoqpozPdNg6AYWcoIobgAGu7ZQGq+oE1MT3GZxotMPe/NUJiAc5YE9Thb5Yf3gyno71pyqPTVl/6IQuh4SUz7rkgwF/aVHEnr4aUYNoc0PEzd2Me0jElsA3GAneq1I/wngutOWgTViTK4Nptr5uIzMVQs9H1rOMJNorP8b02t1NDu010rSsib9GaaJJq4r4iy46laQOxWoU0ex26arYnk+jw4833WSCTVBIprTgizZ+fKjoY0xwXvI2oOvGNEUCtGFvKFORTaQrlaXZIg1toa2BBVNicyONbwnI3KIu3MgGJ2SlCVXJn8oHFppVHFCdwgN1uDzGiKAhjvr0sZTUtXin2f2CszPTbbo=|fUhbVKrr8CSKE7TZJneXpDGraj5YhRrq9ESo206S+BY=",
}; };
return (organization, keys.Key);
} }
} }
public static class OrganizationExtensions public static class OrgnaizationExtensions
{ {
public static OrganizationUser CreateOrganizationUser(this Organization organization, User user, string orgKey) public static OrganizationUser CreateOrganizationUser(this Organization organization, User user)
{ {
var nativeService = RustSdkServiceFactory.CreateSingleton();
var userOrgKey = nativeService.GenerateUserOrganizationKey(user.PublicKey!, orgKey);
return new OrganizationUser return new OrganizationUser
{ {
Id = Guid.NewGuid(), Id = Guid.NewGuid(),
OrganizationId = organization.Id, OrganizationId = organization.Id,
UserId = user.Id, UserId = user.Id,
Key = userOrgKey,
Key = "4.rY01mZFXHOsBAg5Fq4gyXuklWfm6mQASm42DJpx05a+e2mmp+P5W6r54WU2hlREX0uoTxyP91bKKwickSPdCQQ58J45LXHdr9t2uzOYyjVzpzebFcdMw1eElR9W2DW8wEk9+mvtWvKwu7yTebzND+46y1nRMoFydi5zPVLSlJEf81qZZ4Uh1UUMLwXz+NRWfixnGXgq2wRq1bH0n3mqDhayiG4LJKgGdDjWXC8W8MMXDYx24SIJrJu9KiNEMprJE+XVF9nQVNijNAjlWBqkDpsfaWTUfeVLRLctfAqW1blsmIv4RQ91PupYJZDNc8nO9ZTF3TEVM+2KHoxzDJrLs2Q==",
Type = OrganizationUserType.Admin,
Status = OrganizationUserStatusType.Confirmed
};
}
public static OrganizationUser CreateSdkOrganizationUser(this Organization organization, User user)
{
return new OrganizationUser
{
Id = Guid.NewGuid(),
OrganizationId = organization.Id,
UserId = user.Id,
Key = "4.rY01mZFXHOsBAg5Fq4gyXuklWfm6mQASm42DJpx05a+e2mmp+P5W6r54WU2hlREX0uoTxyP91bKKwickSPdCQQ58J45LXHdr9t2uzOYyjVzpzebFcdMw1eElR9W2DW8wEk9+mvtWvKwu7yTebzND+46y1nRMoFydi5zPVLSlJEf81qZZ4Uh1UUMLwXz+NRWfixnGXgq2wRq1bH0n3mqDhayiG4LJKgGdDjWXC8W8MMXDYx24SIJrJu9KiNEMprJE+XVF9nQVNijNAjlWBqkDpsfaWTUfeVLRLctfAqW1blsmIv4RQ91PupYJZDNc8nO9ZTF3TEVM+2KHoxzDJrLs2Q==",
Type = OrganizationUserType.Admin, Type = OrganizationUserType.Admin,
Status = OrganizationUserStatusType.Confirmed Status = OrganizationUserStatusType.Confirmed
}; };

View File

@@ -8,7 +8,25 @@ namespace Bit.Seeder.Factories;
public class UserSeeder public class UserSeeder
{ {
public static (User user, string userKey) CreateUser(IPasswordHasher<Bit.Core.Entities.User> passwordHasher, string email) public static User CreateUser(string email)
{
return new User
{
Id = Guid.NewGuid(),
Email = email,
MasterPassword = "AQAAAAIAAYagAAAAEBATmF66OHMpHuHKc1CsGZQ1ltHUHyhYK+7e4re3bVFi16SOpLpDfzdFswnvFQs2Rg==",
SecurityStamp = "4830e359-e150-4eae-be2a-996c81c5e609",
Key = "2.z/eLKFhd62qy9RzXu3UHgA==|fF6yNupiCIguFKSDTB3DoqcGR0Xu4j+9VlnMyT5F3PaWIcGhzQKIzxdB95nhslaCQv3c63M7LBnvzVo1J9SUN85RMbP/57bP1HvhhU1nvL8=|IQPtf8v7k83MFZEhazSYXSdu98BBU5rqtvC4keVWyHM=",
PublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Ww2chogqCpaAR7Uw448am4b7vDFXiM5kXjFlGfXBlrAdAqTTggEvTDlMNYqPlCo+mBM6iFmTTUY9rpZBvFskMnKvsvpJ47/fehAH2o2e3Ulv/5NFevaVCMCmpkBDtbMbO1A4a3btdRtCP8DsKWMefHauEpaoLxNTLWnOIZVfCMjsSgx2EvULHAZPTtbFwm4+UVKniM4ds4jvOsD85h4jn2aLs/jWJXFfxN8iVSqEqpC2TBvsPdyHb49xQoWWfF0Z6BiNqeNGKEU9Uos1pjL+kzhEzzSpH31PZT/ufJ/oo4+93wrUt57hb6f0jxiXhwd5yQ+9F6wVwpbfkq0IwhjOwIDAQAB",
PrivateKey = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=",
ApiKey = "7gp59kKHt9kMlks0BuNC4IjNXYkljR",
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = 600_000,
};
}
public static (User user, string userKey) CreateSdkUser(IPasswordHasher<Bit.Core.Entities.User> passwordHasher, string email)
{ {
var nativeService = RustSdkServiceFactory.CreateSingleton(); var nativeService = RustSdkServiceFactory.CreateSingleton();
var keys = nativeService.GenerateUserKeys(email, "asdfasdfasdf"); var keys = nativeService.GenerateUserKeys(email, "asdfasdfasdf");

View File

@@ -1,26 +1,25 @@
using Bit.Core.Entities; using Bit.Infrastructure.EntityFramework.Models;
using Bit.Infrastructure.EntityFramework.Repositories; using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.Seeder.Factories; using Bit.Seeder.Factories;
using LinqToDB.EntityFrameworkCore; using LinqToDB.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
namespace Bit.Seeder.Recipes; namespace Bit.Seeder.Recipes;
public class OrganizationWithUsersRecipe(DatabaseContext db, IPasswordHasher<User> passwordHasher) public class OrganizationWithUsersRecipe(DatabaseContext db)
{ {
public Guid Seed(string name, int users, string domain) public Guid Seed(string name, int users, string domain)
{ {
var (organization, orgKey) = OrganizationSeeder.CreateEnterprise(name, domain, users); var organization = OrganizationSeeder.CreateEnterprise(name, domain, users);
var (user, _) = UserSeeder.CreateUser(passwordHasher, $"admin@{domain}"); var user = UserSeeder.CreateUser($"admin@{domain}");
var orgUser = organization.CreateOrganizationUser(user, orgKey); var orgUser = organization.CreateOrganizationUser(user);
var additionalUsers = new List<User>(); var additionalUsers = new List<User>();
var additionalOrgUsers = new List<OrganizationUser>(); var additionalOrgUsers = new List<OrganizationUser>();
for (var i = 0; i < users; i++) for (var i = 0; i < users; i++)
{ {
var (additionalUser, _) = UserSeeder.CreateUser(passwordHasher, $"user{i}@{domain}"); var additionalUser = UserSeeder.CreateUser($"user{i}@{domain}");
additionalUsers.Add(additionalUser); additionalUsers.Add(additionalUser);
additionalOrgUsers.Add(organization.CreateOrganizationUser(additionalUser, orgKey)); additionalOrgUsers.Add(organization.CreateOrganizationUser(additionalUser));
} }
db.Add(organization); db.Add(organization);