1
0
mirror of https://github.com/bitwarden/server synced 2025-12-16 08:13:33 +00:00

Stricter scene and query types

SeederAPI only serves Scenes, Recipes are inteded to be used locally only.
This commit is contained in:
Matt Gibson
2025-10-29 12:27:15 -07:00
parent 16ee5cfaad
commit 878b78b51e
14 changed files with 261 additions and 204 deletions

View File

@@ -1,4 +1,4 @@
namespace Bit.Seeder;
namespace Bit.Seeder;
public interface IQuery
{
@@ -6,9 +6,9 @@ public interface IQuery
object Execute(object request);
}
public interface IQuery<TRequest> : IQuery where TRequest : class
public interface IQuery<TRequest, TResult> : IQuery where TRequest : class where TResult : class
{
object Execute(TRequest request);
TResult Execute(TRequest request);
Type IQuery.GetRequestType() => typeof(TRequest);
object IQuery.Execute(object request) => Execute((TRequest)request);

View File

@@ -1,15 +1,30 @@
namespace Bit.Seeder;
namespace Bit.Seeder;
public interface IScene
{
Type GetRequestType();
SceneResult Seed(object request);
SceneResult<object?> Seed(object request);
}
/// <summary>
/// Generic scene interface for seeding operations with a specific request type. Does not return a value beyond tracking entities and a mangle map.
/// </summary>
/// <typeparam name="TRequest"></typeparam>
public interface IScene<TRequest> : IScene where TRequest : class
{
SceneResult Seed(TRequest request);
Type IScene.GetRequestType() => typeof(TRequest);
SceneResult<object?> IScene.Seed(object request)
{
var result = Seed((TRequest)request);
return new SceneResult(mangleMap: result.MangleMap, trackedEntities: result.TrackedEntities);
}
}
public interface IScene<TRequest, TResult> : IScene where TRequest : class where TResult : class
{
SceneResult<TResult> Seed(TRequest request);
Type IScene.GetRequestType() => typeof(TRequest);
SceneResult IScene.Seed(object request) => Seed((TRequest)request);
SceneResult<object?> IScene.Seed(object request) => (SceneResult<object?>)Seed((TRequest)request);
}

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Auth.Enums;
using System.ComponentModel.DataAnnotations;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Tokens;
using Bit.Infrastructure.EntityFramework.Repositories;
@@ -9,7 +8,7 @@ namespace Bit.Seeder.Queries;
public class EmergencyAccessInviteQuery(
DatabaseContext db,
IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> dataProtectorTokenizer)
: IQuery<EmergencyAccessInviteQuery.Request>
: IQuery<EmergencyAccessInviteQuery.Request, IEnumerable<string>>
{
public class Request
{
@@ -17,7 +16,7 @@ public class EmergencyAccessInviteQuery(
public required string Email { get; set; }
}
public object Execute(Request request)
public IEnumerable<string> Execute(Request request)
{
var invites = db.EmergencyAccesses
.Where(ea => ea.Email == request.Email).ToList().Select(ea =>

View File

@@ -0,0 +1,6 @@
namespace Bit.Seeder;
public class RecipeResult
{
public Dictionary<string, List<Guid>> TrackedEntities { get; init; } = new();
}

View File

@@ -1,7 +1,35 @@
namespace Bit.Seeder;
public class SceneResult
public class SceneResult : SceneResult<object?>
{
public required object Result { get; init; }
public Dictionary<string, List<Guid>> TrackedEntities { get; init; } = new();
public SceneResult(Dictionary<string, string?> mangleMap, Dictionary<string, List<Guid>> trackedEntities)
: base(result: null, mangleMap: mangleMap, trackedEntities: trackedEntities) { }
}
public class SceneResult<TResult>
{
public TResult Result { get; init; }
public Dictionary<string, string?> MangleMap { get; init; }
public Dictionary<string, List<Guid>> TrackedEntities { get; init; }
public SceneResult(TResult result, Dictionary<string, string?> mangleMap, Dictionary<string, List<Guid>> trackedEntities)
{
Result = result;
MangleMap = mangleMap;
TrackedEntities = trackedEntities;
}
public static explicit operator SceneResult<object?>(SceneResult<TResult> v)
{
var result = v.Result;
if (result is null)
{
return new SceneResult<object?>(result: null, mangleMap: v.MangleMap, trackedEntities: v.TrackedEntities);
}
else
{
return new SceneResult<object?>(result: result, mangleMap: v.MangleMap, trackedEntities: v.TrackedEntities);
}
}
}

View File

@@ -1,5 +1,4 @@
using System.ComponentModel.DataAnnotations;
using Bit.Core.Enums;
using System.ComponentModel.DataAnnotations;
using Bit.Infrastructure.EntityFramework.Repositories;
using Bit.Seeder.Factories;
@@ -22,23 +21,19 @@ public class SingleUserScene(DatabaseContext db, UserSeeder userSeeder) : IScene
db.Add(user);
db.SaveChanges();
return new SceneResult
return new SceneResult(mangleMap: userSeeder.GetMangleMap(user, new UserData
{
Result = userSeeder.GetMangleMap(user, new UserData
{
Email = request.Email,
Id = Guid.Parse("00000000-0000-0000-0000-000000000001"),
Key = "seeded_key",
PublicKey = "seeded_public_key",
PrivateKey = "seeded_private_key",
ApiKey = "seeded_api_key",
Kdf = KdfType.PBKDF2_SHA256,
KdfIterations = 600_000,
}),
TrackedEntities = new Dictionary<string, List<Guid>>
{
["User"] = [user.Id]
}
};
Email = request.Email,
Id = user.Id,
Key = user.Key,
PublicKey = user.PublicKey,
PrivateKey = user.PrivateKey,
ApiKey = user.ApiKey,
Kdf = user.Kdf,
KdfIterations = user.KdfIterations,
}), trackedEntities: new Dictionary<string, List<Guid>>
{
["User"] = [user.Id]
});
}
}