1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-01 09:42:19 -05:00

[PM-17562] Add feature flag for event-based organization integrations (#5710)

* Added EventBasedOrganizationIntegrations feature flag; Added enforcement of flag at the API layer

* [PM-17562] Use EventBasedOrganizationIntegrations feature flag to turn on/off event queue

* Optimization that removes the need for EventRouteService (from @justindbaur)
This commit is contained in:
Brant DeBow 2025-04-28 08:20:47 -04:00 committed by GitHub
parent 9a7fddd77c
commit ad19d3d3ad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 46 additions and 10 deletions

View File

@ -1,13 +1,16 @@
using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Core;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.AdminConsole.Controllers; namespace Bit.Api.AdminConsole.Controllers;
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
[Route("organizations/{organizationId:guid}/integrations/{integrationId:guid}/configurations")] [Route("organizations/{organizationId:guid}/integrations/{integrationId:guid}/configurations")]
[Authorize("Application")] [Authorize("Application")]
public class OrganizationIntegrationConfigurationController( public class OrganizationIntegrationConfigurationController(

View File

@ -1,8 +1,10 @@
using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Request.Organizations;
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Core;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -10,6 +12,7 @@ using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.AdminConsole.Controllers; namespace Bit.Api.AdminConsole.Controllers;
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
[Route("organizations/{organizationId:guid}/integrations")] [Route("organizations/{organizationId:guid}/integrations")]
[Authorize("Application")] [Authorize("Application")]
public class OrganizationIntegrationController( public class OrganizationIntegrationController(

View File

@ -1,5 +1,6 @@
using System.Text.Json; using System.Text.Json;
using Bit.Api.AdminConsole.Models.Response.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations;
using Bit.Core;
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.Context; using Bit.Core.Context;
using Bit.Core.Enums; using Bit.Core.Enums;
@ -7,11 +8,13 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Data.Integrations; using Bit.Core.Models.Data.Integrations;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace Bit.Api.AdminConsole.Controllers; namespace Bit.Api.AdminConsole.Controllers;
[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)]
[Route("organizations/{organizationId:guid}/integrations/slack")] [Route("organizations/{organizationId:guid}/integrations/slack")]
[Authorize("Application")] [Authorize("Application")]
public class SlackIntegrationController( public class SlackIntegrationController(

View File

@ -108,6 +108,7 @@ public static class FeatureFlagKeys
public const string PolicyRequirements = "pm-14439-policy-requirements"; public const string PolicyRequirements = "pm-14439-policy-requirements";
public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility"; public const string SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility";
public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast"; public const string ScimInviteUserOptimization = "pm-16811-optimize-invite-user-flow-to-fail-fast";
public const string EventBasedOrganizationIntegrations = "event-based-organization-integrations";
/* Auth Team */ /* Auth Team */
public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence"; public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence";

View File

@ -1,4 +1,5 @@
using System.Globalization; using System.Globalization;
using Bit.Core;
using Bit.Core.AdminConsole.Services.Implementations; using Bit.Core.AdminConsole.Services.Implementations;
using Bit.Core.AdminConsole.Services.NoopImplementations; using Bit.Core.AdminConsole.Services.NoopImplementations;
using Bit.Core.Context; using Bit.Core.Context;
@ -62,33 +63,45 @@ public class Startup
{ {
services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>(); services.AddSingleton<IApplicationCacheService, InMemoryApplicationCacheService>();
} }
services.AddScoped<IEventService, EventService>();
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
{ {
services.AddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
{ {
services.AddSingleton<IEventWriteService, AzureServiceBusEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, AzureServiceBusEventWriteService>("broadcast");
} }
else else
{ {
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
} }
} }
else else
{ {
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
{ {
services.AddSingleton<IEventWriteService, RabbitMqEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, RabbitMqEventWriteService>("broadcast");
} }
else else
{ {
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
} }
} }
services.AddScoped<IEventWriteService>(sp =>
{
var featureService = sp.GetRequiredService<IFeatureService>();
var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)
? "broadcast" : "storage";
return sp.GetRequiredKeyedService<IEventWriteService>(key);
});
services.AddScoped<IEventService, EventService>();
services.AddOptionality(); services.AddOptionality();

View File

@ -4,6 +4,7 @@ using System.Security.Claims;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using AspNetCoreRateLimit; using AspNetCoreRateLimit;
using Azure.Storage.Queues; using Azure.Storage.Queues;
using Bit.Core;
using Bit.Core.AdminConsole.Models.Business.Tokenables; using Bit.Core.AdminConsole.Models.Business.Tokenables;
using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.OrganizationFeatures.Policies;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
@ -332,34 +333,46 @@ public static class ServiceCollectionExtensions
if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString))
{ {
services.AddKeyedSingleton<IEventWriteService, AzureQueueEventWriteService>("storage");
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName))
{ {
services.AddSingleton<IEventWriteService, AzureServiceBusEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, AzureServiceBusEventWriteService>("broadcast");
} }
else else
{ {
services.AddSingleton<IEventWriteService, AzureQueueEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
} }
} }
else if (globalSettings.SelfHosted) else if (globalSettings.SelfHosted)
{ {
services.AddKeyedSingleton<IEventWriteService, RepositoryEventWriteService>("storage");
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) &&
CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName))
{ {
services.AddSingleton<IEventWriteService, RabbitMqEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, RabbitMqEventWriteService>("broadcast");
} }
else else
{ {
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
} }
} }
else else
{ {
services.AddSingleton<IEventWriteService, NoopEventWriteService>(); services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("storage");
services.AddKeyedSingleton<IEventWriteService, NoopEventWriteService>("broadcast");
} }
services.AddScoped<IEventWriteService>(sp =>
{
var featureService = sp.GetRequiredService<IFeatureService>();
var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations)
? "broadcast" : "storage";
return sp.GetRequiredKeyedService<IEventWriteService>(key);
});
if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString)) if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString))
{ {