mirror of
https://github.com/bitwarden/server.git
synced 2024-11-21 12:05:42 +01:00
[PM-1012] Feature access using context (#2764)
* Document online method * Feature accessors with context * Direct null assertion * Establish a constants class for flag keys
This commit is contained in:
parent
7334de636b
commit
11c59addf4
@ -22,3 +22,8 @@ public static class AuthenticationSchemes
|
||||
{
|
||||
public const string BitwardenExternalCookieAuthenticationScheme = "bw.external";
|
||||
}
|
||||
|
||||
public static class FeatureFlagKeys
|
||||
{
|
||||
public const string SecretsManager = "secrets-manager";
|
||||
}
|
||||
|
@ -1,6 +1,39 @@
|
||||
namespace Bit.Core.Services;
|
||||
using Bit.Core.Context;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public interface IFeatureService
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks whether online access to feature status is available.
|
||||
/// </summary>
|
||||
/// <returns>True if the service is online, otherwise false.</returns>
|
||||
bool IsOnline();
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
|
||||
/// <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);
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Settings;
|
||||
using LaunchDarkly.Sdk.Server;
|
||||
using LaunchDarkly.Sdk.Server.Integrations;
|
||||
|
||||
@ -47,8 +48,44 @@ public class LaunchDarklyFeatureService : IFeatureService, IDisposable
|
||||
return _client.Initialized && !_client.IsOffline();
|
||||
}
|
||||
|
||||
public bool IsEnabled(string key, ICurrentContext currentContext, bool defaultValue = false)
|
||||
{
|
||||
return _client.BoolVariation(key, BuildContext(currentContext), defaultValue);
|
||||
}
|
||||
|
||||
public int GetIntVariation(string key, ICurrentContext currentContext, int defaultValue = 0)
|
||||
{
|
||||
return _client.IntVariation(key, BuildContext(currentContext), defaultValue);
|
||||
}
|
||||
|
||||
public string GetStringVariation(string key, ICurrentContext currentContext, string defaultValue = null)
|
||||
{
|
||||
return _client.StringVariation(key, BuildContext(currentContext), defaultValue);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_client?.Dispose();
|
||||
}
|
||||
|
||||
private LaunchDarkly.Sdk.Context BuildContext(ICurrentContext currentContext)
|
||||
{
|
||||
var builder = LaunchDarkly.Sdk.Context.MultiBuilder();
|
||||
|
||||
if (currentContext.UserId.HasValue)
|
||||
{
|
||||
var user = LaunchDarkly.Sdk.Context.Builder(currentContext.UserId.Value.ToString());
|
||||
user.Kind(LaunchDarkly.Sdk.ContextKind.Default);
|
||||
builder.Add(user.Build());
|
||||
}
|
||||
|
||||
if (currentContext.OrganizationId.HasValue)
|
||||
{
|
||||
var org = LaunchDarkly.Sdk.Context.Builder(currentContext.OrganizationId.Value.ToString());
|
||||
org.Kind("org");
|
||||
builder.Add(org.Build());
|
||||
}
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
using AutoFixture;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Test.Common.AutoFixture;
|
||||
using Bit.Test.Common.AutoFixture.Attributes;
|
||||
using NSubstitute;
|
||||
using Xunit;
|
||||
|
||||
namespace Bit.Core.Test.Services;
|
||||
@ -25,4 +27,68 @@ public class LaunchDarklyFeatureServiceTests
|
||||
|
||||
Assert.False(sutProvider.Sut.IsOnline());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public void DefaultFeatureValue_WhenSelfHost(string key)
|
||||
{
|
||||
var sutProvider = GetSutProvider(new Core.Settings.GlobalSettings() { SelfHosted = true });
|
||||
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
Assert.False(sutProvider.Sut.IsEnabled(key, currentContext));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DefaultFeatureValue_NoSdkKey()
|
||||
{
|
||||
var sutProvider = GetSutProvider(new Core.Settings.GlobalSettings());
|
||||
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
Assert.False(sutProvider.Sut.IsEnabled(FeatureFlagKeys.SecretsManager, currentContext));
|
||||
}
|
||||
|
||||
[Fact(Skip = "For local development")]
|
||||
public void FeatureValue_Boolean()
|
||||
{
|
||||
var settings = new Core.Settings.GlobalSettings();
|
||||
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||
|
||||
var sutProvider = GetSutProvider(settings);
|
||||
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
Assert.False(sutProvider.Sut.IsEnabled(FeatureFlagKeys.SecretsManager, currentContext));
|
||||
}
|
||||
|
||||
[Fact(Skip = "For local development")]
|
||||
public void FeatureValue_Int()
|
||||
{
|
||||
var settings = new Core.Settings.GlobalSettings();
|
||||
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||
|
||||
var sutProvider = GetSutProvider(settings);
|
||||
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
Assert.Equal(0, sutProvider.Sut.GetIntVariation(FeatureFlagKeys.SecretsManager, currentContext));
|
||||
}
|
||||
|
||||
[Fact(Skip = "For local development")]
|
||||
public void FeatureValue_String()
|
||||
{
|
||||
var settings = new Core.Settings.GlobalSettings();
|
||||
settings.LaunchDarkly.SdkKey = "somevalue";
|
||||
|
||||
var sutProvider = GetSutProvider(settings);
|
||||
|
||||
var currentContext = Substitute.For<ICurrentContext>();
|
||||
currentContext.UserId.Returns(Guid.NewGuid());
|
||||
|
||||
Assert.Null(sutProvider.Sut.GetStringVariation(FeatureFlagKeys.SecretsManager, currentContext));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user