From ad19d3d3ade994e6a167b2b17d2c065f820f8696 Mon Sep 17 00:00:00 2001 From: Brant DeBow <125889545+brant-livefront@users.noreply.github.com> Date: Mon, 28 Apr 2025 08:20:47 -0400 Subject: [PATCH] [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) --- ...ationIntegrationConfigurationController.cs | 3 +++ .../OrganizationIntegrationController.cs | 3 +++ .../Controllers/SlackIntegrationController.cs | 3 +++ src/Core/Constants.cs | 1 + src/Events/Startup.cs | 23 +++++++++++++++---- .../Utilities/ServiceCollectionExtensions.cs | 23 +++++++++++++++---- 6 files changed, 46 insertions(+), 10 deletions(-) diff --git a/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs b/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs index da0151067b..848098ef00 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationIntegrationConfigurationController.cs @@ -1,13 +1,16 @@ using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations; +using Bit.Core; using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Repositories; +using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.AdminConsole.Controllers; +[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)] [Route("organizations/{organizationId:guid}/integrations/{integrationId:guid}/configurations")] [Authorize("Application")] public class OrganizationIntegrationConfigurationController( diff --git a/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs b/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs index cb96be97c7..3b52e7a8da 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationIntegrationController.cs @@ -1,8 +1,10 @@ using Bit.Api.AdminConsole.Models.Request.Organizations; using Bit.Api.AdminConsole.Models.Response.Organizations; +using Bit.Core; using Bit.Core.Context; using Bit.Core.Exceptions; using Bit.Core.Repositories; +using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -10,6 +12,7 @@ using Microsoft.AspNetCore.Mvc; namespace Bit.Api.AdminConsole.Controllers; +[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)] [Route("organizations/{organizationId:guid}/integrations")] [Authorize("Application")] public class OrganizationIntegrationController( diff --git a/src/Api/AdminConsole/Controllers/SlackIntegrationController.cs b/src/Api/AdminConsole/Controllers/SlackIntegrationController.cs index ed6971911b..a8bef10dc6 100644 --- a/src/Api/AdminConsole/Controllers/SlackIntegrationController.cs +++ b/src/Api/AdminConsole/Controllers/SlackIntegrationController.cs @@ -1,5 +1,6 @@ using System.Text.Json; using Bit.Api.AdminConsole.Models.Response.Organizations; +using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.Context; using Bit.Core.Enums; @@ -7,11 +8,13 @@ using Bit.Core.Exceptions; using Bit.Core.Models.Data.Integrations; using Bit.Core.Repositories; using Bit.Core.Services; +using Bit.Core.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Bit.Api.AdminConsole.Controllers; +[RequireFeature(FeatureFlagKeys.EventBasedOrganizationIntegrations)] [Route("organizations/{organizationId:guid}/integrations/slack")] [Authorize("Application")] public class SlackIntegrationController( diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index b6cc079600..5a7ed13843 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -108,6 +108,7 @@ public static class FeatureFlagKeys public const string PolicyRequirements = "pm-14439-policy-requirements"; 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 EventBasedOrganizationIntegrations = "event-based-organization-integrations"; /* Auth Team */ public const string PM9112DeviceApprovalPersistence = "pm-9112-device-approval-persistence"; diff --git a/src/Events/Startup.cs b/src/Events/Startup.cs index 34ffed4ee6..bb37e240c8 100644 --- a/src/Events/Startup.cs +++ b/src/Events/Startup.cs @@ -1,4 +1,5 @@ using System.Globalization; +using Bit.Core; using Bit.Core.AdminConsole.Services.Implementations; using Bit.Core.AdminConsole.Services.NoopImplementations; using Bit.Core.Context; @@ -62,33 +63,45 @@ public class Startup { services.AddSingleton(); } - services.AddScoped(); + if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } + services.AddScoped(sp => + { + var featureService = sp.GetRequiredService(); + var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations) + ? "broadcast" : "storage"; + return sp.GetRequiredKeyedService(key); + }); + services.AddScoped(); services.AddOptionality(); diff --git a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs index 8d48fc86ef..9883e6db47 100644 --- a/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs +++ b/src/SharedWeb/Utilities/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using AspNetCoreRateLimit; using Azure.Storage.Queues; +using Bit.Core; using Bit.Core.AdminConsole.Models.Business.Tokenables; using Bit.Core.AdminConsole.OrganizationFeatures.Policies; using Bit.Core.AdminConsole.Services; @@ -332,34 +333,46 @@ public static class ServiceCollectionExtensions if (!globalSettings.SelfHosted && CoreHelpers.SettingHasValue(globalSettings.Events.ConnectionString)) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.ConnectionString) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.AzureServiceBus.TopicName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else if (globalSettings.SelfHosted) { + services.AddKeyedSingleton("storage"); + if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.HostName) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Username) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.Password) && CoreHelpers.SettingHasValue(globalSettings.EventLogging.RabbitMq.ExchangeName)) { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } else { - services.AddSingleton(); + services.AddKeyedSingleton("broadcast"); } } else { - services.AddSingleton(); + services.AddKeyedSingleton("storage"); + services.AddKeyedSingleton("broadcast"); } + services.AddScoped(sp => + { + var featureService = sp.GetRequiredService(); + var key = featureService.IsEnabled(FeatureFlagKeys.EventBasedOrganizationIntegrations) + ? "broadcast" : "storage"; + return sp.GetRequiredKeyedService(key); + }); if (CoreHelpers.SettingHasValue(globalSettings.Attachment.ConnectionString)) {