mirror of
https://github.com/bitwarden/server
synced 2026-03-02 19:31:24 +00:00
Merge branch 'main' into auth/pm-29584/create-email-for-emergency-access-removal
This commit is contained in:
30
src/SharedWeb/Play/PlayServiceCollectionExtensions.cs
Normal file
30
src/SharedWeb/Play/PlayServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.SharedWeb.Play.Repositories;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.SharedWeb.Play;
|
||||
|
||||
public static class PlayServiceCollectionExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds PlayId tracking decorators for User and Organization repositories using Dapper implementations.
|
||||
/// This replaces the standard repository implementations with tracking versions
|
||||
/// that record created entities for test data cleanup. Only call when TestPlayIdTrackingEnabled is true.
|
||||
/// </summary>
|
||||
public static void AddPlayIdTrackingDapperRepositories(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IOrganizationRepository, DapperTestOrganizationTrackingOrganizationRepository>();
|
||||
services.AddSingleton<IUserRepository, DapperTestUserTrackingUserRepository>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds PlayId tracking decorators for User and Organization repositories using EntityFramework implementations.
|
||||
/// This replaces the standard repository implementations with tracking versions
|
||||
/// that record created entities for test data cleanup. Only call when TestPlayIdTrackingEnabled is true.
|
||||
/// </summary>
|
||||
public static void AddPlayIdTrackingEFRepositories(this IServiceCollection services)
|
||||
{
|
||||
services.AddSingleton<IOrganizationRepository, EFTestOrganizationTrackingOrganizationRepository>();
|
||||
services.AddSingleton<IUserRepository, EFTestUserTrackingUserRepository>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.SharedWeb.Play.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Dapper decorator around the <see cref="Bit.Infrastructure.Dapper.Repositories.OrganizationRepository"/> that tracks
|
||||
/// created Organizations for seeding.
|
||||
/// </summary>
|
||||
public class DapperTestOrganizationTrackingOrganizationRepository : OrganizationRepository
|
||||
{
|
||||
private readonly IPlayItemService _playItemService;
|
||||
|
||||
public DapperTestOrganizationTrackingOrganizationRepository(
|
||||
IPlayItemService playItemService,
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<OrganizationRepository> logger)
|
||||
: base(globalSettings, logger)
|
||||
{
|
||||
_playItemService = playItemService;
|
||||
}
|
||||
|
||||
public override async Task<Organization> CreateAsync(Organization obj)
|
||||
{
|
||||
var createdOrganization = await base.CreateAsync(obj);
|
||||
await _playItemService.Record(createdOrganization);
|
||||
return createdOrganization;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Infrastructure.Dapper.Repositories;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
|
||||
namespace Bit.SharedWeb.Play.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// Dapper decorator around the <see cref="Bit.Infrastructure.Dapper.Repositories.UserRepository"/> that tracks
|
||||
/// created Users for seeding.
|
||||
/// </summary>
|
||||
public class DapperTestUserTrackingUserRepository : UserRepository
|
||||
{
|
||||
private readonly IPlayItemService _playItemService;
|
||||
|
||||
public DapperTestUserTrackingUserRepository(
|
||||
IPlayItemService playItemService,
|
||||
GlobalSettings globalSettings,
|
||||
IDataProtectionProvider dataProtectionProvider)
|
||||
: base(globalSettings, dataProtectionProvider)
|
||||
{
|
||||
_playItemService = playItemService;
|
||||
}
|
||||
|
||||
public override async Task<User> CreateAsync(User user)
|
||||
{
|
||||
var createdUser = await base.CreateAsync(user);
|
||||
|
||||
await _playItemService.Record(createdUser);
|
||||
return createdUser;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using AutoMapper;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Bit.SharedWeb.Play.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// EntityFramework decorator around the <see cref="Bit.Infrastructure.EntityFramework.Repositories.OrganizationRepository"/> that tracks
|
||||
/// created Organizations for seeding.
|
||||
/// </summary>
|
||||
public class EFTestOrganizationTrackingOrganizationRepository : OrganizationRepository
|
||||
{
|
||||
private readonly IPlayItemService _playItemService;
|
||||
|
||||
public EFTestOrganizationTrackingOrganizationRepository(
|
||||
IPlayItemService playItemService,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IMapper mapper,
|
||||
ILogger<OrganizationRepository> logger)
|
||||
: base(serviceScopeFactory, mapper, logger)
|
||||
{
|
||||
_playItemService = playItemService;
|
||||
}
|
||||
|
||||
public override async Task<Core.AdminConsole.Entities.Organization> CreateAsync(Core.AdminConsole.Entities.Organization organization)
|
||||
{
|
||||
var createdOrganization = await base.CreateAsync(organization);
|
||||
await _playItemService.Record(createdOrganization);
|
||||
return createdOrganization;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using AutoMapper;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.SharedWeb.Play.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// EntityFramework decorator around the <see cref="Bit.Infrastructure.EntityFramework.Repositories.UserRepository"/> that tracks
|
||||
/// created Users for seeding.
|
||||
/// </summary>
|
||||
public class EFTestUserTrackingUserRepository : UserRepository
|
||||
{
|
||||
private readonly IPlayItemService _playItemService;
|
||||
|
||||
public EFTestUserTrackingUserRepository(
|
||||
IPlayItemService playItemService,
|
||||
IServiceScopeFactory serviceScopeFactory,
|
||||
IMapper mapper)
|
||||
: base(serviceScopeFactory, mapper)
|
||||
{
|
||||
_playItemService = playItemService;
|
||||
}
|
||||
|
||||
public override async Task<Core.Entities.User> CreateAsync(Core.Entities.User user)
|
||||
{
|
||||
var createdUser = await base.CreateAsync(user);
|
||||
await _playItemService.Record(createdUser);
|
||||
return createdUser;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- These opt outs should be removed when all warnings are addressed -->
|
||||
<WarningsNotAsErrors>$(WarningsNotAsErrors);CA1304</WarningsNotAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Infrastructure.Dapper\Infrastructure.Dapper.csproj" />
|
||||
<ProjectReference Include="..\Core\Core.csproj" />
|
||||
|
||||
41
src/SharedWeb/Utilities/PlayIdMiddleware.cs
Normal file
41
src/SharedWeb/Utilities/PlayIdMiddleware.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Bit.Core.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Bit.SharedWeb.Utilities;
|
||||
|
||||
/// <summary>
|
||||
/// Middleware to extract the x-play-id header and set it in the PlayIdService.
|
||||
///
|
||||
/// PlayId is used in testing infrastructure to track data created during automated testing and fa cilitate cleanup.
|
||||
/// </summary>
|
||||
/// <param name="next"></param>
|
||||
public sealed class PlayIdMiddleware(RequestDelegate next)
|
||||
{
|
||||
private const int MaxPlayIdLength = 256;
|
||||
|
||||
public async Task Invoke(HttpContext context, PlayIdService playIdService)
|
||||
{
|
||||
if (context.Request.Headers.TryGetValue("x-play-id", out var playId))
|
||||
{
|
||||
var playIdValue = playId.ToString();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(playIdValue))
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
await context.Response.WriteAsJsonAsync(new { Error = "x-play-id header cannot be empty or whitespace" });
|
||||
return;
|
||||
}
|
||||
|
||||
if (playIdValue.Length > MaxPlayIdLength)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
await context.Response.WriteAsJsonAsync(new { Error = $"x-play-id header cannot exceed {MaxPlayIdLength} characters" });
|
||||
return;
|
||||
}
|
||||
|
||||
playIdService.PlayId = playIdValue;
|
||||
}
|
||||
|
||||
await next(context);
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@ using Bit.Core.Vault;
|
||||
using Bit.Core.Vault.Services;
|
||||
using Bit.Infrastructure.Dapper;
|
||||
using Bit.Infrastructure.EntityFramework;
|
||||
using Bit.SharedWeb.Play;
|
||||
using DnsClient;
|
||||
using Duende.IdentityModel;
|
||||
using LaunchDarkly.Sdk.Server;
|
||||
@@ -118,6 +119,40 @@ public static class ServiceCollectionExtensions
|
||||
return provider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers test PlayId tracking services for test data management and cleanup.
|
||||
/// This infrastructure is isolated to test environments and enables tracking of test-generated entities.
|
||||
/// </summary>
|
||||
public static void AddTestPlayIdTracking(this IServiceCollection services, GlobalSettings globalSettings)
|
||||
{
|
||||
if (globalSettings.TestPlayIdTrackingEnabled)
|
||||
{
|
||||
var (provider, _) = GetDatabaseProvider(globalSettings);
|
||||
|
||||
// Include PlayIdService for tracking Play Ids in repositories
|
||||
// We need the http context accessor to use the Singleton version, which pulls from the scoped version
|
||||
services.AddHttpContextAccessor();
|
||||
|
||||
services.AddSingleton<IPlayItemService, PlayItemService>();
|
||||
services.AddSingleton<IPlayIdService, PlayIdSingletonService>();
|
||||
services.AddScoped<PlayIdService>();
|
||||
|
||||
// Replace standard repositories with PlayId tracking decorators
|
||||
if (provider == SupportedDatabaseProviders.SqlServer)
|
||||
{
|
||||
services.AddPlayIdTrackingDapperRepositories();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddPlayIdTrackingEFRepositories();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddSingleton<IPlayIdService, NeverPlayIdServices>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void AddBaseServices(this IServiceCollection services, IGlobalSettings globalSettings)
|
||||
{
|
||||
services.AddScoped<ICipherService, CipherService>();
|
||||
@@ -523,6 +558,10 @@ public static class ServiceCollectionExtensions
|
||||
IWebHostEnvironment env, GlobalSettings globalSettings)
|
||||
{
|
||||
app.UseMiddleware<RequestLoggingMiddleware>();
|
||||
if (globalSettings.TestPlayIdTrackingEnabled)
|
||||
{
|
||||
app.UseMiddleware<PlayIdMiddleware>();
|
||||
}
|
||||
}
|
||||
|
||||
public static void UseForwardedHeaders(this IApplicationBuilder app, IGlobalSettings globalSettings)
|
||||
|
||||
Reference in New Issue
Block a user