The core infrastructure backend (API, database, Docker, etc).
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

80 lines
3.1 KiB

using Bit.Seeder.Factories;
using Bit.Seeder.Models;
using Bit.Seeder.Pipeline;
namespace Bit.Seeder.Steps;
/// <summary>
/// Resolves preset folder assignments, setting <c>Cipher.Folders</c> JSON for each
/// <c>(cipher, user, folder)</c> tuple declared in the preset.
/// </summary>
internal sealed class CreateCipherFoldersStep(List<SeedFolderAssignment> assignments) : IStep
{
public void Execute(SeederContext context)
{
var cipherNames = context.Registry.FixtureCipherNameToId;
var emailToUserId = context.Registry.UserEmailPrefixToUserId;
var namedFolders = context.Registry.UserNamedFolders;
// Phase 1: Validate all references before any mutations
foreach (var a in assignments)
{
if (!cipherNames.ContainsKey(a.Cipher))
{
var available = string.Join(", ", cipherNames.Keys.OrderBy(k => k));
throw new InvalidOperationException(
$"Folder assignment references unknown cipher '{a.Cipher}'. Available ciphers: {available}");
}
if (!emailToUserId.ContainsKey(a.User))
{
var available = string.Join(", ", emailToUserId.Keys.OrderBy(k => k));
throw new InvalidOperationException(
$"Folder assignment references unknown user '{a.User}'. Available users: {available}");
}
if (!namedFolders.TryGetValue(a.User, out var userFolders) || !userFolders.ContainsKey(a.Folder))
{
var available = userFolders != null
? string.Join(", ", userFolders.Keys.OrderBy(k => k))
: "(none)";
throw new InvalidOperationException(
$"Folder assignment references unknown folder '{a.Folder}' for user '{a.User}'. " +
$"Available folders for '{a.User}': {available}");
}
}
// Phase 2: Accumulate (cipherId → {userId → folderId}) mappings
var cipherFolderMap = new Dictionary<Guid, Dictionary<Guid, Guid>>();
var seen = new HashSet<(Guid CipherId, Guid UserId)>();
foreach (var a in assignments)
{
var cipherId = cipherNames[a.Cipher];
var userId = emailToUserId[a.User];
var folderId = namedFolders[a.User][a.Folder];
if (!seen.Add((cipherId, userId)))
{
throw new InvalidOperationException(
$"Duplicate folder assignment: cipher '{a.Cipher}' + user '{a.User}' appears more than once.");
}
if (!cipherFolderMap.TryGetValue(cipherId, out var userFolderMap))
{
userFolderMap = new Dictionary<Guid, Guid>();
cipherFolderMap[cipherId] = userFolderMap;
}
userFolderMap[userId] = folderId;
}
// Phase 3: Set Cipher.Folders JSON
var cipherLookup = context.Ciphers.ToDictionary(c => c.Id);
foreach (var (cipherId, userFolderMap) in cipherFolderMap)
{
cipherLookup[cipherId].Folders = CipherComposer.BuildFoldersJson(userFolderMap);
}
}
}