1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-21 12:05:42 +01:00

Establish IFeatureService as scoped (#3679)

* Establish IFeatureService as scoped

* Lint

* Feedback around injection
This commit is contained in:
Matt Bishop 2024-01-18 09:47:34 -05:00 committed by GitHub
parent cd006f3779
commit 974d23efdd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 96 additions and 131 deletions

View File

@ -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,

View File

@ -37,7 +37,6 @@ public class GroupsController : Controller
ICreateGroupCommand createGroupCommand,
IUpdateGroupCommand updateGroupCommand,
IDeleteGroupCommand deleteGroupCommand,
IFeatureService featureService,
IAuthorizationService authorizationService,
IApplicationCacheService applicationCacheService)
{

View File

@ -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)
{

View File

@ -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<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
private readonly IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> _folderValidator;
@ -95,7 +93,6 @@ public class AccountsController : Controller
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
IRotateUserKeyCommand rotateUserKeyCommand,
IFeatureService featureService,
ICurrentContext currentContext,
IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>> cipherValidator,
IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> folderValidator,
IRotationValidator<IEnumerable<SendWithIdRequestModel>, IReadOnlyList<Send>> 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
{

View File

@ -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());
}
}

View File

@ -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,

View File

@ -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;
}

View File

@ -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<CipherWithIdRequestModel>, IEnumerable<Cipher>>
{
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<IEnumerable<Cipher>> ValidateAsync(User user, IEnumerable<CipherWithIdRequestModel> ciphers)

View File

@ -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
{

View File

@ -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<User> _passwordHasher;
private readonly IOrganizationService _organizationService;
private readonly IDataProtectorTokenFactory<EmergencyAccessInviteTokenable> _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<EmergencyAccessInviteTokenable> 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;
}

View File

@ -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> _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)

View File

@ -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.
/// </summary>
/// <param name="key">The key of the feature to check.</param>
/// <param name="currentContext">A context providing information that can be used to evaluate whether a feature should be on or off.</param>
/// <param name="defaultValue">The default value for the feature.</param>
/// <returns>True if the feature is enabled, otherwise false.</returns>
bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false);
bool IsEnabled(string key, bool defaultValue = false);
/// <summary>
/// Gets the integer variation of a feature.
/// </summary>
/// <param name="key">The key of the feature to check.</param>
/// <param name="currentContext">A context providing information that can be used to evaluate the feature value.</param>
/// <param name="defaultValue">The default value for the feature.</param>
/// <returns>The feature variation value.</returns>
int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0);
int GetIntVariation(string key, int defaultValue = 0);
/// <summary>
/// Gets the string variation of a feature.
/// </summary>
/// <param name="key">The key of the feature to check.</param>
/// <param name="currentContext">A context providing information that can be used to evaluate the feature value.</param>
/// <param name="defaultValue">The default value for the feature.</param>
/// <returns>The feature variation value.</returns>
string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null);
string GetStringVariation(string key, string defaultValue = null);
/// <summary>
/// Gets all feature values.
/// </summary>
/// <param name="currentContext">A context providing information that can be used to evaluate the feature values.</param>
/// <returns>A dictionary of feature keys and their values.</returns>
Dictionary<string, object> GetAll(ICurrentContext currentContext);
Dictionary<string, object> GetAll();
}

View File

@ -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);
}

View File

@ -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<string, object> GetAll(ICurrentContext currentContext)
public Dictionary<string, object> GetAll()
{
var results = new Dictionary<string, object>();
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<string, string> values)
private static TestData BuildDataSource(Dictionary<string, string> values)
{
var source = TestData.DataSource();
foreach (var kvp in values)

View File

@ -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<ICurrentContext>();
var featureService = context.HttpContext.RequestServices.GetRequiredService<IFeatureService>();
if (!featureService.IsEnabled(_featureFlagKey, currentContext))
if (!featureService.IsEnabled(_featureFlagKey))
{
throw new FeatureUnavailableException();
}

View File

@ -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,

View File

@ -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);

View File

