mirror of
https://github.com/bitwarden/server.git
synced 2025-04-04 20:50:21 -05:00
[PM-17562] Add Dapper and EF Repositories For Ogranization Integrations and Configurations (#5589)
* [PM-17562] Add Dapper and EF Repositories For Ogranization Integrations and Configurations * Updated with changes from PR comments
This commit is contained in:
parent
60e9827196
commit
33f5a19b99
@ -0,0 +1,64 @@
|
|||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Data.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationConfigurationDetails
|
||||||
|
{
|
||||||
|
public Guid Id { get; set; }
|
||||||
|
public Guid OrganizationIntegrationId { get; set; }
|
||||||
|
public IntegrationType IntegrationType { get; set; }
|
||||||
|
public EventType EventType { get; set; }
|
||||||
|
public string? Configuration { get; set; }
|
||||||
|
public string? IntegrationConfiguration { get; set; }
|
||||||
|
public string? Template { get; set; }
|
||||||
|
|
||||||
|
public JsonObject MergedConfiguration
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var integrationJson = IntegrationConfigurationJson;
|
||||||
|
|
||||||
|
foreach (var kvp in ConfigurationJson)
|
||||||
|
{
|
||||||
|
integrationJson[kvp.Key] = kvp.Value?.DeepClone();
|
||||||
|
}
|
||||||
|
|
||||||
|
return integrationJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject ConfigurationJson
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var configuration = Configuration ?? string.Empty;
|
||||||
|
return JsonNode.Parse(configuration) as JsonObject ?? new JsonObject();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new JsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private JsonObject IntegrationConfigurationJson
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var integration = IntegrationConfiguration ?? string.Empty;
|
||||||
|
return JsonNode.Parse(integration) as JsonObject ?? new JsonObject();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return new JsonObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Repositories;
|
||||||
|
|
||||||
|
public interface IOrganizationIntegrationConfigurationRepository : IRepository<OrganizationIntegrationConfiguration, Guid>
|
||||||
|
{
|
||||||
|
Task<List<OrganizationIntegrationConfigurationDetails>> GetConfigurationDetailsAsync(
|
||||||
|
Guid organizationId,
|
||||||
|
IntegrationType integrationType,
|
||||||
|
EventType eventType);
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.Repositories;
|
||||||
|
|
||||||
|
public interface IOrganizationIntegrationRepository : IRepository<OrganizationIntegration, Guid>
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
using System.Data;
|
||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Infrastructure.Dapper.Repositories;
|
||||||
|
using Dapper;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.Dapper.AdminConsole.Repositories;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationConfigurationRepository : Repository<OrganizationIntegrationConfiguration, Guid>, IOrganizationIntegrationConfigurationRepository
|
||||||
|
{
|
||||||
|
public OrganizationIntegrationConfigurationRepository(GlobalSettings globalSettings)
|
||||||
|
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public OrganizationIntegrationConfigurationRepository(string connectionString, string readOnlyConnectionString)
|
||||||
|
: base(connectionString, readOnlyConnectionString)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public async Task<List<OrganizationIntegrationConfigurationDetails>> GetConfigurationDetailsAsync(
|
||||||
|
Guid organizationId,
|
||||||
|
IntegrationType integrationType,
|
||||||
|
EventType eventType)
|
||||||
|
{
|
||||||
|
using (var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<OrganizationIntegrationConfigurationDetails>(
|
||||||
|
"[dbo].[OrganizationIntegrationConfigurationDetails_ReadManyByEventTypeOrganizationIdIntegrationType]",
|
||||||
|
new
|
||||||
|
{
|
||||||
|
EventType = eventType,
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
IntegrationType = integrationType
|
||||||
|
},
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
using Bit.Core.AdminConsole.Entities;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Settings;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.Dapper.Repositories;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationRepository : Repository<OrganizationIntegration, Guid>, IOrganizationIntegrationRepository
|
||||||
|
{
|
||||||
|
public OrganizationIntegrationRepository(GlobalSettings globalSettings)
|
||||||
|
: this(globalSettings.SqlServer.ConnectionString, globalSettings.SqlServer.ReadOnlyConnectionString)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public OrganizationIntegrationRepository(string connectionString, string readOnlyConnectionString)
|
||||||
|
: base(connectionString, readOnlyConnectionString)
|
||||||
|
{ }
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationConfigurationRepository : Repository<Core.AdminConsole.Entities.OrganizationIntegrationConfiguration, OrganizationIntegrationConfiguration, Guid>, IOrganizationIntegrationConfigurationRepository
|
||||||
|
{
|
||||||
|
public OrganizationIntegrationConfigurationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
|
||||||
|
: base(serviceScopeFactory, mapper, context => context.OrganizationIntegrationConfigurations)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public async Task<List<OrganizationIntegrationConfigurationDetails>> GetConfigurationDetailsAsync(
|
||||||
|
Guid organizationId,
|
||||||
|
IntegrationType integrationType,
|
||||||
|
EventType eventType)
|
||||||
|
{
|
||||||
|
using (var scope = ServiceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var dbContext = GetDatabaseContext(scope);
|
||||||
|
var query = new OrganizationIntegrationConfigurationDetailsReadManyByEventTypeOrganizationIdIntegrationTypeQuery(
|
||||||
|
organizationId, eventType, integrationType
|
||||||
|
);
|
||||||
|
return await query.Run(dbContext).ToListAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Infrastructure.EntityFramework.AdminConsole.Models;
|
||||||
|
using Bit.Infrastructure.EntityFramework.Repositories;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.AdminConsole.Repositories;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationRepository : Repository<Core.AdminConsole.Entities.OrganizationIntegration, OrganizationIntegration, Guid>, IOrganizationIntegrationRepository
|
||||||
|
{
|
||||||
|
public OrganizationIntegrationRepository(IServiceScopeFactory serviceScopeFactory, IMapper mapper)
|
||||||
|
: base(serviceScopeFactory, mapper, (DatabaseContext context) => context.OrganizationIntegrations)
|
||||||
|
{ }
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
|
||||||
|
namespace Bit.Infrastructure.EntityFramework.Repositories.Queries;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationConfigurationDetailsReadManyByEventTypeOrganizationIdIntegrationTypeQuery : IQuery<OrganizationIntegrationConfigurationDetails>
|
||||||
|
{
|
||||||
|
private readonly Guid _organizationId;
|
||||||
|
private readonly EventType _eventType;
|
||||||
|
private readonly IntegrationType _integrationType;
|
||||||
|
|
||||||
|
public OrganizationIntegrationConfigurationDetailsReadManyByEventTypeOrganizationIdIntegrationTypeQuery(Guid organizationId, EventType eventType, IntegrationType integrationType)
|
||||||
|
{
|
||||||
|
_organizationId = organizationId;
|
||||||
|
_eventType = eventType;
|
||||||
|
_integrationType = integrationType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IQueryable<OrganizationIntegrationConfigurationDetails> Run(DatabaseContext dbContext)
|
||||||
|
{
|
||||||
|
var query = from oic in dbContext.OrganizationIntegrationConfigurations
|
||||||
|
join oi in dbContext.OrganizationIntegrations on oic.OrganizationIntegrationId equals oi.Id into oioic
|
||||||
|
from oi in dbContext.OrganizationIntegrations
|
||||||
|
where oi.OrganizationId == _organizationId &&
|
||||||
|
oi.Type == _integrationType &&
|
||||||
|
oic.EventType == _eventType
|
||||||
|
select new OrganizationIntegrationConfigurationDetails()
|
||||||
|
{
|
||||||
|
Id = oic.Id,
|
||||||
|
OrganizationIntegrationId = oic.OrganizationIntegrationId,
|
||||||
|
IntegrationType = oi.Type,
|
||||||
|
EventType = oic.EventType,
|
||||||
|
Configuration = oic.Configuration,
|
||||||
|
IntegrationConfiguration = oi.Configuration,
|
||||||
|
Template = oic.Template
|
||||||
|
};
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
}
|
@ -78,6 +78,8 @@ public static class EntityFrameworkServiceCollectionExtensions
|
|||||||
services.AddSingleton<IMaintenanceRepository, MaintenanceRepository>();
|
services.AddSingleton<IMaintenanceRepository, MaintenanceRepository>();
|
||||||
services.AddSingleton<IOrganizationApiKeyRepository, OrganizationApiKeyRepository>();
|
services.AddSingleton<IOrganizationApiKeyRepository, OrganizationApiKeyRepository>();
|
||||||
services.AddSingleton<IOrganizationConnectionRepository, OrganizationConnectionRepository>();
|
services.AddSingleton<IOrganizationConnectionRepository, OrganizationConnectionRepository>();
|
||||||
|
services.AddSingleton<IOrganizationIntegrationRepository, OrganizationIntegrationRepository>();
|
||||||
|
services.AddSingleton<IOrganizationIntegrationConfigurationRepository, OrganizationIntegrationConfigurationRepository>();
|
||||||
services.AddSingleton<IOrganizationRepository, OrganizationRepository>();
|
services.AddSingleton<IOrganizationRepository, OrganizationRepository>();
|
||||||
services.AddSingleton<IOrganizationSponsorshipRepository, OrganizationSponsorshipRepository>();
|
services.AddSingleton<IOrganizationSponsorshipRepository, OrganizationSponsorshipRepository>();
|
||||||
services.AddSingleton<IOrganizationUserRepository, OrganizationUserRepository>();
|
services.AddSingleton<IOrganizationUserRepository, OrganizationUserRepository>();
|
||||||
|
@ -55,6 +55,8 @@ public class DatabaseContext : DbContext
|
|||||||
public DbSet<OrganizationApiKey> OrganizationApiKeys { get; set; }
|
public DbSet<OrganizationApiKey> OrganizationApiKeys { get; set; }
|
||||||
public DbSet<OrganizationSponsorship> OrganizationSponsorships { get; set; }
|
public DbSet<OrganizationSponsorship> OrganizationSponsorships { get; set; }
|
||||||
public DbSet<OrganizationConnection> OrganizationConnections { get; set; }
|
public DbSet<OrganizationConnection> OrganizationConnections { get; set; }
|
||||||
|
public DbSet<OrganizationIntegration> OrganizationIntegrations { get; set; }
|
||||||
|
public DbSet<OrganizationIntegrationConfiguration> OrganizationIntegrationConfigurations { get; set; }
|
||||||
public DbSet<OrganizationUser> OrganizationUsers { get; set; }
|
public DbSet<OrganizationUser> OrganizationUsers { get; set; }
|
||||||
public DbSet<Policy> Policies { get; set; }
|
public DbSet<Policy> Policies { get; set; }
|
||||||
public DbSet<Provider> Providers { get; set; }
|
public DbSet<Provider> Providers { get; set; }
|
||||||
|
@ -0,0 +1,82 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using Bit.Core.Models.Data.Organizations;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.Models.Data.Organizations;
|
||||||
|
|
||||||
|
public class OrganizationIntegrationConfigurationDetailsTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void MergedConfiguration_WithValidConfigAndIntegration_ReturnsMergedJson()
|
||||||
|
{
|
||||||
|
var config = new { config = "A new config value" };
|
||||||
|
var integration = new { integration = "An integration value" };
|
||||||
|
var expectedObj = new { integration = "An integration value", config = "A new config value" };
|
||||||
|
var expected = JsonSerializer.Serialize(expectedObj);
|
||||||
|
|
||||||
|
var sut = new OrganizationIntegrationConfigurationDetails();
|
||||||
|
sut.Configuration = JsonSerializer.Serialize(config);
|
||||||
|
sut.IntegrationConfiguration = JsonSerializer.Serialize(integration);
|
||||||
|
|
||||||
|
var result = sut.MergedConfiguration;
|
||||||
|
Assert.Equal(expected, result.ToJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MergedConfiguration_WithInvalidJsonConfigAndIntegration_ReturnsEmptyJson()
|
||||||
|
{
|
||||||
|
var expectedObj = new { };
|
||||||
|
var expected = JsonSerializer.Serialize(expectedObj);
|
||||||
|
|
||||||
|
var sut = new OrganizationIntegrationConfigurationDetails();
|
||||||
|
sut.Configuration = "Not JSON";
|
||||||
|
sut.IntegrationConfiguration = "Not JSON";
|
||||||
|
|
||||||
|
var result = sut.MergedConfiguration;
|
||||||
|
Assert.Equal(expected, result.ToJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MergedConfiguration_WithNullConfigAndIntegration_ReturnsEmptyJson()
|
||||||
|
{
|
||||||
|
var expectedObj = new { };
|
||||||
|
var expected = JsonSerializer.Serialize(expectedObj);
|
||||||
|
|
||||||
|
var sut = new OrganizationIntegrationConfigurationDetails();
|
||||||
|
sut.Configuration = null;
|
||||||
|
sut.IntegrationConfiguration = null;
|
||||||
|
|
||||||
|
var result = sut.MergedConfiguration;
|
||||||
|
Assert.Equal(expected, result.ToJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MergedConfiguration_WithValidIntegrationAndNullConfig_ReturnsIntegrationJson()
|
||||||
|
{
|
||||||
|
var integration = new { integration = "An integration value" };
|
||||||
|
var expectedObj = new { integration = "An integration value" };
|
||||||
|
var expected = JsonSerializer.Serialize(expectedObj);
|
||||||
|
|
||||||
|
var sut = new OrganizationIntegrationConfigurationDetails();
|
||||||
|
sut.Configuration = null;
|
||||||
|
sut.IntegrationConfiguration = JsonSerializer.Serialize(integration);
|
||||||
|
|
||||||
|
var result = sut.MergedConfiguration;
|
||||||
|
Assert.Equal(expected, result.ToJsonString());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void MergedConfiguration_WithValidConfigAndNullIntegration_ReturnsConfigJson()
|
||||||
|
{
|
||||||
|
var config = new { config = "A new config value" };
|
||||||
|
var expectedObj = new { config = "A new config value" };
|
||||||
|
var expected = JsonSerializer.Serialize(expectedObj);
|
||||||
|
|
||||||
|
var sut = new OrganizationIntegrationConfigurationDetails();
|
||||||
|
sut.Configuration = JsonSerializer.Serialize(config);
|
||||||
|
sut.IntegrationConfiguration = null;
|
||||||
|
|
||||||
|
var result = sut.MergedConfiguration;
|
||||||
|
Assert.Equal(expected, result.ToJsonString());
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user