1
0
mirror of https://github.com/bitwarden/server.git synced 2025-06-30 07:36:14 -05:00

Data protection for user columns at rest (#2571)

* ServerProtectedData for user entity

* remove using statements

* formatting

* use data protection libs

* no async

* add data protection to ef user repo

* switch to `SetApplicationName` per ASPNET docs

* null checks

* cleanup

* value converter for EF

* new line at eof

* fix using

* remove folder ref

* restore ctor

* fix lint

* use global constant

* UseApplicationServiceProvider for integration tests

* implement constant for DatabaseFieldProtectedPrefix

* Fix EF IntegrationTest

* restore original values after protect and save

* lint fixes

* Use Constants

Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
Kyle Spearrin
2023-01-18 13:16:57 -05:00
committed by GitHub
parent 22201bf30a
commit 6f04298e17
8 changed files with 199 additions and 14 deletions

View File

@ -1,8 +1,12 @@
using System.Reflection;
using System.Text;
using AutoFixture;
using AutoFixture.Kernel;
using AutoFixture.Xunit2;
using Bit.Core;
using Bit.Core.Test.Helpers.Factories;
using Microsoft.AspNetCore.DataProtection;
using Moq;
namespace Bit.Test.Common.AutoFixture;
@ -15,13 +19,34 @@ public class GlobalSettingsBuilder : ISpecimenBuilder
throw new ArgumentNullException(nameof(context));
}
var pi = request as ParameterInfo;
var fixture = new Fixture();
if (pi == null || pi.ParameterType != typeof(Bit.Core.Settings.GlobalSettings))
if (request is not ParameterInfo pi)
{
return new NoSpecimen();
}
return GlobalSettingsFactory.GlobalSettings;
if (pi.ParameterType == typeof(Bit.Core.Settings.GlobalSettings))
{
return GlobalSettingsFactory.GlobalSettings;
}
if (pi.ParameterType == typeof(IDataProtectionProvider))
{
var dataProtector = new Mock<IDataProtector>();
dataProtector
.Setup(d => d.Unprotect(It.IsAny<byte[]>()))
.Returns<byte[]>(data => Encoding.UTF8.GetBytes(Constants.DatabaseFieldProtectedPrefix + Encoding.UTF8.GetString(data)));
var dataProtectionProvider = new Mock<IDataProtectionProvider>();
dataProtectionProvider
.Setup(x => x.CreateProtector(Constants.DatabaseFieldProtectorPurpose))
.Returns(dataProtector.Object);
return dataProtectionProvider.Object;
}
return new NoSpecimen();
}
}

View File

@ -1,6 +1,11 @@
using Bit.Core.Test.Helpers.Factories;
using System.Text;
using Bit.Core;
using Bit.Core.Test.Helpers.Factories;
using Bit.Infrastructure.EntityFramework.Repositories;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Moq;
namespace Bit.Infrastructure.EFIntegration.Test.Helpers;
@ -10,16 +15,39 @@ public static class DatabaseOptionsFactory
static DatabaseOptionsFactory()
{
var services = new ServiceCollection()
.AddSingleton(sp =>
{
var dataProtector = new Mock<IDataProtector>();
dataProtector
.Setup(d => d.Unprotect(It.IsAny<byte[]>()))
.Returns<byte[]>(data => Encoding.UTF8.GetBytes(Constants.DatabaseFieldProtectedPrefix + Encoding.UTF8.GetString(data)));
var dataProtectionProvider = new Mock<IDataProtectionProvider>();
dataProtectionProvider
.Setup(x => x.CreateProtector(Constants.DatabaseFieldProtectorPurpose))
.Returns(dataProtector.Object);
return dataProtectionProvider.Object;
})
.BuildServiceProvider();
var globalSettings = GlobalSettingsFactory.GlobalSettings;
if (!string.IsNullOrWhiteSpace(GlobalSettingsFactory.GlobalSettings.PostgreSql?.ConnectionString))
{
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
Options.Add(new DbContextOptionsBuilder<DatabaseContext>().UseNpgsql(globalSettings.PostgreSql.ConnectionString).Options);
Options.Add(new DbContextOptionsBuilder<DatabaseContext>()
.UseNpgsql(globalSettings.PostgreSql.ConnectionString)
.UseApplicationServiceProvider(services)
.Options);
}
if (!string.IsNullOrWhiteSpace(GlobalSettingsFactory.GlobalSettings.MySql?.ConnectionString))
{
var mySqlConnectionString = globalSettings.MySql.ConnectionString;
Options.Add(new DbContextOptionsBuilder<DatabaseContext>().UseMySql(mySqlConnectionString, ServerVersion.AutoDetect(mySqlConnectionString)).Options);
Options.Add(new DbContextOptionsBuilder<DatabaseContext>()
.UseMySql(mySqlConnectionString, ServerVersion.AutoDetect(mySqlConnectionString))
.UseApplicationServiceProvider(services)
.Options);
}
}
}

View File

@ -56,10 +56,11 @@ public abstract class WebApplicationFactoryBase<T> : WebApplicationFactory<T>
{
var dbContextOptions = services.First(sd => sd.ServiceType == typeof(DbContextOptions<DatabaseContext>));
services.Remove(dbContextOptions);
services.AddScoped(_ =>
services.AddScoped(services =>
{
return new DbContextOptionsBuilder<DatabaseContext>()
.UseInMemoryDatabase(DatabaseName)
.UseApplicationServiceProvider(services)
.Options;
});