From 974d23efdd00fc54de52954c34c43e42e2a51e8a Mon Sep 17 00:00:00 2001 From: Matt Bishop Date: Thu, 18 Jan 2024 09:47:34 -0500 Subject: [PATCH] Establish IFeatureService as scoped (#3679) * Establish IFeatureService as scoped * Lint * Feedback around injection --- src/Admin/Controllers/UsersController.cs | 2 +- .../Controllers/GroupsController.cs | 1 - .../Controllers/OrganizationsController.cs | 2 +- .../Auth/Controllers/AccountsController.cs | 8 +-- src/Api/Controllers/ConfigController.cs | 6 +- .../Vault/Controllers/CiphersController.cs | 2 +- src/Api/Vault/Controllers/SyncController.cs | 6 +- .../Validators/CipherRotationValidator.cs | 9 +-- .../Implementations/OrganizationService.cs | 6 +- .../Implementations/EmergencyAccessService.cs | 6 +- src/Core/Context/CurrentContext.cs | 6 +- src/Core/Services/IFeatureService.cs | 16 ++--- .../Implementations/CollectionService.cs | 4 +- .../LaunchDarklyFeatureService.cs | 64 ++++++++++--------- src/Core/Utilities/RequireFeatureAttribute.cs | 6 +- .../Services/Implementations/CipherService.cs | 2 +- src/Events/Controllers/CollectController.cs | 2 +- src/Events/Startup.cs | 2 +- .../IdentityServer/WebAuthnGrantValidator.cs | 2 +- src/Notifications/NotificationsHub.cs | 4 +- .../Utilities/ServiceCollectionExtensions.cs | 19 +++++- .../Controllers/AccountsControllerTests.cs | 4 -- .../Controllers/ConfigControllerTests.cs | 6 +- .../Services/CollectionServiceTests.cs | 2 +- .../LaunchDarklyFeatureServiceTests.cs | 38 ++++------- .../Utilities/RequireFeatureAttributeTests.cs | 2 +- 26 files changed, 96 insertions(+), 131 deletions(-) diff --git a/src/Admin/Controllers/UsersController.cs b/src/Admin/Controllers/UsersController.cs index e4ff88a68..ba9d04e3a 100644 --- a/src/Admin/Controllers/UsersController.cs +++ b/src/Admin/Controllers/UsersController.cs @@ -27,7 +27,7 @@ public class UsersController : Controller private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); public UsersController( IUserRepository userRepository, diff --git a/src/Api/AdminConsole/Controllers/GroupsController.cs b/src/Api/AdminConsole/Controllers/GroupsController.cs index 3a256043a..9fa392af7 100644 --- a/src/Api/AdminConsole/Controllers/GroupsController.cs +++ b/src/Api/AdminConsole/Controllers/GroupsController.cs @@ -37,7 +37,6 @@ public class GroupsController : Controller ICreateGroupCommand createGroupCommand, IUpdateGroupCommand updateGroupCommand, IDeleteGroupCommand deleteGroupCommand, - IFeatureService featureService, IAuthorizationService authorizationService, IApplicationCacheService applicationCacheService) { diff --git a/src/Api/AdminConsole/Controllers/OrganizationsController.cs b/src/Api/AdminConsole/Controllers/OrganizationsController.cs index e4f0c3aa5..da0b8d4e2 100644 --- a/src/Api/AdminConsole/Controllers/OrganizationsController.cs +++ b/src/Api/AdminConsole/Controllers/OrganizationsController.cs @@ -803,7 +803,7 @@ public class OrganizationsController : Controller throw new BadRequestException("Organization does not have collection enhancements enabled"); } - var v1Enabled = _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, _currentContext); + var v1Enabled = _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1); if (!v1Enabled) { diff --git a/src/Api/Auth/Controllers/AccountsController.cs b/src/Api/Auth/Controllers/AccountsController.cs index 0818bb13f..a4b41310e 100644 --- a/src/Api/Auth/Controllers/AccountsController.cs +++ b/src/Api/Auth/Controllers/AccountsController.cs @@ -21,7 +21,6 @@ using Bit.Core.Auth.Services; using Bit.Core.Auth.UserFeatures.UserKey; using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces; using Bit.Core.Auth.Utilities; -using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -63,10 +62,9 @@ public class AccountsController : Controller private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand; private readonly IRotateUserKeyCommand _rotateUserKeyCommand; private readonly IFeatureService _featureService; - private readonly ICurrentContext _currentContext; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); private readonly IRotationValidator, IEnumerable> _cipherValidator; private readonly IRotationValidator, IEnumerable> _folderValidator; @@ -95,7 +93,6 @@ public class AccountsController : Controller ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand, IRotateUserKeyCommand rotateUserKeyCommand, IFeatureService featureService, - ICurrentContext currentContext, IRotationValidator, IEnumerable> cipherValidator, IRotationValidator, IEnumerable> folderValidator, IRotationValidator, IReadOnlyList> sendValidator, @@ -121,7 +118,6 @@ public class AccountsController : Controller _setInitialMasterPasswordCommand = setInitialMasterPasswordCommand; _rotateUserKeyCommand = rotateUserKeyCommand; _featureService = featureService; - _currentContext = currentContext; _cipherValidator = cipherValidator; _folderValidator = folderValidator; _sendValidator = sendValidator; @@ -425,7 +421,7 @@ public class AccountsController : Controller } IdentityResult result; - if (_featureService.IsEnabled(FeatureFlagKeys.KeyRotationImprovements, _currentContext)) + if (_featureService.IsEnabled(FeatureFlagKeys.KeyRotationImprovements)) { var dataModel = new RotateUserKeyData { diff --git a/src/Api/Controllers/ConfigController.cs b/src/Api/Controllers/ConfigController.cs index 167de7c90..7699c6b11 100644 --- a/src/Api/Controllers/ConfigController.cs +++ b/src/Api/Controllers/ConfigController.cs @@ -1,5 +1,4 @@ using Bit.Api.Models.Response; -using Bit.Core.Context; using Bit.Core.Services; using Bit.Core.Settings; @@ -11,22 +10,19 @@ namespace Bit.Api.Controllers; public class ConfigController : Controller { private readonly IGlobalSettings _globalSettings; - private readonly ICurrentContext _currentContext; private readonly IFeatureService _featureService; public ConfigController( IGlobalSettings globalSettings, - ICurrentContext currentContext, IFeatureService featureService) { _globalSettings = globalSettings; - _currentContext = currentContext; _featureService = featureService; } [HttpGet("")] public ConfigResponseModel GetConfigs() { - return new ConfigResponseModel(_globalSettings, _featureService.GetAll(_currentContext)); + return new ConfigResponseModel(_globalSettings, _featureService.GetAll()); } } diff --git a/src/Api/Vault/Controllers/CiphersController.cs b/src/Api/Vault/Controllers/CiphersController.cs index a99d2b01c..1bf20f4c5 100644 --- a/src/Api/Vault/Controllers/CiphersController.cs +++ b/src/Api/Vault/Controllers/CiphersController.cs @@ -43,7 +43,7 @@ public class CiphersController : Controller private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); public CiphersController( ICipherRepository cipherRepository, diff --git a/src/Api/Vault/Controllers/SyncController.cs b/src/Api/Vault/Controllers/SyncController.cs index 5835b9ebe..80e7d1a7e 100644 --- a/src/Api/Vault/Controllers/SyncController.cs +++ b/src/Api/Vault/Controllers/SyncController.cs @@ -3,7 +3,6 @@ using Bit.Core; using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Enums.Provider; using Bit.Core.AdminConsole.Repositories; -using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -32,11 +31,10 @@ public class SyncController : Controller private readonly IPolicyRepository _policyRepository; private readonly ISendRepository _sendRepository; private readonly GlobalSettings _globalSettings; - private readonly ICurrentContext _currentContext; private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); public SyncController( IUserService userService, @@ -49,7 +47,6 @@ public class SyncController : Controller IPolicyRepository policyRepository, ISendRepository sendRepository, GlobalSettings globalSettings, - ICurrentContext currentContext, IFeatureService featureService) { _userService = userService; @@ -62,7 +59,6 @@ public class SyncController : Controller _policyRepository = policyRepository; _sendRepository = sendRepository; _globalSettings = globalSettings; - _currentContext = currentContext; _featureService = featureService; } diff --git a/src/Api/Vault/Validators/CipherRotationValidator.cs b/src/Api/Vault/Validators/CipherRotationValidator.cs index 9259235d8..2f5ae36ef 100644 --- a/src/Api/Vault/Validators/CipherRotationValidator.cs +++ b/src/Api/Vault/Validators/CipherRotationValidator.cs @@ -1,7 +1,6 @@ using Bit.Api.Auth.Validators; using Bit.Api.Vault.Models.Request; using Bit.Core; -using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Exceptions; using Bit.Core.Services; @@ -13,19 +12,15 @@ namespace Bit.Api.Vault.Validators; public class CipherRotationValidator : IRotationValidator, IEnumerable> { private readonly ICipherRepository _cipherRepository; - private readonly ICurrentContext _currentContext; private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); - public CipherRotationValidator(ICipherRepository cipherRepository, ICurrentContext currentContext, - IFeatureService featureService) + public CipherRotationValidator(ICipherRepository cipherRepository, IFeatureService featureService) { _cipherRepository = cipherRepository; - _currentContext = currentContext; _featureService = featureService; - } public async Task> ValidateAsync(User user, IEnumerable ciphers) diff --git a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs index 1355a1512..7adf30f85 100644 --- a/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs +++ b/src/Core/AdminConsole/Services/Implementations/OrganizationService.cs @@ -442,10 +442,10 @@ public class OrganizationService : IOrganizationService } var flexibleCollectionsSignupEnabled = - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup); var flexibleCollectionsV1IsEnabled = - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1); var organization = new Organization { @@ -572,7 +572,7 @@ public class OrganizationService : IOrganizationService await ValidateSignUpPoliciesAsync(owner.Id); var flexibleCollectionsSignupEnabled = - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsSignup); var organization = new Organization { diff --git a/src/Core/Auth/Services/Implementations/EmergencyAccessService.cs b/src/Core/Auth/Services/Implementations/EmergencyAccessService.cs index 34c0e8e0e..6936ce303 100644 --- a/src/Core/Auth/Services/Implementations/EmergencyAccessService.cs +++ b/src/Core/Auth/Services/Implementations/EmergencyAccessService.cs @@ -5,7 +5,6 @@ using Bit.Core.Auth.Enums; using Bit.Core.Auth.Models; using Bit.Core.Auth.Models.Business.Tokenables; using Bit.Core.Auth.Models.Data; -using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -34,11 +33,10 @@ public class EmergencyAccessService : IEmergencyAccessService private readonly IPasswordHasher _passwordHasher; private readonly IOrganizationService _organizationService; private readonly IDataProtectorTokenFactory _dataProtectorTokenizer; - private readonly ICurrentContext _currentContext; private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); public EmergencyAccessService( IEmergencyAccessRepository emergencyAccessRepository, @@ -53,7 +51,6 @@ public class EmergencyAccessService : IEmergencyAccessService GlobalSettings globalSettings, IOrganizationService organizationService, IDataProtectorTokenFactory dataProtectorTokenizer, - ICurrentContext currentContext, IFeatureService featureService) { _emergencyAccessRepository = emergencyAccessRepository; @@ -68,7 +65,6 @@ public class EmergencyAccessService : IEmergencyAccessService _globalSettings = globalSettings; _organizationService = organizationService; _dataProtectorTokenizer = dataProtectorTokenizer; - _currentContext = currentContext; _featureService = featureService; } diff --git a/src/Core/Context/CurrentContext.cs b/src/Core/Context/CurrentContext.cs index 129e90e39..cc74e60a8 100644 --- a/src/Core/Context/CurrentContext.cs +++ b/src/Core/Context/CurrentContext.cs @@ -8,7 +8,6 @@ using Bit.Core.Enums; using Bit.Core.Identity; using Bit.Core.Models.Data; using Bit.Core.Repositories; -using Bit.Core.Services; using Bit.Core.Settings; using Bit.Core.Utilities; using Microsoft.AspNetCore.Http; @@ -19,7 +18,6 @@ public class CurrentContext : ICurrentContext { private readonly IProviderOrganizationRepository _providerOrganizationRepository; private readonly IProviderUserRepository _providerUserRepository; - private readonly IFeatureService _featureService; private bool _builtHttpContext; private bool _builtClaimsPrincipal; private IEnumerable _providerOrganizationProviderDetails; @@ -46,12 +44,10 @@ public class CurrentContext : ICurrentContext public CurrentContext( IProviderOrganizationRepository providerOrganizationRepository, - IProviderUserRepository providerUserRepository, - IFeatureService featureService) + IProviderUserRepository providerUserRepository) { _providerOrganizationRepository = providerOrganizationRepository; _providerUserRepository = providerUserRepository; - _featureService = featureService; ; } public async virtual Task BuildAsync(HttpContext httpContext, GlobalSettings globalSettings) diff --git a/src/Core/Services/IFeatureService.cs b/src/Core/Services/IFeatureService.cs index a85b16ec8..0ac168a0c 100644 --- a/src/Core/Services/IFeatureService.cs +++ b/src/Core/Services/IFeatureService.cs @@ -1,6 +1,4 @@ -using Bit.Core.Context; - -namespace Bit.Core.Services; +namespace Bit.Core.Services; public interface IFeatureService { @@ -14,33 +12,29 @@ public interface IFeatureService /// Checks whether a given feature is enabled. /// /// The key of the feature to check. - /// A context providing information that can be used to evaluate whether a feature should be on or off. /// The default value for the feature. /// True if the feature is enabled, otherwise false. - bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false); + bool IsEnabled(string key, bool defaultValue = false); /// /// Gets the integer variation of a feature. /// /// The key of the feature to check. - /// A context providing information that can be used to evaluate the feature value. /// The default value for the feature. /// The feature variation value. - int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0); + int GetIntVariation(string key, int defaultValue = 0); /// /// Gets the string variation of a feature. /// /// The key of the feature to check. - /// A context providing information that can be used to evaluate the feature value. /// The default value for the feature. /// The feature variation value. - string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null); + string GetStringVariation(string key, string defaultValue = null); /// /// Gets all feature values. /// - /// A context providing information that can be used to evaluate the feature values. /// A dictionary of feature keys and their values. - Dictionary GetAll(ICurrentContext currentContext); + Dictionary GetAll(); } diff --git a/src/Core/Services/Implementations/CollectionService.cs b/src/Core/Services/Implementations/CollectionService.cs index 27c933826..2941de2d5 100644 --- a/src/Core/Services/Implementations/CollectionService.cs +++ b/src/Core/Services/Implementations/CollectionService.cs @@ -56,7 +56,7 @@ public class CollectionService : ICollectionService var usersList = users?.ToList(); // If using Flexible Collections - a collection should always have someone with Can Manage permissions - if (_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, _currentContext)) + if (_featureService.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1)) { var groupHasManageAccess = groupsList?.Any(g => g.Manage) ?? false; var userHasManageAccess = usersList?.Any(u => u.Manage) ?? false; @@ -124,7 +124,7 @@ public class CollectionService : ICollectionService { var collections = await _collectionRepository.GetManyByUserIdAsync( _currentContext.UserId.Value, - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext) + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections) ); orgCollections = collections.Where(c => c.OrganizationId == organizationId); } diff --git a/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs b/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs index ad3d6e82e..b1fd9c564 100644 --- a/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs +++ b/src/Core/Services/Implementations/LaunchDarklyFeatureService.cs @@ -4,16 +4,25 @@ using Bit.Core.Utilities; using LaunchDarkly.Logging; using LaunchDarkly.Sdk.Server; using LaunchDarkly.Sdk.Server.Integrations; +using LaunchDarkly.Sdk.Server.Interfaces; namespace Bit.Core.Services; -public class LaunchDarklyFeatureService : IFeatureService, IDisposable +public class LaunchDarklyFeatureService : IFeatureService { - private readonly LdClient _client; + private readonly ILdClient _client; + private readonly ICurrentContext _currentContext; private const string _anonymousUser = "25a15cac-58cf-4ac0-ad0f-b17c4bd92294"; public LaunchDarklyFeatureService( - IGlobalSettings globalSettings) + ILdClient client, + ICurrentContext currentContext) + { + _client = client; + _currentContext = currentContext; + } + + public static Configuration GetConfiguredClient(GlobalSettings globalSettings) { var ldConfig = Configuration.Builder(globalSettings.LaunchDarkly?.SdkKey); ldConfig.Logging(Components.Logging().Level(LogLevel.Error)); @@ -64,7 +73,7 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable ldConfig.Offline(true); } - _client = new LdClient(ldConfig.Build()); + return ldConfig.Build(); } public bool IsOnline() @@ -72,28 +81,28 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable return _client.Initialized && !_client.IsOffline(); } - public bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false) + public bool IsEnabled(string key, bool defaultValue = false) { - return _client.BoolVariation(key, BuildContext(currentContext), defaultValue); + return _client.BoolVariation(key, BuildContext(), defaultValue); } - public int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0) + public int GetIntVariation(string key, int defaultValue = 0) { - return _client.IntVariation(key, BuildContext(currentContext), defaultValue); + return _client.IntVariation(key, BuildContext(), defaultValue); } - public string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null) + public string GetStringVariation(string key, string defaultValue = null) { - return _client.StringVariation(key, BuildContext(currentContext), defaultValue); + return _client.StringVariation(key, BuildContext(), defaultValue); } - public Dictionary GetAll(ICurrentContext currentContext) + public Dictionary GetAll() { var results = new Dictionary(); var keys = FeatureFlagKeys.GetAllKeys(); - var values = _client.AllFlagsState(BuildContext(currentContext)); + var values = _client.AllFlagsState(BuildContext()); if (values.Valid) { foreach (var key in keys) @@ -119,23 +128,18 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable return results; } - public void Dispose() - { - _client?.Dispose(); - } - - private LaunchDarkly.Sdk.Context BuildContext(ICurrentContext currentContext) + private LaunchDarkly.Sdk.Context BuildContext() { var builder = LaunchDarkly.Sdk.Context.MultiBuilder(); - switch (currentContext.ClientType) + switch (_currentContext.ClientType) { case Identity.ClientType.User: { LaunchDarkly.Sdk.ContextBuilder ldUser; - if (currentContext.UserId.HasValue) + if (_currentContext.UserId.HasValue) { - ldUser = LaunchDarkly.Sdk.Context.Builder(currentContext.UserId.Value.ToString()); + ldUser = LaunchDarkly.Sdk.Context.Builder(_currentContext.UserId.Value.ToString()); } else { @@ -146,9 +150,9 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable ldUser.Kind(LaunchDarkly.Sdk.ContextKind.Default); - if (currentContext.Organizations?.Any() ?? false) + if (_currentContext.Organizations?.Any() ?? false) { - var ldOrgs = currentContext.Organizations.Select(o => LaunchDarkly.Sdk.LdValue.Of(o.Id.ToString())); + var ldOrgs = _currentContext.Organizations.Select(o => LaunchDarkly.Sdk.LdValue.Of(o.Id.ToString())); ldUser.Set("organizations", LaunchDarkly.Sdk.LdValue.ArrayFrom(ldOrgs)); } @@ -158,9 +162,9 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable case Identity.ClientType.Organization: { - if (currentContext.OrganizationId.HasValue) + if (_currentContext.OrganizationId.HasValue) { - var ldOrg = LaunchDarkly.Sdk.Context.Builder(currentContext.OrganizationId.Value.ToString()); + var ldOrg = LaunchDarkly.Sdk.Context.Builder(_currentContext.OrganizationId.Value.ToString()); ldOrg.Kind("organization"); builder.Add(ldOrg.Build()); } @@ -169,16 +173,16 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable case Identity.ClientType.ServiceAccount: { - if (currentContext.UserId.HasValue) + if (_currentContext.UserId.HasValue) { - var ldServiceAccount = LaunchDarkly.Sdk.Context.Builder(currentContext.UserId.Value.ToString()); + var ldServiceAccount = LaunchDarkly.Sdk.Context.Builder(_currentContext.UserId.Value.ToString()); ldServiceAccount.Kind("service-account"); builder.Add(ldServiceAccount.Build()); } - if (currentContext.OrganizationId.HasValue) + if (_currentContext.OrganizationId.HasValue) { - var ldOrg = LaunchDarkly.Sdk.Context.Builder(currentContext.OrganizationId.Value.ToString()); + var ldOrg = LaunchDarkly.Sdk.Context.Builder(_currentContext.OrganizationId.Value.ToString()); ldOrg.Kind("organization"); builder.Add(ldOrg.Build()); } @@ -189,7 +193,7 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable return builder.Build(); } - private TestData BuildDataSource(Dictionary values) + private static TestData BuildDataSource(Dictionary values) { var source = TestData.DataSource(); foreach (var kvp in values) diff --git a/src/Core/Utilities/RequireFeatureAttribute.cs b/src/Core/Utilities/RequireFeatureAttribute.cs index f0a96bd0e..62343e891 100644 --- a/src/Core/Utilities/RequireFeatureAttribute.cs +++ b/src/Core/Utilities/RequireFeatureAttribute.cs @@ -1,5 +1,4 @@ -using Bit.Core.Context; -using Bit.Core.Exceptions; +using Bit.Core.Exceptions; using Bit.Core.Services; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.DependencyInjection; @@ -25,10 +24,9 @@ public class RequireFeatureAttribute : ActionFilterAttribute public override void OnActionExecuting(ActionExecutingContext context) { - var currentContext = context.HttpContext.RequestServices.GetRequiredService(); var featureService = context.HttpContext.RequestServices.GetRequiredService(); - if (!featureService.IsEnabled(_featureFlagKey, currentContext)) + if (!featureService.IsEnabled(_featureFlagKey)) { throw new FeatureUnavailableException(); } diff --git a/src/Core/Vault/Services/Implementations/CipherService.cs b/src/Core/Vault/Services/Implementations/CipherService.cs index 665f99aad..9feaab9cb 100644 --- a/src/Core/Vault/Services/Implementations/CipherService.cs +++ b/src/Core/Vault/Services/Implementations/CipherService.cs @@ -41,7 +41,7 @@ public class CipherService : ICipherService private readonly IFeatureService _featureService; private bool UseFlexibleCollections => - _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); public CipherService( ICipherRepository cipherRepository, diff --git a/src/Events/Controllers/CollectController.cs b/src/Events/Controllers/CollectController.cs index 144c248e4..38781c185 100644 --- a/src/Events/Controllers/CollectController.cs +++ b/src/Events/Controllers/CollectController.cs @@ -73,7 +73,7 @@ public class CollectController : Controller } else { - var useFlexibleCollections = _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections, _currentContext); + var useFlexibleCollections = _featureService.IsEnabled(FeatureFlagKeys.FlexibleCollections); cipher = await _cipherRepository.GetByIdAsync(eventModel.CipherId.Value, _currentContext.UserId.Value, useFlexibleCollections); diff --git a/src/Events/Startup.cs b/src/Events/Startup.cs index 2bcb7a3ba..bac39c68d 100644 --- a/src/Events/Startup.cs +++ b/src/Events/Startup.cs @@ -70,7 +70,7 @@ public class Startup services.AddSingleton(); } - services.AddSingleton(); + services.AddOptionality(); // Mvc services.AddMvc(config => diff --git a/src/Identity/IdentityServer/WebAuthnGrantValidator.cs b/src/Identity/IdentityServer/WebAuthnGrantValidator.cs index 5928974d5..dad2de354 100644 --- a/src/Identity/IdentityServer/WebAuthnGrantValidator.cs +++ b/src/Identity/IdentityServer/WebAuthnGrantValidator.cs @@ -66,7 +66,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator(); - services.AddSingleton(); + services.AddOptionality(); services.AddTokenizers(); if (CoreHelpers.SettingHasValue(globalSettings.ServiceBus.ConnectionString) && @@ -426,8 +428,6 @@ public static class ServiceCollectionExtensions return identityBuilder; } - - public static void AddIdentityAuthenticationServices( this IServiceCollection services, GlobalSettings globalSettings, IWebHostEnvironment environment, Action addAuthorization) @@ -727,4 +727,17 @@ public static class ServiceCollectionExtensions }); }); } + + public static IServiceCollection AddOptionality(this IServiceCollection services) + { + services.AddSingleton(s => + { + return new LdClient(LaunchDarklyFeatureService.GetConfiguredClient( + s.GetRequiredService())); + }); + + services.AddScoped(); + + return services; + } } diff --git a/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs b/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs index 51a04b65a..b19b11f15 100644 --- a/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs +++ b/test/Api.Test/Auth/Controllers/AccountsControllerTests.cs @@ -14,7 +14,6 @@ using Bit.Core.Auth.Models.Api.Request.Accounts; using Bit.Core.Auth.Services; using Bit.Core.Auth.UserFeatures.UserKey; using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces; -using Bit.Core.Context; using Bit.Core.Entities; using Bit.Core.Enums; using Bit.Core.Exceptions; @@ -54,7 +53,6 @@ public class AccountsControllerTests : IDisposable private readonly ISetInitialMasterPasswordCommand _setInitialMasterPasswordCommand; private readonly IRotateUserKeyCommand _rotateUserKeyCommand; private readonly IFeatureService _featureService; - private readonly ICurrentContext _currentContext; private readonly IRotationValidator, IEnumerable> _cipherValidator; private readonly IRotationValidator, IEnumerable> _folderValidator; @@ -84,7 +82,6 @@ public class AccountsControllerTests : IDisposable _setInitialMasterPasswordCommand = Substitute.For(); _rotateUserKeyCommand = Substitute.For(); _featureService = Substitute.For(); - _currentContext = Substitute.For(); _cipherValidator = Substitute.For, IEnumerable>>(); _folderValidator = @@ -113,7 +110,6 @@ public class AccountsControllerTests : IDisposable _setInitialMasterPasswordCommand, _rotateUserKeyCommand, _featureService, - _currentContext, _cipherValidator, _folderValidator, _sendValidator, diff --git a/test/Api.Test/Controllers/ConfigControllerTests.cs b/test/Api.Test/Controllers/ConfigControllerTests.cs index d9b857194..4df352094 100644 --- a/test/Api.Test/Controllers/ConfigControllerTests.cs +++ b/test/Api.Test/Controllers/ConfigControllerTests.cs @@ -1,6 +1,5 @@ using AutoFixture.Xunit2; using Bit.Api.Controllers; -using Bit.Core.Context; using Bit.Core.Services; using Bit.Core.Settings; using NSubstitute; @@ -13,17 +12,14 @@ public class ConfigControllerTests : IDisposable private readonly ConfigController _sut; private readonly GlobalSettings _globalSettings; private readonly IFeatureService _featureService; - private readonly ICurrentContext _currentContext; public ConfigControllerTests() { _globalSettings = new GlobalSettings(); - _currentContext = Substitute.For(); _featureService = Substitute.For(); _sut = new ConfigController( _globalSettings, - _currentContext, _featureService ); } @@ -36,7 +32,7 @@ public class ConfigControllerTests : IDisposable [Theory, AutoData] public void GetConfigs_WithFeatureStates(Dictionary featureStates) { - _featureService.GetAll(_currentContext).Returns(featureStates); + _featureService.GetAll().Returns(featureStates); var response = _sut.GetConfigs(); diff --git a/test/Core.Test/Services/CollectionServiceTests.cs b/test/Core.Test/Services/CollectionServiceTests.cs index e9c3acb48..d57acebe3 100644 --- a/test/Core.Test/Services/CollectionServiceTests.cs +++ b/test/Core.Test/Services/CollectionServiceTests.cs @@ -114,7 +114,7 @@ public class CollectionServiceTest collection.Id = default; sutProvider.GetDependency().GetByIdAsync(organization.Id).Returns(organization); sutProvider.GetDependency() - .IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, Arg.Any(), Arg.Any()) + .IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, Arg.Any()) .Returns(true); organization.AllowAdminAccessToAllCollectionItems = false; diff --git a/test/Core.Test/Services/LaunchDarklyFeatureServiceTests.cs b/test/Core.Test/Services/LaunchDarklyFeatureServiceTests.cs index 3eb8c2547..6970b5f90 100644 --- a/test/Core.Test/Services/LaunchDarklyFeatureServiceTests.cs +++ b/test/Core.Test/Services/LaunchDarklyFeatureServiceTests.cs @@ -4,6 +4,7 @@ using Bit.Core.Services; using Bit.Core.Settings; using Bit.Test.Common.AutoFixture; using Bit.Test.Common.AutoFixture.Attributes; +using LaunchDarkly.Sdk.Server.Interfaces; using NSubstitute; using Xunit; @@ -19,9 +20,16 @@ public class LaunchDarklyFeatureServiceTests { globalSettings.ProjectName = "LaunchDarkly Tests"; + var currentContext = Substitute.For(); + currentContext.UserId.Returns(Guid.NewGuid()); + + var client = Substitute.For(); + var fixture = new Fixture(); return new SutProvider(fixture) .SetDependency(globalSettings) + .SetDependency(currentContext) + .SetDependency(client) .Create(); } @@ -30,10 +38,7 @@ public class LaunchDarklyFeatureServiceTests { var sutProvider = GetSutProvider(new Settings.GlobalSettings { SelfHosted = true }); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - Assert.False(sutProvider.Sut.IsEnabled(key, currentContext)); + Assert.False(sutProvider.Sut.IsEnabled(key)); } [Fact] @@ -41,10 +46,7 @@ public class LaunchDarklyFeatureServiceTests { var sutProvider = GetSutProvider(new Settings.GlobalSettings()); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - Assert.False(sutProvider.Sut.IsEnabled(_fakeFeatureKey, currentContext)); + Assert.False(sutProvider.Sut.IsEnabled(_fakeFeatureKey)); } [Fact(Skip = "For local development")] @@ -54,10 +56,7 @@ public class LaunchDarklyFeatureServiceTests var sutProvider = GetSutProvider(settings); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - Assert.False(sutProvider.Sut.IsEnabled(_fakeFeatureKey, currentContext)); + Assert.False(sutProvider.Sut.IsEnabled(_fakeFeatureKey)); } [Fact(Skip = "For local development")] @@ -67,10 +66,7 @@ public class LaunchDarklyFeatureServiceTests var sutProvider = GetSutProvider(settings); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - Assert.Equal(0, sutProvider.Sut.GetIntVariation(_fakeFeatureKey, currentContext)); + Assert.Equal(0, sutProvider.Sut.GetIntVariation(_fakeFeatureKey)); } [Fact(Skip = "For local development")] @@ -80,10 +76,7 @@ public class LaunchDarklyFeatureServiceTests var sutProvider = GetSutProvider(settings); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - Assert.Null(sutProvider.Sut.GetStringVariation(_fakeFeatureKey, currentContext)); + Assert.Null(sutProvider.Sut.GetStringVariation(_fakeFeatureKey)); } [Fact(Skip = "For local development")] @@ -91,10 +84,7 @@ public class LaunchDarklyFeatureServiceTests { var sutProvider = GetSutProvider(new Settings.GlobalSettings()); - var currentContext = Substitute.For(); - currentContext.UserId.Returns(Guid.NewGuid()); - - var results = sutProvider.Sut.GetAll(currentContext); + var results = sutProvider.Sut.GetAll(); Assert.NotNull(results); Assert.NotEmpty(results); diff --git a/test/Core.Test/Utilities/RequireFeatureAttributeTests.cs b/test/Core.Test/Utilities/RequireFeatureAttributeTests.cs index 04917cc0c..1734cc980 100644 --- a/test/Core.Test/Utilities/RequireFeatureAttributeTests.cs +++ b/test/Core.Test/Utilities/RequireFeatureAttributeTests.cs @@ -64,7 +64,7 @@ public class RequireFeatureAttributeTests var featureService = Substitute.For(); var currentContext = Substitute.For(); - featureService.IsEnabled(_testFeature, Arg.Any()).Returns(enabled); + featureService.IsEnabled(_testFeature).Returns(enabled); services.AddSingleton(featureService); services.AddSingleton(currentContext);