mirror of
https://github.com/bitwarden/server.git
synced 2026-01-31 14:13:18 +08:00
Refactor to correctly implement statics and remove hardcoded organization keys (#6924)
This commit is contained in:
@@ -1,11 +1,14 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using AutoMapper;
|
||||||
using Bit.Api.AdminConsole.Models.Request;
|
using Bit.Api.AdminConsole.Models.Request;
|
||||||
using Bit.Api.IntegrationTest.Factories;
|
using Bit.Api.IntegrationTest.Factories;
|
||||||
using Bit.Api.IntegrationTest.Helpers;
|
using Bit.Api.IntegrationTest.Helpers;
|
||||||
using Bit.Api.Models.Request;
|
using Bit.Api.Models.Request;
|
||||||
|
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;
|
||||||
|
|
||||||
@@ -26,7 +29,9 @@ public class GroupsControllerPerformanceTests(ITestOutputHelper testOutputHelper
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -34,8 +39,8 @@ public class GroupsControllerPerformanceTests(ITestOutputHelper testOutputHelper
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
var collectionIds = collectionsSeeder.AddToOrganization(orgId, collectionCount, orgUserIds, 0);
|
var collectionIds = collectionsSeeder.Seed(orgId, collectionCount, orgUserIds, 0);
|
||||||
var groupIds = groupsSeeder.AddToOrganization(orgId, 1, orgUserIds, 0);
|
var groupIds = groupsSeeder.Seed(orgId, 1, orgUserIds, 0);
|
||||||
|
|
||||||
var groupId = groupIds.First();
|
var groupId = groupIds.First();
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using AutoMapper;
|
||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.IntegrationTest.Factories;
|
using Bit.Api.IntegrationTest.Factories;
|
||||||
using Bit.Api.IntegrationTest.Helpers;
|
using Bit.Api.IntegrationTest.Helpers;
|
||||||
using Bit.Api.Models.Request;
|
using Bit.Api.Models.Request;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Seeder.Recipes;
|
using Bit.Seeder.Recipes;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
@@ -28,7 +31,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -37,8 +42,8 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: seats);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: seats);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
collectionsSeeder.AddToOrganization(orgId, 10, orgUserIds);
|
collectionsSeeder.Seed(orgId, 10, orgUserIds);
|
||||||
groupsSeeder.AddToOrganization(orgId, 5, orgUserIds);
|
groupsSeeder.Seed(orgId, 5, orgUserIds);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -64,7 +69,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -72,8 +79,8 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: seats);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: seats);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
collectionsSeeder.AddToOrganization(orgId, 10, orgUserIds);
|
collectionsSeeder.Seed(orgId, 10, orgUserIds);
|
||||||
groupsSeeder.AddToOrganization(orgId, 5, orgUserIds);
|
groupsSeeder.Seed(orgId, 5, orgUserIds);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -98,14 +105,16 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
||||||
|
|
||||||
var orgUserId = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).FirstOrDefault();
|
var orgUserId = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).FirstOrDefault();
|
||||||
groupsSeeder.AddToOrganization(orgId, 2, [orgUserId]);
|
groupsSeeder.Seed(orgId, 2, [orgUserId]);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -130,7 +139,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
||||||
@@ -163,7 +174,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(
|
var orgId = orgSeeder.Seed(
|
||||||
@@ -211,7 +224,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
||||||
@@ -251,7 +266,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(
|
var orgId = orgSeeder.Seed(
|
||||||
@@ -295,7 +312,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(
|
var orgId = orgSeeder.Seed(
|
||||||
@@ -339,7 +358,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var domainSeeder = new OrganizationDomainRecipe(db);
|
var domainSeeder = new OrganizationDomainRecipe(db);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
@@ -350,7 +371,7 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
users: userCount,
|
users: userCount,
|
||||||
usersStatus: OrganizationUserStatusType.Confirmed);
|
usersStatus: OrganizationUserStatusType.Confirmed);
|
||||||
|
|
||||||
domainSeeder.AddVerifiedDomainToOrganization(orgId, domain);
|
domainSeeder.Seed(orgId, domain);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -384,7 +405,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -392,8 +415,8 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
var collectionIds = collectionsSeeder.AddToOrganization(orgId, 3, orgUserIds, 0);
|
var collectionIds = collectionsSeeder.Seed(orgId, 3, orgUserIds, 0);
|
||||||
var groupIds = groupsSeeder.AddToOrganization(orgId, 2, orgUserIds, 0);
|
var groupIds = groupsSeeder.Seed(orgId, 2, orgUserIds, 0);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -434,7 +457,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
||||||
@@ -471,7 +496,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var domainSeeder = new OrganizationDomainRecipe(db);
|
var domainSeeder = new OrganizationDomainRecipe(db);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
@@ -481,7 +508,7 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
users: 2,
|
users: 2,
|
||||||
usersStatus: OrganizationUserStatusType.Confirmed);
|
usersStatus: OrganizationUserStatusType.Confirmed);
|
||||||
|
|
||||||
domainSeeder.AddVerifiedDomainToOrganization(orgId, domain);
|
domainSeeder.Seed(orgId, domain);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -512,14 +539,16 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: 1);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
var collectionIds = collectionsSeeder.AddToOrganization(orgId, 2, orgUserIds, 0);
|
var collectionIds = collectionsSeeder.Seed(orgId, 2, orgUserIds, 0);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -560,7 +589,9 @@ public class OrganizationUsersControllerPerformanceTests(ITestOutputHelper testO
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
|
|
||||||
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
var domain = OrganizationTestHelpers.GenerateRandomDomain();
|
||||||
var orgId = orgSeeder.Seed(
|
var orgId = orgSeeder.Seed(
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using AutoMapper;
|
||||||
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
using Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||||
using Bit.Api.Auth.Models.Request.Accounts;
|
using Bit.Api.Auth.Models.Request.Accounts;
|
||||||
using Bit.Api.IntegrationTest.Factories;
|
using Bit.Api.IntegrationTest.Factories;
|
||||||
using Bit.Api.IntegrationTest.Helpers;
|
using Bit.Api.IntegrationTest.Helpers;
|
||||||
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
using Bit.Core.AdminConsole.Models.Business.Tokenables;
|
||||||
using Bit.Core.Billing.Enums;
|
using Bit.Core.Billing.Enums;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Tokens;
|
using Bit.Core.Tokens;
|
||||||
using Bit.Seeder.Recipes;
|
using Bit.Seeder.Recipes;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
@@ -29,7 +32,9 @@ public class OrganizationsControllerPerformanceTests(ITestOutputHelper testOutpu
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -37,8 +42,8 @@ public class OrganizationsControllerPerformanceTests(ITestOutputHelper testOutpu
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
collectionsSeeder.AddToOrganization(orgId, collectionCount, orgUserIds, 0);
|
collectionsSeeder.Seed(orgId, collectionCount, orgUserIds, 0);
|
||||||
groupsSeeder.AddToOrganization(orgId, groupCount, orgUserIds, 0);
|
groupsSeeder.Seed(orgId, groupCount, orgUserIds, 0);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
@@ -77,7 +82,9 @@ public class OrganizationsControllerPerformanceTests(ITestOutputHelper testOutpu
|
|||||||
var client = factory.CreateClient();
|
var client = factory.CreateClient();
|
||||||
|
|
||||||
var db = factory.GetDatabaseContext();
|
var db = factory.GetDatabaseContext();
|
||||||
var orgSeeder = new OrganizationWithUsersRecipe(db);
|
var mapper = factory.GetService<IMapper>();
|
||||||
|
var passwordHasher = factory.GetService<IPasswordHasher<User>>();
|
||||||
|
var orgSeeder = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
var collectionsSeeder = new CollectionsRecipe(db);
|
var collectionsSeeder = new CollectionsRecipe(db);
|
||||||
var groupsSeeder = new GroupsRecipe(db);
|
var groupsSeeder = new GroupsRecipe(db);
|
||||||
|
|
||||||
@@ -85,8 +92,8 @@ public class OrganizationsControllerPerformanceTests(ITestOutputHelper testOutpu
|
|||||||
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
var orgId = orgSeeder.Seed(name: "Org", domain: domain, users: userCount);
|
||||||
|
|
||||||
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
var orgUserIds = db.OrganizationUsers.Where(ou => ou.OrganizationId == orgId).Select(ou => ou.Id).ToList();
|
||||||
collectionsSeeder.AddToOrganization(orgId, collectionCount, orgUserIds, 0);
|
collectionsSeeder.Seed(orgId, collectionCount, orgUserIds, 0);
|
||||||
groupsSeeder.AddToOrganization(orgId, groupCount, orgUserIds, 0);
|
groupsSeeder.Seed(orgId, groupCount, orgUserIds, 0);
|
||||||
|
|
||||||
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
await PerformanceTestHelpers.AuthenticateClientAsync(factory, client, $"owner@{domain}");
|
||||||
|
|
||||||
|
|||||||
@@ -18,18 +18,17 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void EncryptDecrypt_LoginCipher_RoundtripPreservesPlaintext()
|
public void EncryptDecrypt_LoginCipher_RoundtripPreservesPlaintext()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
var orgKeys = sdk.GenerateOrganizationKeys();
|
|
||||||
|
|
||||||
var originalCipher = CreateTestLoginCipher();
|
var originalCipher = CreateTestLoginCipher();
|
||||||
var originalJson = JsonSerializer.Serialize(originalCipher, SdkJsonOptions);
|
var originalJson = JsonSerializer.Serialize(originalCipher, SdkJsonOptions);
|
||||||
|
|
||||||
var encryptedJson = sdk.EncryptCipher(originalJson, orgKeys.Key);
|
var encryptedJson = RustSdkService.EncryptCipher(originalJson, orgKeys.Key);
|
||||||
|
|
||||||
Assert.DoesNotContain("\"error\"", encryptedJson);
|
Assert.DoesNotContain("\"error\"", encryptedJson);
|
||||||
Assert.Contains("\"name\":\"2.", encryptedJson);
|
Assert.Contains("\"name\":\"2.", encryptedJson);
|
||||||
|
|
||||||
var decryptedJson = sdk.DecryptCipher(encryptedJson, orgKeys.Key);
|
var decryptedJson = RustSdkService.DecryptCipher(encryptedJson, orgKeys.Key);
|
||||||
|
|
||||||
Assert.DoesNotContain("\"error\"", decryptedJson);
|
Assert.DoesNotContain("\"error\"", decryptedJson);
|
||||||
|
|
||||||
@@ -45,8 +44,7 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void EncryptCipher_WithUri_EncryptsAllFields()
|
public void EncryptCipher_WithUri_EncryptsAllFields()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
var orgKeys = sdk.GenerateOrganizationKeys();
|
|
||||||
|
|
||||||
var cipher = new CipherViewDto
|
var cipher = new CipherViewDto
|
||||||
{
|
{
|
||||||
@@ -66,7 +64,7 @@ public class RustSdkCipherTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var cipherJson = JsonSerializer.Serialize(cipher, SdkJsonOptions);
|
var cipherJson = JsonSerializer.Serialize(cipher, SdkJsonOptions);
|
||||||
var encryptedJson = sdk.EncryptCipher(cipherJson, orgKeys.Key);
|
var encryptedJson = RustSdkService.EncryptCipher(cipherJson, orgKeys.Key);
|
||||||
|
|
||||||
Assert.DoesNotContain("\"error\"", encryptedJson);
|
Assert.DoesNotContain("\"error\"", encryptedJson);
|
||||||
Assert.DoesNotContain("Amazon Shopping", encryptedJson);
|
Assert.DoesNotContain("Amazon Shopping", encryptedJson);
|
||||||
@@ -77,17 +75,16 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void DecryptCipher_WithWrongKey_FailsOrProducesGarbage()
|
public void DecryptCipher_WithWrongKey_FailsOrProducesGarbage()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var encryptionKey = RustSdkService.GenerateOrganizationKeys();
|
||||||
var encryptionKey = sdk.GenerateOrganizationKeys();
|
var differentKey = RustSdkService.GenerateOrganizationKeys();
|
||||||
var differentKey = sdk.GenerateOrganizationKeys();
|
|
||||||
|
|
||||||
var originalCipher = CreateTestLoginCipher();
|
var originalCipher = CreateTestLoginCipher();
|
||||||
var cipherJson = JsonSerializer.Serialize(originalCipher, SdkJsonOptions);
|
var cipherJson = JsonSerializer.Serialize(originalCipher, SdkJsonOptions);
|
||||||
|
|
||||||
var encryptedJson = sdk.EncryptCipher(cipherJson, encryptionKey.Key);
|
var encryptedJson = RustSdkService.EncryptCipher(cipherJson, encryptionKey.Key);
|
||||||
Assert.DoesNotContain("\"error\"", encryptedJson);
|
Assert.DoesNotContain("\"error\"", encryptedJson);
|
||||||
|
|
||||||
var decryptedJson = sdk.DecryptCipher(encryptedJson, differentKey.Key);
|
var decryptedJson = RustSdkService.DecryptCipher(encryptedJson, differentKey.Key);
|
||||||
|
|
||||||
var decryptionFailedWithError = decryptedJson.Contains("\"error\"");
|
var decryptionFailedWithError = decryptedJson.Contains("\"error\"");
|
||||||
if (!decryptionFailedWithError)
|
if (!decryptionFailedWithError)
|
||||||
@@ -100,8 +97,7 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void EncryptCipher_WithFields_EncryptsCustomFields()
|
public void EncryptCipher_WithFields_EncryptsCustomFields()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
var orgKeys = sdk.GenerateOrganizationKeys();
|
|
||||||
|
|
||||||
var cipher = new CipherViewDto
|
var cipher = new CipherViewDto
|
||||||
{
|
{
|
||||||
@@ -120,13 +116,13 @@ public class RustSdkCipherTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var cipherJson = JsonSerializer.Serialize(cipher, SdkJsonOptions);
|
var cipherJson = JsonSerializer.Serialize(cipher, SdkJsonOptions);
|
||||||
var encryptedJson = sdk.EncryptCipher(cipherJson, orgKeys.Key);
|
var encryptedJson = RustSdkService.EncryptCipher(cipherJson, orgKeys.Key);
|
||||||
|
|
||||||
Assert.DoesNotContain("\"error\"", encryptedJson);
|
Assert.DoesNotContain("\"error\"", encryptedJson);
|
||||||
Assert.DoesNotContain("sk-secret-api-key-12345", encryptedJson);
|
Assert.DoesNotContain("sk-secret-api-key-12345", encryptedJson);
|
||||||
Assert.DoesNotContain("client-id-xyz", encryptedJson);
|
Assert.DoesNotContain("client-id-xyz", encryptedJson);
|
||||||
|
|
||||||
var decryptedJson = sdk.DecryptCipher(encryptedJson, orgKeys.Key);
|
var decryptedJson = RustSdkService.DecryptCipher(encryptedJson, orgKeys.Key);
|
||||||
var decrypted = JsonSerializer.Deserialize<CipherViewDto>(decryptedJson, SdkJsonOptions);
|
var decrypted = JsonSerializer.Deserialize<CipherViewDto>(decryptedJson, SdkJsonOptions);
|
||||||
|
|
||||||
Assert.NotNull(decrypted?.Fields);
|
Assert.NotNull(decrypted?.Fields);
|
||||||
@@ -138,13 +134,11 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CipherSeeder_ProducesServerCompatibleFormat()
|
public void CipherSeeder_ProducesServerCompatibleFormat()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
var orgKeys = sdk.GenerateOrganizationKeys();
|
|
||||||
var seeder = new CipherSeeder(sdk);
|
|
||||||
var orgId = Guid.NewGuid();
|
var orgId = Guid.NewGuid();
|
||||||
|
|
||||||
// Create cipher using the seeder
|
// Create cipher using the seeder
|
||||||
var cipher = seeder.CreateOrganizationLoginCipher(
|
var cipher = CipherSeeder.CreateOrganizationLoginCipher(
|
||||||
orgId,
|
orgId,
|
||||||
orgKeys.Key,
|
orgKeys.Key,
|
||||||
name: "GitHub Account",
|
name: "GitHub Account",
|
||||||
@@ -179,11 +173,9 @@ public class RustSdkCipherTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CipherSeeder_WithFields_ProducesCorrectServerFormat()
|
public void CipherSeeder_WithFields_ProducesCorrectServerFormat()
|
||||||
{
|
{
|
||||||
var sdk = new RustSdkService();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
var orgKeys = sdk.GenerateOrganizationKeys();
|
|
||||||
var seeder = new CipherSeeder(sdk);
|
|
||||||
|
|
||||||
var cipher = seeder.CreateOrganizationLoginCipherWithFields(
|
var cipher = CipherSeeder.CreateOrganizationLoginCipherWithFields(
|
||||||
Guid.NewGuid(),
|
Guid.NewGuid(),
|
||||||
orgKeys.Key,
|
orgKeys.Key,
|
||||||
name: "API Service",
|
name: "API Service",
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using AutoMapper;
|
using AutoMapper;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
using Bit.RustSDK;
|
|
||||||
using Bit.Seeder.Recipes;
|
using Bit.Seeder.Recipes;
|
||||||
using CommandDotNet;
|
using CommandDotNet;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -37,7 +36,9 @@ public class Program
|
|||||||
var scopedServices = scope.ServiceProvider;
|
var scopedServices = scope.ServiceProvider;
|
||||||
var db = scopedServices.GetRequiredService<DatabaseContext>();
|
var db = scopedServices.GetRequiredService<DatabaseContext>();
|
||||||
|
|
||||||
var recipe = new OrganizationWithUsersRecipe(db);
|
var mapper = scopedServices.GetRequiredService<IMapper>();
|
||||||
|
var passwordHasher = scopedServices.GetRequiredService<IPasswordHasher<User>>();
|
||||||
|
var recipe = new OrganizationWithUsersRecipe(db, mapper, passwordHasher);
|
||||||
recipe.Seed(name: name, domain: domain, users: users);
|
recipe.Seed(name: name, domain: domain, users: users);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,7 +57,6 @@ public class Program
|
|||||||
var recipe = new OrganizationWithVaultRecipe(
|
var recipe = new OrganizationWithVaultRecipe(
|
||||||
scopedServices.GetRequiredService<DatabaseContext>(),
|
scopedServices.GetRequiredService<DatabaseContext>(),
|
||||||
scopedServices.GetRequiredService<IMapper>(),
|
scopedServices.GetRequiredService<IMapper>(),
|
||||||
scopedServices.GetRequiredService<RustSdkService>(),
|
|
||||||
scopedServices.GetRequiredService<IPasswordHasher<User>>());
|
scopedServices.GetRequiredService<IPasswordHasher<User>>());
|
||||||
|
|
||||||
recipe.Seed(args.ToOptions());
|
recipe.Seed(args.ToOptions());
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.RustSDK;
|
|
||||||
using Bit.SharedWeb.Utilities;
|
using Bit.SharedWeb.Utilities;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
@@ -23,7 +22,6 @@ public static class ServiceCollectionExtension
|
|||||||
builder.AddFilter("Microsoft.EntityFrameworkCore.Model.Validation", LogLevel.Error);
|
builder.AddFilter("Microsoft.EntityFrameworkCore.Model.Validation", LogLevel.Error);
|
||||||
});
|
});
|
||||||
services.AddSingleton(globalSettings);
|
services.AddSingleton(globalSettings);
|
||||||
services.AddSingleton<RustSdkService>();
|
|
||||||
services.AddSingleton<IPasswordHasher<User>, PasswordHasher<User>>();
|
services.AddSingleton<IPasswordHasher<User>, PasswordHasher<User>>();
|
||||||
|
|
||||||
// Add Data Protection services
|
// Add Data Protection services
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public class RustSdkService
|
|||||||
PropertyNameCaseInsensitive = true
|
PropertyNameCaseInsensitive = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public unsafe UserKeys GenerateUserKeys(string email, string password)
|
public static unsafe UserKeys GenerateUserKeys(string email, string password)
|
||||||
{
|
{
|
||||||
var emailBytes = StringToRustString(email);
|
var emailBytes = StringToRustString(email);
|
||||||
var passwordBytes = StringToRustString(password);
|
var passwordBytes = StringToRustString(password);
|
||||||
@@ -53,7 +53,7 @@ public class RustSdkService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe OrganizationKeys GenerateOrganizationKeys()
|
public static unsafe OrganizationKeys GenerateOrganizationKeys()
|
||||||
{
|
{
|
||||||
var resultPtr = NativeMethods.generate_organization_keys();
|
var resultPtr = NativeMethods.generate_organization_keys();
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ public class RustSdkService
|
|||||||
return JsonSerializer.Deserialize<OrganizationKeys>(result, CaseInsensitiveOptions)!;
|
return JsonSerializer.Deserialize<OrganizationKeys>(result, CaseInsensitiveOptions)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe string GenerateUserOrganizationKey(string userKey, string orgKey)
|
public static unsafe string GenerateUserOrganizationKey(string userKey, string orgKey)
|
||||||
{
|
{
|
||||||
var userKeyBytes = StringToRustString(userKey);
|
var userKeyBytes = StringToRustString(userKey);
|
||||||
var orgKeyBytes = StringToRustString(orgKey);
|
var orgKeyBytes = StringToRustString(orgKey);
|
||||||
@@ -78,7 +78,7 @@ public class RustSdkService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe string EncryptCipher(string cipherViewJson, string symmetricKeyBase64)
|
public static unsafe string EncryptCipher(string cipherViewJson, string symmetricKeyBase64)
|
||||||
{
|
{
|
||||||
var cipherViewBytes = StringToRustString(cipherViewJson);
|
var cipherViewBytes = StringToRustString(cipherViewJson);
|
||||||
var keyBytes = StringToRustString(symmetricKeyBase64);
|
var keyBytes = StringToRustString(symmetricKeyBase64);
|
||||||
@@ -92,7 +92,7 @@ public class RustSdkService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe string DecryptCipher(string cipherJson, string symmetricKeyBase64)
|
public static unsafe string DecryptCipher(string cipherJson, string symmetricKeyBase64)
|
||||||
{
|
{
|
||||||
var cipherBytes = StringToRustString(cipherJson);
|
var cipherBytes = StringToRustString(cipherJson);
|
||||||
var keyBytes = StringToRustString(symmetricKeyBase64);
|
var keyBytes = StringToRustString(symmetricKeyBase64);
|
||||||
@@ -110,7 +110,7 @@ public class RustSdkService
|
|||||||
/// Encrypts a plaintext string using the provided symmetric key.
|
/// Encrypts a plaintext string using the provided symmetric key.
|
||||||
/// Returns an EncString in format "2.{iv}|{data}|{mac}".
|
/// Returns an EncString in format "2.{iv}|{data}|{mac}".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe string EncryptString(string plaintext, string symmetricKeyBase64)
|
public static unsafe string EncryptString(string plaintext, string symmetricKeyBase64)
|
||||||
{
|
{
|
||||||
var plaintextBytes = StringToRustString(plaintext);
|
var plaintextBytes = StringToRustString(plaintext);
|
||||||
var keyBytes = StringToRustString(symmetricKeyBase64);
|
var keyBytes = StringToRustString(symmetricKeyBase64);
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace Bit.Seeder.Factories;
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class CipherSeeder
|
public class CipherSeeder
|
||||||
{
|
{
|
||||||
private readonly RustSdkService _sdkService;
|
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions SdkJsonOptions = new()
|
private static readonly JsonSerializerOptions SdkJsonOptions = new()
|
||||||
{
|
{
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||||
@@ -36,12 +34,7 @@ public class CipherSeeder
|
|||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||||
};
|
};
|
||||||
|
|
||||||
public CipherSeeder(RustSdkService sdkService)
|
public static Cipher CreateOrganizationLoginCipher(
|
||||||
{
|
|
||||||
_sdkService = sdkService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cipher CreateOrganizationLoginCipher(
|
|
||||||
Guid organizationId,
|
Guid organizationId,
|
||||||
string orgKeyBase64,
|
string orgKeyBase64,
|
||||||
string name,
|
string name,
|
||||||
@@ -67,7 +60,7 @@ public class CipherSeeder
|
|||||||
return EncryptAndTransform(cipherView, orgKeyBase64, organizationId);
|
return EncryptAndTransform(cipherView, orgKeyBase64, organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cipher CreateOrganizationLoginCipherWithFields(
|
public static Cipher CreateOrganizationLoginCipherWithFields(
|
||||||
Guid organizationId,
|
Guid organizationId,
|
||||||
string orgKeyBase64,
|
string orgKeyBase64,
|
||||||
string name,
|
string name,
|
||||||
@@ -98,10 +91,10 @@ public class CipherSeeder
|
|||||||
return EncryptAndTransform(cipherView, orgKeyBase64, organizationId);
|
return EncryptAndTransform(cipherView, orgKeyBase64, organizationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cipher EncryptAndTransform(CipherViewDto cipherView, string keyBase64, Guid organizationId)
|
private static Cipher EncryptAndTransform(CipherViewDto cipherView, string keyBase64, Guid organizationId)
|
||||||
{
|
{
|
||||||
var viewJson = JsonSerializer.Serialize(cipherView, SdkJsonOptions);
|
var viewJson = JsonSerializer.Serialize(cipherView, SdkJsonOptions);
|
||||||
var encryptedJson = _sdkService.EncryptCipher(viewJson, keyBase64);
|
var encryptedJson = RustSdkService.EncryptCipher(viewJson, keyBase64);
|
||||||
|
|
||||||
var encryptedDto = JsonSerializer.Deserialize<EncryptedCipherDto>(encryptedJson, SdkJsonOptions)
|
var encryptedDto = JsonSerializer.Deserialize<EncryptedCipherDto>(encryptedJson, SdkJsonOptions)
|
||||||
?? throw new InvalidOperationException("Failed to parse encrypted cipher");
|
?? throw new InvalidOperationException("Failed to parse encrypted cipher");
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ using Bit.RustSDK;
|
|||||||
|
|
||||||
namespace Bit.Seeder.Factories;
|
namespace Bit.Seeder.Factories;
|
||||||
|
|
||||||
public class CollectionSeeder(RustSdkService sdkService)
|
public class CollectionSeeder
|
||||||
{
|
{
|
||||||
public Collection CreateCollection(Guid organizationId, string orgKey, string name)
|
public static Collection CreateCollection(Guid organizationId, string orgKey, string name)
|
||||||
{
|
{
|
||||||
return new Collection
|
return new Collection
|
||||||
{
|
{
|
||||||
Id = Guid.NewGuid(),
|
Id = Guid.NewGuid(),
|
||||||
OrganizationId = organizationId,
|
OrganizationId = organizationId,
|
||||||
Name = sdkService.EncryptString(name, orgKey),
|
Name = RustSdkService.EncryptString(name, orgKey),
|
||||||
CreationDate = DateTime.UtcNow,
|
CreationDate = DateTime.UtcNow,
|
||||||
RevisionDate = DateTime.UtcNow
|
RevisionDate = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Bit.Seeder.Factories;
|
|||||||
/// Factory for creating Folder entities with encrypted names.
|
/// Factory for creating Folder entities with encrypted names.
|
||||||
/// Folders are per-user constructs encrypted with the user's symmetric key.
|
/// Folders are per-user constructs encrypted with the user's symmetric key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal sealed class FolderSeeder(RustSdkService sdkService)
|
internal sealed class FolderSeeder
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a folder with an encrypted name.
|
/// Creates a folder with an encrypted name.
|
||||||
@@ -16,13 +16,13 @@ internal sealed class FolderSeeder(RustSdkService sdkService)
|
|||||||
/// <param name="userId">The user who owns this folder.</param>
|
/// <param name="userId">The user who owns this folder.</param>
|
||||||
/// <param name="userKeyBase64">The user's symmetric key (not org key).</param>
|
/// <param name="userKeyBase64">The user's symmetric key (not org key).</param>
|
||||||
/// <param name="name">The plaintext folder name to encrypt.</param>
|
/// <param name="name">The plaintext folder name to encrypt.</param>
|
||||||
public Folder CreateFolder(Guid userId, string userKeyBase64, string name)
|
public static Folder CreateFolder(Guid userId, string userKeyBase64, string name)
|
||||||
{
|
{
|
||||||
return new Folder
|
return new Folder
|
||||||
{
|
{
|
||||||
Id = CoreHelpers.GenerateComb(),
|
Id = CoreHelpers.GenerateComb(),
|
||||||
UserId = userId,
|
UserId = userId,
|
||||||
Name = sdkService.EncryptString(name, userKeyBase64)
|
Name = RustSdkService.EncryptString(name, userKeyBase64)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ namespace Bit.Seeder.Factories;
|
|||||||
|
|
||||||
public class OrganizationSeeder
|
public class OrganizationSeeder
|
||||||
{
|
{
|
||||||
private static readonly string _defaultPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmIJbGMk6eZqVE7UxhZ46Weu2jKciqOiOkSVYtGvs61rfe9AXxtLaaZEKN4d4DmkZcF6dna2eXNxZmb7U4pwlttye8ksqISe6IUAZQox7auBpjopdCEPhKRg3BD/u8ks9UxSxgWe+fpebjt6gd5hsl1/5HOObn7SeU6EEU04cp3/eH7a4OTdXxB8oN62HGV9kM/ubM1goILgjoSJDbihMK0eb7b8hPHwcA/YOgKKiu/N3FighccdSMD5Pk+HfjacsFNZQa2EsqW09IvvSZ+iL6HQeZ1vwc/6TO1J7EOfJZFQcjoEL9LVI693efYoMZSmrPEWziZ4PvwpOOGo6OObyMQIDAQAB";
|
|
||||||
private static readonly string _defaultPrivateKey = "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=";
|
|
||||||
|
|
||||||
public static Organization CreateEnterprise(string name, string domain, int seats, string? publicKey = null, string? privateKey = null)
|
public static Organization CreateEnterprise(string name, string domain, int seats, string? publicKey = null, string? privateKey = null)
|
||||||
{
|
{
|
||||||
return new Organization
|
return new Organization
|
||||||
@@ -43,36 +40,14 @@ public class OrganizationSeeder
|
|||||||
SyncSeats = true,
|
SyncSeats = true,
|
||||||
Status = OrganizationStatusType.Created,
|
Status = OrganizationStatusType.Created,
|
||||||
MaxStorageGb = 10,
|
MaxStorageGb = 10,
|
||||||
PublicKey = publicKey ?? _defaultPublicKey,
|
PublicKey = publicKey,
|
||||||
PrivateKey = privateKey ?? _defaultPrivateKey,
|
PrivateKey = privateKey
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class OrganizationExtensions
|
public static class OrganizationExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Creates an OrganizationUser with fields populated based on status.
|
|
||||||
/// For Invited status, only user.Email is used. For other statuses, user.Id is used.
|
|
||||||
/// </summary>
|
|
||||||
public static OrganizationUser CreateOrganizationUser(
|
|
||||||
this Organization organization, User user, OrganizationUserType type, OrganizationUserStatusType status)
|
|
||||||
{
|
|
||||||
var isInvited = status == OrganizationUserStatusType.Invited;
|
|
||||||
var isConfirmed = status == OrganizationUserStatusType.Confirmed || status == OrganizationUserStatusType.Revoked;
|
|
||||||
|
|
||||||
return new OrganizationUser
|
|
||||||
{
|
|
||||||
Id = Guid.NewGuid(),
|
|
||||||
OrganizationId = organization.Id,
|
|
||||||
UserId = isInvited ? null : user.Id,
|
|
||||||
Email = isInvited ? user.Email : null,
|
|
||||||
Key = isConfirmed ? "4.rY01mZFXHOsBAg5Fq4gyXuklWfm6mQASm42DJpx05a+e2mmp+P5W6r54WU2hlREX0uoTxyP91bKKwickSPdCQQ58J45LXHdr9t2uzOYyjVzpzebFcdMw1eElR9W2DW8wEk9+mvtWvKwu7yTebzND+46y1nRMoFydi5zPVLSlJEf81qZZ4Uh1UUMLwXz+NRWfixnGXgq2wRq1bH0n3mqDhayiG4LJKgGdDjWXC8W8MMXDYx24SIJrJu9KiNEMprJE+XVF9nQVNijNAjlWBqkDpsfaWTUfeVLRLctfAqW1blsmIv4RQ91PupYJZDNc8nO9ZTF3TEVM+2KHoxzDJrLs2Q==" : null,
|
|
||||||
Type = type,
|
|
||||||
Status = status
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an OrganizationUser with a dynamically provided encrypted org key.
|
/// Creates an OrganizationUser with a dynamically provided encrypted org key.
|
||||||
/// The encryptedOrgKey should be generated using sdkService.GenerateUserOrganizationKey().
|
/// The encryptedOrgKey should be generated using sdkService.GenerateUserOrganizationKey().
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ public struct UserData
|
|||||||
public string Email;
|
public string Email;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class UserSeeder(RustSdkService sdkService, IPasswordHasher<Bit.Core.Entities.User> passwordHasher, MangleId mangleId)
|
public class UserSeeder(IPasswordHasher<Bit.Core.Entities.User> passwordHasher, MangleId mangleId)
|
||||||
{
|
{
|
||||||
private string MangleEmail(string email)
|
private string MangleEmail(string email)
|
||||||
{
|
{
|
||||||
@@ -21,7 +21,7 @@ public class UserSeeder(RustSdkService sdkService, IPasswordHasher<Bit.Core.Enti
|
|||||||
public User CreateUser(string email, bool emailVerified = false, bool premium = false)
|
public User CreateUser(string email, bool emailVerified = false, bool premium = false)
|
||||||
{
|
{
|
||||||
email = MangleEmail(email);
|
email = MangleEmail(email);
|
||||||
var keys = sdkService.GenerateUserKeys(email, DefaultPassword);
|
var keys = RustSdkService.GenerateUserKeys(email, DefaultPassword);
|
||||||
|
|
||||||
var user = new User
|
var user = new User
|
||||||
{
|
{
|
||||||
@@ -76,10 +76,9 @@ public class UserSeeder(RustSdkService sdkService, IPasswordHasher<Bit.Core.Enti
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static User CreateUserWithSdkKeys(
|
public static User CreateUserWithSdkKeys(
|
||||||
string email,
|
string email,
|
||||||
RustSdkService sdkService,
|
|
||||||
IPasswordHasher<User> passwordHasher)
|
IPasswordHasher<User> passwordHasher)
|
||||||
{
|
{
|
||||||
var keys = sdkService.GenerateUserKeys(email, DefaultPassword);
|
var keys = RustSdkService.GenerateUserKeys(email, DefaultPassword);
|
||||||
return CreateUserFromKeys(email, keys, passwordHasher);
|
return CreateUserFromKeys(email, keys, passwordHasher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ public class CollectionsRecipe(DatabaseContext db)
|
|||||||
/// <param name="collections">The number of collections to add.</param>
|
/// <param name="collections">The number of collections to add.</param>
|
||||||
/// <param name="organizationUserIds">The IDs of the users to create relationships with.</param>
|
/// <param name="organizationUserIds">The IDs of the users to create relationships with.</param>
|
||||||
/// <param name="maxUsersWithRelationships">The maximum number of users to create relationships with.</param>
|
/// <param name="maxUsersWithRelationships">The maximum number of users to create relationships with.</param>
|
||||||
public List<Guid> AddToOrganization(Guid organizationId, int collections, List<Guid> organizationUserIds, int maxUsersWithRelationships = 1000)
|
public List<Guid> Seed(Guid organizationId, int collections, List<Guid> organizationUserIds, int maxUsersWithRelationships = 1000)
|
||||||
{
|
{
|
||||||
var collectionList = CreateAndSaveCollections(organizationId, collections);
|
var collectionList = CreateAndSaveCollections(organizationId, collections);
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public class GroupsRecipe(DatabaseContext db)
|
|||||||
/// <param name="groups">The number of groups to add.</param>
|
/// <param name="groups">The number of groups to add.</param>
|
||||||
/// <param name="organizationUserIds">The IDs of the users to create relationships with.</param>
|
/// <param name="organizationUserIds">The IDs of the users to create relationships with.</param>
|
||||||
/// <param name="maxUsersWithRelationships">The maximum number of users to create relationships with.</param>
|
/// <param name="maxUsersWithRelationships">The maximum number of users to create relationships with.</param>
|
||||||
public List<Guid> AddToOrganization(Guid organizationId, int groups, List<Guid> organizationUserIds, int maxUsersWithRelationships = 1000)
|
public List<Guid> Seed(Guid organizationId, int groups, List<Guid> organizationUserIds, int maxUsersWithRelationships = 1000)
|
||||||
{
|
{
|
||||||
var groupList = CreateAndSaveGroups(organizationId, groups);
|
var groupList = CreateAndSaveGroups(organizationId, groups);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ namespace Bit.Seeder.Recipes;
|
|||||||
|
|
||||||
public class OrganizationDomainRecipe(DatabaseContext db)
|
public class OrganizationDomainRecipe(DatabaseContext db)
|
||||||
{
|
{
|
||||||
public void AddVerifiedDomainToOrganization(Guid organizationId, string domainName)
|
public void Seed(Guid organizationId, string domainName)
|
||||||
{
|
{
|
||||||
var domain = new OrganizationDomain
|
var domain = new OrganizationDomain
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,39 +1,66 @@
|
|||||||
using Bit.Core.Entities;
|
using AutoMapper;
|
||||||
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
using Bit.RustSDK;
|
||||||
using Bit.Seeder.Factories;
|
using Bit.Seeder.Factories;
|
||||||
using LinqToDB.EntityFrameworkCore;
|
using LinqToDB.EntityFrameworkCore;
|
||||||
|
using Microsoft.AspNetCore.Identity;
|
||||||
|
using EfOrganization = Bit.Infrastructure.EntityFramework.AdminConsole.Models.Organization;
|
||||||
|
using EfOrganizationUser = Bit.Infrastructure.EntityFramework.Models.OrganizationUser;
|
||||||
|
using EfUser = Bit.Infrastructure.EntityFramework.Models.User;
|
||||||
|
|
||||||
namespace Bit.Seeder.Recipes;
|
namespace Bit.Seeder.Recipes;
|
||||||
|
|
||||||
public class OrganizationWithUsersRecipe(DatabaseContext db)
|
public class OrganizationWithUsersRecipe(DatabaseContext db, IMapper mapper, IPasswordHasher<User> passwordHasher)
|
||||||
{
|
{
|
||||||
public Guid Seed(string name, string domain, int users, OrganizationUserStatusType usersStatus = OrganizationUserStatusType.Confirmed)
|
public Guid Seed(string name, string domain, int users, OrganizationUserStatusType usersStatus = OrganizationUserStatusType.Confirmed)
|
||||||
{
|
{
|
||||||
var seats = Math.Max(users + 1, 1000);
|
var seats = Math.Max(users + 1, 1000);
|
||||||
var organization = OrganizationSeeder.CreateEnterprise(name, domain, seats);
|
|
||||||
var ownerUser = UserSeeder.CreateUserNoMangle($"owner@{domain}");
|
// Generate organization keys
|
||||||
var ownerOrgUser = organization.CreateOrganizationUser(ownerUser, OrganizationUserType.Owner, OrganizationUserStatusType.Confirmed);
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
|
var organization = OrganizationSeeder.CreateEnterprise(
|
||||||
|
name, domain, seats, orgKeys.PublicKey, orgKeys.PrivateKey);
|
||||||
|
|
||||||
|
// Create owner with SDK-generated keys
|
||||||
|
var ownerUser = UserSeeder.CreateUserWithSdkKeys($"owner@{domain}", passwordHasher);
|
||||||
|
var ownerOrgKey = RustSdkService.GenerateUserOrganizationKey(ownerUser.PublicKey!, orgKeys.Key);
|
||||||
|
var ownerOrgUser = organization.CreateOrganizationUserWithKey(
|
||||||
|
ownerUser, OrganizationUserType.Owner, OrganizationUserStatusType.Confirmed, ownerOrgKey);
|
||||||
|
|
||||||
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.CreateUserNoMangle($"user{i}@{domain}");
|
var additionalUser = UserSeeder.CreateUserWithSdkKeys($"user{i}@{domain}", passwordHasher);
|
||||||
additionalUsers.Add(additionalUser);
|
additionalUsers.Add(additionalUser);
|
||||||
additionalOrgUsers.Add(organization.CreateOrganizationUser(additionalUser, OrganizationUserType.User, usersStatus));
|
|
||||||
|
// Generate org key for confirmed/revoked users
|
||||||
|
var shouldHaveKey = usersStatus == OrganizationUserStatusType.Confirmed
|
||||||
|
|| usersStatus == OrganizationUserStatusType.Revoked;
|
||||||
|
var userOrgKey = shouldHaveKey
|
||||||
|
? RustSdkService.GenerateUserOrganizationKey(additionalUser.PublicKey!, orgKeys.Key)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
additionalOrgUsers.Add(organization.CreateOrganizationUserWithKey(
|
||||||
|
additionalUser, OrganizationUserType.User, usersStatus, userOrgKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
db.Add(organization);
|
// Map Core entities to EF entities before adding to DbContext
|
||||||
db.Add(ownerUser);
|
db.Add(mapper.Map<EfOrganization>(organization));
|
||||||
db.Add(ownerOrgUser);
|
db.Add(mapper.Map<EfUser>(ownerUser));
|
||||||
|
db.Add(mapper.Map<EfOrganizationUser>(ownerOrgUser));
|
||||||
|
|
||||||
|
// Map and BulkCopy additional users
|
||||||
|
var efAdditionalUsers = additionalUsers.Select(u => mapper.Map<EfUser>(u)).ToList();
|
||||||
|
var efAdditionalOrgUsers = additionalOrgUsers.Select(ou => mapper.Map<EfOrganizationUser>(ou)).ToList();
|
||||||
|
|
||||||
|
db.BulkCopy(efAdditionalUsers);
|
||||||
|
db.BulkCopy(efAdditionalOrgUsers);
|
||||||
|
|
||||||
db.SaveChanges();
|
db.SaveChanges();
|
||||||
|
|
||||||
// Use LinqToDB's BulkCopy for significant better performance
|
|
||||||
db.BulkCopy(additionalUsers);
|
|
||||||
db.BulkCopy(additionalOrgUsers);
|
|
||||||
|
|
||||||
return organization.Id;
|
return organization.Id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,12 +27,8 @@ namespace Bit.Seeder.Recipes;
|
|||||||
public class OrganizationWithVaultRecipe(
|
public class OrganizationWithVaultRecipe(
|
||||||
DatabaseContext db,
|
DatabaseContext db,
|
||||||
IMapper mapper,
|
IMapper mapper,
|
||||||
RustSdkService sdkService,
|
|
||||||
IPasswordHasher<User> passwordHasher)
|
IPasswordHasher<User> passwordHasher)
|
||||||
{
|
{
|
||||||
private readonly CollectionSeeder _collectionSeeder = new(sdkService);
|
|
||||||
private readonly CipherSeeder _cipherSeeder = new(sdkService);
|
|
||||||
private readonly FolderSeeder _folderSeeder = new(sdkService);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tracks a user with their symmetric key for folder encryption.
|
/// Tracks a user with their symmetric key for folder encryption.
|
||||||
@@ -47,15 +43,15 @@ public class OrganizationWithVaultRecipe(
|
|||||||
public Guid Seed(OrganizationVaultOptions options)
|
public Guid Seed(OrganizationVaultOptions options)
|
||||||
{
|
{
|
||||||
var seats = Math.Max(options.Users + 1, 1000);
|
var seats = Math.Max(options.Users + 1, 1000);
|
||||||
var orgKeys = sdkService.GenerateOrganizationKeys();
|
var orgKeys = RustSdkService.GenerateOrganizationKeys();
|
||||||
|
|
||||||
// Create organization via factory
|
// Create organization via factory
|
||||||
var organization = OrganizationSeeder.CreateEnterprise(
|
var organization = OrganizationSeeder.CreateEnterprise(
|
||||||
options.Name, options.Domain, seats, orgKeys.PublicKey, orgKeys.PrivateKey);
|
options.Name, options.Domain, seats, orgKeys.PublicKey, orgKeys.PrivateKey);
|
||||||
|
|
||||||
// Create owner user via factory
|
// Create owner user via factory
|
||||||
var ownerUser = UserSeeder.CreateUserWithSdkKeys($"owner@{options.Domain}", sdkService, passwordHasher);
|
var ownerUser = UserSeeder.CreateUserWithSdkKeys($"owner@{options.Domain}", passwordHasher);
|
||||||
var ownerOrgKey = sdkService.GenerateUserOrganizationKey(ownerUser.PublicKey!, orgKeys.Key);
|
var ownerOrgKey = RustSdkService.GenerateUserOrganizationKey(ownerUser.PublicKey!, orgKeys.Key);
|
||||||
var ownerOrgUser = organization.CreateOrganizationUserWithKey(
|
var ownerOrgUser = organization.CreateOrganizationUserWithKey(
|
||||||
ownerUser, OrganizationUserType.Owner, OrganizationUserStatusType.Confirmed, ownerOrgKey);
|
ownerUser, OrganizationUserType.Owner, OrganizationUserStatusType.Confirmed, ownerOrgKey);
|
||||||
|
|
||||||
@@ -67,7 +63,7 @@ public class OrganizationWithVaultRecipe(
|
|||||||
for (var i = 0; i < options.Users; i++)
|
for (var i = 0; i < options.Users; i++)
|
||||||
{
|
{
|
||||||
var email = $"user{i}@{options.Domain}";
|
var email = $"user{i}@{options.Domain}";
|
||||||
var userKeys = sdkService.GenerateUserKeys(email, UserSeeder.DefaultPassword);
|
var userKeys = RustSdkService.GenerateUserKeys(email, UserSeeder.DefaultPassword);
|
||||||
var memberUser = UserSeeder.CreateUserFromKeys(email, userKeys, passwordHasher);
|
var memberUser = UserSeeder.CreateUserFromKeys(email, userKeys, passwordHasher);
|
||||||
memberUsersWithKeys.Add(new UserWithKey(memberUser, userKeys.Key));
|
memberUsersWithKeys.Add(new UserWithKey(memberUser, userKeys.Key));
|
||||||
|
|
||||||
@@ -77,7 +73,7 @@ public class OrganizationWithVaultRecipe(
|
|||||||
|
|
||||||
var memberOrgKey = (status == OrganizationUserStatusType.Confirmed ||
|
var memberOrgKey = (status == OrganizationUserStatusType.Confirmed ||
|
||||||
status == OrganizationUserStatusType.Revoked)
|
status == OrganizationUserStatusType.Revoked)
|
||||||
? sdkService.GenerateUserOrganizationKey(memberUser.PublicKey!, orgKeys.Key)
|
? RustSdkService.GenerateUserOrganizationKey(memberUser.PublicKey!, orgKeys.Key)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
memberOrgUsers.Add(organization.CreateOrganizationUserWithKey(
|
memberOrgUsers.Add(organization.CreateOrganizationUserWithKey(
|
||||||
@@ -124,12 +120,12 @@ public class OrganizationWithVaultRecipe(
|
|||||||
{
|
{
|
||||||
var structure = OrgStructures.GetStructure(structureModel.Value);
|
var structure = OrgStructures.GetStructure(structureModel.Value);
|
||||||
collections = structure.Units
|
collections = structure.Units
|
||||||
.Select(unit => _collectionSeeder.CreateCollection(organizationId, orgKeyBase64, unit.Name))
|
.Select(unit => CollectionSeeder.CreateCollection(organizationId, orgKeyBase64, unit.Name))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
collections = [_collectionSeeder.CreateCollection(organizationId, orgKeyBase64, "Default Collection")];
|
collections = [CollectionSeeder.CreateCollection(organizationId, orgKeyBase64, "Default Collection")];
|
||||||
}
|
}
|
||||||
|
|
||||||
db.BulkCopy(collections);
|
db.BulkCopy(collections);
|
||||||
@@ -191,7 +187,7 @@ public class OrganizationWithVaultRecipe(
|
|||||||
.Select(i =>
|
.Select(i =>
|
||||||
{
|
{
|
||||||
var company = companies[i % companies.Length];
|
var company = companies[i % companies.Length];
|
||||||
return _cipherSeeder.CreateOrganizationLoginCipher(
|
return CipherSeeder.CreateOrganizationLoginCipher(
|
||||||
organizationId,
|
organizationId,
|
||||||
orgKeyBase64,
|
orgKeyBase64,
|
||||||
name: $"{company.Name} ({company.Category})",
|
name: $"{company.Name} ({company.Category})",
|
||||||
@@ -285,7 +281,7 @@ public class OrganizationWithVaultRecipe(
|
|||||||
{
|
{
|
||||||
var folderCount = GetFolderCountForUser(userIndex, usersWithKeys.Count, random);
|
var folderCount = GetFolderCountForUser(userIndex, usersWithKeys.Count, random);
|
||||||
return Enumerable.Range(0, folderCount)
|
return Enumerable.Range(0, folderCount)
|
||||||
.Select(folderIndex => _folderSeeder.CreateFolder(
|
.Select(folderIndex => FolderSeeder.CreateFolder(
|
||||||
uwk.User.Id,
|
uwk.User.Id,
|
||||||
uwk.SymmetricKey,
|
uwk.SymmetricKey,
|
||||||
folderNameGenerator.GetFolderName(userIndex * 15 + folderIndex)));
|
folderNameGenerator.GetFolderName(userIndex * 15 + folderIndex)));
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ public class Startup
|
|||||||
|
|
||||||
services.AddScoped<IPasswordHasher<Core.Entities.User>, PasswordHasher<Core.Entities.User>>();
|
services.AddScoped<IPasswordHasher<Core.Entities.User>, PasswordHasher<Core.Entities.User>>();
|
||||||
|
|
||||||
services.AddSingleton<RustSDK.RustSdkService>();
|
|
||||||
services.AddScoped<UserSeeder>();
|
services.AddScoped<UserSeeder>();
|
||||||
|
|
||||||
services.AddSeederApiServices();
|
services.AddSeederApiServices();
|
||||||
|
|||||||
Reference in New Issue
Block a user