@ -70,7 +70,7 @@ public class Startup
services.AddSingleton<IEventWriteService, RepositoryEventWriteService>();
}
services.AddSingleton<IFeatureService, LaunchDarklyFeatureService>();
services.AddOptionality();
// Mvc
services.AddMvc(config =>

View File

@ -66,7 +66,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
if (!FeatureService.IsEnabled(FeatureFlagKeys.PasswordlessLogin, CurrentContext))
if (!FeatureService.IsEnabled(FeatureFlagKeys.PasswordlessLogin))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;

View File

@ -18,7 +18,7 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
public override async Task OnConnectedAsync()
{
var currentContext = new CurrentContext(null, null, null);
var currentContext = new CurrentContext(null, null);
await currentContext.BuildAsync(Context.User, _globalSettings);
if (currentContext.Organizations != null)
{
@ -33,7 +33,7 @@ public class NotificationsHub : Microsoft.AspNetCore.SignalR.Hub
public override async Task OnDisconnectedAsync(Exception exception)
{
var currentContext = new CurrentContext(null, null, null);
var currentContext = new CurrentContext(null, null);
await currentContext.BuildAsync(Context.User, _globalSettings);
if (currentContext.Organizations != null)
{

View File

@ -35,6 +35,8 @@ using Bit.Infrastructure.EntityFramework;
using DnsClient;
using Duende.IdentityServer.Configuration;
using IdentityModel;
using LaunchDarkly.Sdk.Server;
using LaunchDarkly.Sdk.Server.Interfaces;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
@ -222,7 +224,7 @@ public static class ServiceCollectionExtensions
return new LookupClient(options);
});
services.AddSingleton<IDnsResolverService, DnsResolverService>();
services.AddSingleton<IFeatureService, LaunchDarklyFeatureService>();
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<AuthorizationOptions> addAuthorization)
@ -727,4 +727,17 @@ public static class ServiceCollectionExtensions
});
});
}
public static IServiceCollection AddOptionality(this IServiceCollection services)
{
services.AddSingleton<ILdClient>(s =>
{
return new LdClient(LaunchDarklyFeatureService.GetConfiguredClient(
s.GetRequiredService<GlobalSettings>()));
});
services.AddScoped<IFeatureService, LaunchDarklyFeatureService>();
return services;
}
}

View File

@ -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<CipherWithIdRequestModel>, IEnumerable<Cipher>> _cipherValidator;
private readonly IRotationValidator<IEnumerable<FolderWithIdRequestModel>, IEnumerable<Folder>> _folderValidator;
@ -84,7 +82,6 @@ public class AccountsControllerTests : IDisposable
_setInitialMasterPasswordCommand = Substitute.For<ISetInitialMasterPasswordCommand>();
_rotateUserKeyCommand = Substitute.For<IRotateUserKeyCommand>();
_featureService = Substitute.For<IFeatureService>();
_currentContext = Substitute.For<ICurrentContext>();
_cipherValidator =
Substitute.For<IRotationValidator<IEnumerable<CipherWithIdRequestModel>, IEnumerable<Cipher>>>();
_folderValidator =
@ -113,7 +110,6 @@ public class AccountsControllerTests : IDisposable
_setInitialMasterPasswordCommand,
_rotateUserKeyCommand,
_featureService,
_currentContext,
_cipherValidator,
_folderValidator,
_sendValidator,

View File

@ -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<ICurrentContext>();
_featureService = Substitute.For<IFeatureService>();
_sut = new ConfigController(
_globalSettings,
_currentContext,
_featureService
);
}
@ -36,7 +32,7 @@ public class ConfigControllerTests : IDisposable
[Theory, AutoData]
public void GetConfigs_WithFeatureStates(Dictionary<string, object> featureStates)
{
_featureService.GetAll(_currentContext).Returns(featureStates);
_featureService.GetAll().Returns(featureStates);
var response = _sut.GetConfigs();

View File

@ -114,7 +114,7 @@ public class CollectionServiceTest
collection.Id = default;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdAsync(organization.Id).Returns(organization);
sutProvider.GetDependency<IFeatureService>()
.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, Arg.Any<ICurrentContext>(), Arg.Any<bool>())
.IsEnabled(FeatureFlagKeys.FlexibleCollectionsV1, Arg.Any<bool>())
.Returns(true);
organization.AllowAdminAccessToAllCollectionItems = false;

View File

@ -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<ICurrentContext>();
currentContext.UserId.Returns(Guid.NewGuid());
var client = Substitute.For<ILdClient>();
var fixture = new Fixture();
return new SutProvider<LaunchDarklyFeatureService>(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<ICurrentContext>();
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<ICurrentContext>();
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<ICurrentContext>();
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<ICurrentContext>();
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<ICurrentContext>();
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<ICurrentContext>();
currentContext.UserId.Returns(Guid.NewGuid());
var results = sutProvider.Sut.GetAll(currentContext);
var results = sutProvider.Sut.GetAll();
Assert.NotNull(results);
Assert.NotEmpty(results);

View File

@ -64,7 +64,7 @@ public class RequireFeatureAttributeTests
var featureService = Substitute.For<IFeatureService>();
var currentContext = Substitute.For<ICurrentContext>();
featureService.IsEnabled(_testFeature, Arg.Any<ICurrentContext>()).Returns(enabled);
featureService.IsEnabled(_testFeature).Returns(enabled);
services.AddSingleton(featureService);
services.AddSingleton(currentContext);