mirror of
https://github.com/bitwarden/server.git
synced 2025-02-07 00:21:32 +01:00
* Upgrade AspNetCoreRateLimiter and enable redis distributed cache for rate limiting. - Upgrades AspNetCoreRateLimiter to 4.0.2, which required updating NewtonSoft.Json to 13.0.1. - Replaces Microsoft.Extensions.Caching.Redis with Microsoft.Extensions.Caching.StackExchangeRedis as the original was deprecated and conflicted with the latest AspNetCoreRateLimiter - Adds startup task to Program.cs for Api/Identity projects to support AspNetCoreRateLimiters breaking changes for seeding its stores. - Adds a Redis connection string option to GlobalSettings Signed-off-by: Shane Melton <smelton@bitwarden.com> * Cleanup Redis distributed cache registration - Add new AddDistributedCache service collection extension to add either a Memory or Redis distributed cache. - Remove distributed cache registration from Identity service collection extension. - Add IpRateLimitSeedStartupService.cs to run at application startup to seed the Ip rate limiting policies. Signed-off-by: Shane Melton <smelton@bitwarden.com> * Add caching configuration to SSO Startup.cs Signed-off-by: Shane Melton <smelton@bitwarden.com> * Add ProjectName as an instance name for Redis options Signed-off-by: Shane Melton <smelton@bitwarden.com> * Use distributed cache in CustomIpRateLimitMiddleware.cs Signed-off-by: Shane Melton <smelton@bitwarden.com> * Undo changes to Program.cs and launchSettings.json * Move new service collection extensions to SharedWeb * Upgrade Caching.StackExchangeRedis package to v6 * Cleanup and fix leftover merge conflicts * Remove use of Newtonsoft.Json in distributed cache extensions * Cleanup more formatting * Fix formatting * Fix startup issue caused by merge and fix integration test Signed-off-by: Shane Melton <smelton@bitwarden.com> * Linting fix Signed-off-by: Shane Melton <smelton@bitwarden.com>
104 lines
4.9 KiB
C#
104 lines
4.9 KiB
C#
using AspNetCoreRateLimit;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Services;
|
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Mvc.Testing;
|
|
using Microsoft.AspNetCore.TestHost;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Configuration;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
namespace Bit.IntegrationTestCommon.Factories
|
|
{
|
|
public static class FactoryConstants
|
|
{
|
|
public const string DefaultDatabaseName = "test_database";
|
|
public const string WhitelistedIp = "1.1.1.1";
|
|
}
|
|
|
|
public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
|
|
where T : class
|
|
{
|
|
/// <summary>
|
|
/// The database name to use for this instance of the factory. By default it will use a shared database name so all instances will connect to the same database during it's lifetime.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This will need to be set BEFORE using the <c>Server</c> property
|
|
/// </remarks>
|
|
public string DatabaseName { get; set; } = FactoryConstants.DefaultDatabaseName;
|
|
|
|
/// <summary>
|
|
/// Configure the web host to use an EF in memory database
|
|
/// </summary>
|
|
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
|
{
|
|
builder.ConfigureAppConfiguration(c =>
|
|
{
|
|
c.AddInMemoryCollection(new Dictionary<string, string>
|
|
{
|
|
// Manually insert a EF provider so that ConfigureServices will add EF repositories but we will override
|
|
// DbContextOptions to use an in memory database
|
|
{ "globalSettings:databaseProvider", "postgres" },
|
|
{ "globalSettings:postgreSql:connectionString", "Host=localhost;Username=test;Password=test;Database=test" },
|
|
|
|
// Clear the redis connection string for distributed caching, forcing an in-memory implementation
|
|
{ "globalSettings:redis:connectionString", ""}
|
|
});
|
|
});
|
|
|
|
builder.ConfigureTestServices(services =>
|
|
{
|
|
var dbContextOptions = services.First(sd => sd.ServiceType == typeof(DbContextOptions<DatabaseContext>));
|
|
services.Remove(dbContextOptions);
|
|
services.AddScoped(_ =>
|
|
{
|
|
return new DbContextOptionsBuilder<DatabaseContext>()
|
|
.UseInMemoryDatabase(DatabaseName)
|
|
.Options;
|
|
});
|
|
|
|
// QUESTION: The normal licensing service should run fine on developer machines but not in CI
|
|
// should we have a fork here to leave the normal service for developers?
|
|
// TODO: Eventually add the license file to CI
|
|
var licensingService = services.First(sd => sd.ServiceType == typeof(ILicensingService));
|
|
services.Remove(licensingService);
|
|
services.AddSingleton<ILicensingService, NoopLicensingService>();
|
|
|
|
// FUTURE CONSIDERATION: Add way to run this self hosted/cloud, for now it is cloud only
|
|
var pushRegistrationService = services.First(sd => sd.ServiceType == typeof(IPushRegistrationService));
|
|
services.Remove(pushRegistrationService);
|
|
services.AddSingleton<IPushRegistrationService, NoopPushRegistrationService>();
|
|
|
|
// Even though we are cloud we currently set this up as cloud, we can use the EF/selfhosted service
|
|
// instead of using Noop for this service
|
|
// TODO: Install and use azurite in CI pipeline
|
|
var eventWriteService = services.First(sd => sd.ServiceType == typeof(IEventWriteService));
|
|
services.Remove(eventWriteService);
|
|
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
|
|
|
|
var eventRepositoryService = services.First(sd => sd.ServiceType == typeof(IEventRepository));
|
|
services.Remove(eventRepositoryService);
|
|
services.AddSingleton<IEventRepository, EventRepository>();
|
|
|
|
// Our Rate limiter works so well that it begins to fail tests unless we carve out
|
|
// one whitelisted ip. We should still test the rate limiter though and they should change the Ip
|
|
// to something that is NOT whitelisted
|
|
services.Configure<IpRateLimitOptions>(options =>
|
|
{
|
|
options.IpWhitelist = new List<string>
|
|
{
|
|
FactoryConstants.WhitelistedIp,
|
|
};
|
|
});
|
|
});
|
|
}
|
|
|
|
public DatabaseContext GetDatabaseContext()
|
|
{
|
|
var scope = Services.CreateScope();
|
|
return scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
|
}
|
|
}
|
|
}
|