mirror of
https://github.com/bitwarden/server
synced 2025-12-06 00:03:34 +00:00
[PM-17562] Add support for null/all event type (#6100)
* [PM-17562] Add support for null/all event type * Address PR Feedback * Adjusted SQL scripts per feedback
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Api.AdminConsole.Models.Request.Organizations;
|
||||
|
||||
@@ -12,8 +12,7 @@ public class OrganizationIntegrationConfigurationRequestModel
|
||||
{
|
||||
public string? Configuration { get; set; }
|
||||
|
||||
[Required]
|
||||
public EventType EventType { get; set; }
|
||||
public EventType? EventType { get; set; }
|
||||
|
||||
public string? Filters { get; set; }
|
||||
|
||||
|
||||
@@ -25,6 +25,6 @@ public class OrganizationIntegrationConfigurationResponseModel : ResponseModel
|
||||
public string? Configuration { get; set; }
|
||||
public string? Filters { get; set; }
|
||||
public DateTime CreationDate { get; set; }
|
||||
public EventType EventType { get; set; }
|
||||
public EventType? EventType { get; set; }
|
||||
public string? Template { get; set; }
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class OrganizationIntegrationConfiguration : ITableObject<Guid>
|
||||
{
|
||||
public Guid Id { get; set; }
|
||||
public Guid OrganizationIntegrationId { get; set; }
|
||||
public EventType EventType { get; set; }
|
||||
public EventType? EventType { get; set; }
|
||||
public string? Configuration { get; set; }
|
||||
public string? Template { get; set; }
|
||||
public DateTime CreationDate { get; internal set; } = DateTime.UtcNow;
|
||||
|
||||
@@ -11,7 +11,7 @@ public class OrganizationIntegrationConfigurationDetails
|
||||
public Guid OrganizationId { get; set; }
|
||||
public Guid OrganizationIntegrationId { get; set; }
|
||||
public IntegrationType IntegrationType { get; set; }
|
||||
public EventType EventType { get; set; }
|
||||
public EventType? EventType { get; set; }
|
||||
public string? Configuration { get; set; }
|
||||
public string? Filters { get; set; }
|
||||
public string? IntegrationConfiguration { get; set; }
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Bit.Core.Services;
|
||||
|
||||
public class IntegrationConfigurationDetailsCacheService : BackgroundService, IIntegrationConfigurationDetailsCache
|
||||
{
|
||||
private readonly record struct IntegrationCacheKey(Guid OrganizationId, IntegrationType IntegrationType, EventType EventType);
|
||||
private readonly record struct IntegrationCacheKey(Guid OrganizationId, IntegrationType IntegrationType, EventType? EventType);
|
||||
private readonly IOrganizationIntegrationConfigurationRepository _repository;
|
||||
private readonly ILogger<IntegrationConfigurationDetailsCacheService> _logger;
|
||||
private readonly TimeSpan _refreshInterval;
|
||||
@@ -19,8 +19,7 @@ public class IntegrationConfigurationDetailsCacheService : BackgroundService, II
|
||||
public IntegrationConfigurationDetailsCacheService(
|
||||
IOrganizationIntegrationConfigurationRepository repository,
|
||||
GlobalSettings globalSettings,
|
||||
ILogger<IntegrationConfigurationDetailsCacheService> logger
|
||||
)
|
||||
ILogger<IntegrationConfigurationDetailsCacheService> logger)
|
||||
{
|
||||
_repository = repository;
|
||||
_logger = logger;
|
||||
@@ -32,10 +31,21 @@ public class IntegrationConfigurationDetailsCacheService : BackgroundService, II
|
||||
IntegrationType integrationType,
|
||||
EventType eventType)
|
||||
{
|
||||
var key = new IntegrationCacheKey(organizationId, integrationType, eventType);
|
||||
return _cache.TryGetValue(key, out var value)
|
||||
? value
|
||||
: new List<OrganizationIntegrationConfigurationDetails>();
|
||||
var specificKey = new IntegrationCacheKey(organizationId, integrationType, eventType);
|
||||
var allEventsKey = new IntegrationCacheKey(organizationId, integrationType, null);
|
||||
|
||||
var results = new List<OrganizationIntegrationConfigurationDetails>();
|
||||
|
||||
if (_cache.TryGetValue(specificKey, out var specificConfigs))
|
||||
{
|
||||
results.AddRange(specificConfigs);
|
||||
}
|
||||
if (_cache.TryGetValue(allEventsKey, out var fallbackConfigs))
|
||||
{
|
||||
results.AddRange(fallbackConfigs);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
|
||||
@@ -2,7 +2,7 @@ CREATE TABLE [dbo].[OrganizationIntegrationConfiguration]
|
||||
(
|
||||
[Id] UNIQUEIDENTIFIER NOT NULL,
|
||||
[OrganizationIntegrationId] UNIQUEIDENTIFIER NOT NULL,
|
||||
[EventType] SMALLINT NOT NULL,
|
||||
[EventType] SMALLINT NULL,
|
||||
[Configuration] VARCHAR (MAX) NULL,
|
||||
[Template] VARCHAR (MAX) NULL,
|
||||
[CreationDate] DATETIME2 (7) NOT NULL,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#nullable enable
|
||||
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
@@ -28,18 +29,55 @@ public class IntegrationConfigurationDetailsCacheServiceTests
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetConfigurationDetails_KeyExists_ReturnsExpectedList(OrganizationIntegrationConfigurationDetails config)
|
||||
public async Task GetConfigurationDetails_SpecificKeyExists_ReturnsExpectedList(OrganizationIntegrationConfigurationDetails config)
|
||||
{
|
||||
config.EventType = EventType.Cipher_Created;
|
||||
var sutProvider = GetSutProvider([config]);
|
||||
await sutProvider.Sut.RefreshAsync();
|
||||
var result = sutProvider.Sut.GetConfigurationDetails(
|
||||
config.OrganizationId,
|
||||
config.IntegrationType,
|
||||
config.EventType);
|
||||
EventType.Cipher_Created);
|
||||
Assert.Single(result);
|
||||
Assert.Same(config, result[0]);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetConfigurationDetails_AllEventsKeyExists_ReturnsExpectedList(OrganizationIntegrationConfigurationDetails config)
|
||||
{
|
||||
config.EventType = null;
|
||||
var sutProvider = GetSutProvider([config]);
|
||||
await sutProvider.Sut.RefreshAsync();
|
||||
var result = sutProvider.Sut.GetConfigurationDetails(
|
||||
config.OrganizationId,
|
||||
config.IntegrationType,
|
||||
EventType.Cipher_Created);
|
||||
Assert.Single(result);
|
||||
Assert.Same(config, result[0]);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetConfigurationDetails_BothSpecificAndAllEventsKeyExists_ReturnsExpectedList(
|
||||
OrganizationIntegrationConfigurationDetails specificConfig,
|
||||
OrganizationIntegrationConfigurationDetails allKeysConfig
|
||||
)
|
||||
{
|
||||
specificConfig.EventType = EventType.Cipher_Created;
|
||||
allKeysConfig.EventType = null;
|
||||
allKeysConfig.OrganizationId = specificConfig.OrganizationId;
|
||||
allKeysConfig.IntegrationType = specificConfig.IntegrationType;
|
||||
|
||||
var sutProvider = GetSutProvider([specificConfig, allKeysConfig]);
|
||||
await sutProvider.Sut.RefreshAsync();
|
||||
var result = sutProvider.Sut.GetConfigurationDetails(
|
||||
specificConfig.OrganizationId,
|
||||
specificConfig.IntegrationType,
|
||||
EventType.Cipher_Created);
|
||||
Assert.Equal(2, result.Count);
|
||||
Assert.Contains(result, r => r.Template == specificConfig.Template);
|
||||
Assert.Contains(result, r => r.Template == allKeysConfig.Template);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetConfigurationDetails_KeyMissing_ReturnsEmptyList(OrganizationIntegrationConfigurationDetails config)
|
||||
{
|
||||
@@ -48,10 +86,12 @@ public class IntegrationConfigurationDetailsCacheServiceTests
|
||||
var result = sutProvider.Sut.GetConfigurationDetails(
|
||||
Guid.NewGuid(),
|
||||
config.IntegrationType,
|
||||
config.EventType);
|
||||
config.EventType ?? EventType.Cipher_Created);
|
||||
Assert.Empty(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task GetConfigurationDetails_ReturnsCachedValue_EvenIfRepositoryChanges(OrganizationIntegrationConfigurationDetails config)
|
||||
{
|
||||
@@ -67,7 +107,7 @@ public class IntegrationConfigurationDetailsCacheServiceTests
|
||||
var result = sutProvider.Sut.GetConfigurationDetails(
|
||||
config.OrganizationId,
|
||||
config.IntegrationType,
|
||||
config.EventType);
|
||||
config.EventType ?? EventType.Cipher_Created);
|
||||
Assert.Single(result);
|
||||
Assert.NotEqual("Changed", result[0].Template); // should not yet pick up change from repository
|
||||
|
||||
@@ -76,7 +116,7 @@ public class IntegrationConfigurationDetailsCacheServiceTests
|
||||
result = sutProvider.Sut.GetConfigurationDetails(
|
||||
config.OrganizationId,
|
||||
config.IntegrationType,
|
||||
config.EventType);
|
||||
config.EventType ?? EventType.Cipher_Created);
|
||||
Assert.Single(result);
|
||||
Assert.Equal("Changed", result[0].Template); // Should have the new value
|
||||
}
|
||||
@@ -94,7 +134,7 @@ public class IntegrationConfigurationDetailsCacheServiceTests
|
||||
var results = sutProvider.Sut.GetConfigurationDetails(
|
||||
config1.OrganizationId,
|
||||
config1.IntegrationType,
|
||||
config1.EventType);
|
||||
config1.EventType ?? EventType.Cipher_Created);
|
||||
|
||||
Assert.Equal(2, results.Count);
|
||||
Assert.Contains(results, r => r.Template == config1.Template);
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM sys.columns
|
||||
WHERE object_id = OBJECT_ID(N'[dbo].[OrganizationIntegrationConfiguration]')
|
||||
AND name = 'EventType'
|
||||
AND is_nullable = 0 -- Currently NOT NULL
|
||||
)
|
||||
BEGIN
|
||||
ALTER TABLE [dbo].[OrganizationIntegrationConfiguration]
|
||||
ALTER COLUMN [EventType] SMALLINT NULL
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationIntegrationConfiguration_Create]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@OrganizationIntegrationId UNIQUEIDENTIFIER,
|
||||
@EventType SMALLINT,
|
||||
@Configuration VARCHAR(MAX),
|
||||
@Template VARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@Filters VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
INSERT INTO [dbo].[OrganizationIntegrationConfiguration]
|
||||
(
|
||||
[Id],
|
||||
[OrganizationIntegrationId],
|
||||
[EventType],
|
||||
[Configuration],
|
||||
[Template],
|
||||
[CreationDate],
|
||||
[RevisionDate],
|
||||
[Filters]
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@Id,
|
||||
@OrganizationIntegrationId,
|
||||
@EventType,
|
||||
@Configuration,
|
||||
@Template,
|
||||
@CreationDate,
|
||||
@RevisionDate,
|
||||
@Filters
|
||||
)
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE [dbo].[OrganizationIntegrationConfiguration_Update]
|
||||
@Id UNIQUEIDENTIFIER OUTPUT,
|
||||
@OrganizationIntegrationId UNIQUEIDENTIFIER,
|
||||
@EventType SMALLINT,
|
||||
@Configuration VARCHAR(MAX),
|
||||
@Template VARCHAR(MAX),
|
||||
@CreationDate DATETIME2(7),
|
||||
@RevisionDate DATETIME2(7),
|
||||
@Filters VARCHAR(MAX) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON
|
||||
|
||||
UPDATE
|
||||
[dbo].[OrganizationIntegrationConfiguration]
|
||||
SET
|
||||
[OrganizationIntegrationId] = @OrganizationIntegrationId,
|
||||
[EventType] = @EventType,
|
||||
[Configuration] = @Configuration,
|
||||
[Template] = @Template,
|
||||
[CreationDate] = @CreationDate,
|
||||
[RevisionDate] = @RevisionDate,
|
||||
[Filters] = @Filters
|
||||
WHERE
|
||||
[Id] = @Id
|
||||
END
|
||||
GO
|
||||
|
||||
CREATE OR ALTER VIEW [dbo].[OrganizationIntegrationConfigurationView]
|
||||
AS
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
[dbo].[OrganizationIntegrationConfiguration]
|
||||
GO
|
||||
|
||||
CREATE OR ALTER VIEW [dbo].[OrganizationIntegrationConfigurationDetailsView]
|
||||
AS
|
||||
SELECT
|
||||
oi.[OrganizationId],
|
||||
oi.[Type] AS [IntegrationType],
|
||||
oic.[EventType],
|
||||
oic.[Configuration],
|
||||
oi.[Configuration] AS [IntegrationConfiguration],
|
||||
oic.[Template],
|
||||
oic.[Filters]
|
||||
FROM
|
||||
[dbo].[OrganizationIntegrationConfiguration] oic
|
||||
INNER JOIN
|
||||
[dbo].[OrganizationIntegration] oi ON oi.[Id] = oic.[OrganizationIntegrationId]
|
||||
GO
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.MySqlMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AllowNullEventTypeOrganizationIntegrationConfiguration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "int",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
@@ -313,7 +313,7 @@ namespace Bit.MySqlMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
b.Property<int?>("EventType")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Filters")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.PostgresMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AllowNullEventTypeOrganizationIntegrationConfiguration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "integer",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "integer",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "integer",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
@@ -316,7 +316,7 @@ namespace Bit.PostgresMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("timestamp with time zone");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
b.Property<int?>("EventType")
|
||||
.HasColumnType("integer");
|
||||
|
||||
b.Property<string>("Filters")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Bit.SqliteMigrations.Migrations;
|
||||
|
||||
/// <inheritdoc />
|
||||
public partial class AllowNullEventTypeOrganizationIntegrationConfiguration : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "INTEGER",
|
||||
nullable: true,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EventType",
|
||||
table: "OrganizationIntegrationConfiguration",
|
||||
type: "INTEGER",
|
||||
nullable: false,
|
||||
defaultValue: 0,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "INTEGER",
|
||||
oldNullable: true);
|
||||
}
|
||||
}
|
||||
@@ -308,7 +308,7 @@ namespace Bit.SqliteMigrations.Migrations
|
||||
b.Property<DateTime>("CreationDate")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("EventType")
|
||||
b.Property<int?>("EventType")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("Filters")
|
||||
|
||||
Reference in New Issue
Block a user