1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-29 13:25:17 +01:00

add feature flag

This commit is contained in:
Thomas Avery 2024-10-23 16:02:38 -05:00
parent 12da983165
commit 0b60bb93d8
No known key found for this signature in database
GPG Key ID: 44A02A0410B0F429
4 changed files with 62 additions and 1 deletions

View File

@ -1,5 +1,7 @@
#nullable enable #nullable enable
using Bit.Api.KeyManagement.Models.Requests; using Bit.Api.KeyManagement.Models.Requests;
using Bit.Core;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Commands.Interfaces; using Bit.Core.KeyManagement.Commands.Interfaces;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
@ -13,16 +15,19 @@ namespace Bit.Api.KeyManagement.Controllers;
public class AccountsKeyManagementController : Controller public class AccountsKeyManagementController : Controller
{ {
private readonly IEmergencyAccessRepository _emergencyAccessRepository; private readonly IEmergencyAccessRepository _emergencyAccessRepository;
private readonly IFeatureService _featureService;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly IRegenerateUserAsymmetricKeysCommand _regenerateUserAsymmetricKeysCommand; private readonly IRegenerateUserAsymmetricKeysCommand _regenerateUserAsymmetricKeysCommand;
private readonly IUserService _userService; private readonly IUserService _userService;
public AccountsKeyManagementController(IUserService userService, public AccountsKeyManagementController(IUserService userService,
IFeatureService featureService,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
IEmergencyAccessRepository emergencyAccessRepository, IEmergencyAccessRepository emergencyAccessRepository,
IRegenerateUserAsymmetricKeysCommand regenerateUserAsymmetricKeysCommand) IRegenerateUserAsymmetricKeysCommand regenerateUserAsymmetricKeysCommand)
{ {
_userService = userService; _userService = userService;
_featureService = featureService;
_regenerateUserAsymmetricKeysCommand = regenerateUserAsymmetricKeysCommand; _regenerateUserAsymmetricKeysCommand = regenerateUserAsymmetricKeysCommand;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_emergencyAccessRepository = emergencyAccessRepository; _emergencyAccessRepository = emergencyAccessRepository;
@ -31,7 +36,11 @@ public class AccountsKeyManagementController : Controller
[HttpPost("regenerate-keys")] [HttpPost("regenerate-keys")]
public async Task RegenerateKeysAsync([FromBody] KeyRegenerationRequestModel request) public async Task RegenerateKeysAsync([FromBody] KeyRegenerationRequestModel request)
{ {
// FIXME add feature flag check. if (!_featureService.IsEnabled(FeatureFlagKeys.PrivateKeyRegeneration))
{
throw new NotFoundException();
}
var user = await _userService.GetUserByPrincipalAsync(User) ?? throw new UnauthorizedAccessException(); var user = await _userService.GetUserByPrincipalAsync(User) ?? throw new UnauthorizedAccessException();
var usersOrganizationAccounts = await _organizationUserRepository.GetManyByUserAsync(user.Id); var usersOrganizationAccounts = await _organizationUserRepository.GetManyByUserAsync(user.Id);
var designatedEmergencyAccess = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(user.Id); var designatedEmergencyAccess = await _emergencyAccessRepository.GetManyDetailsByGranteeIdAsync(user.Id);

View File

@ -148,6 +148,7 @@ public static class FeatureFlagKeys
public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint"; public const string VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint";
public const string Pm13322AddPolicyDefinitions = "pm-13322-add-policy-definitions"; public const string Pm13322AddPolicyDefinitions = "pm-13322-add-policy-definitions";
public const string LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split"; public const string LimitCollectionCreationDeletionSplit = "pm-10863-limit-collection-creation-deletion-split";
public const string PrivateKeyRegeneration = "pm-12241-private-key-regeneration";
public static List<string> GetAllKeys() public static List<string> GetAllKeys()
{ {

View File

@ -27,6 +27,8 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
public AccountsKeyManagementControllerTests(ApiApplicationFactory factory) public AccountsKeyManagementControllerTests(ApiApplicationFactory factory)
{ {
_factory = factory; _factory = factory;
_factory.UpdateConfiguration("globalSettings:launchDarkly:flagValues:pm-12241-private-key-regeneration",
"true");
_client = factory.CreateClient(); _client = factory.CreateClient();
_loginHelper = new LoginHelper(_factory, _client); _loginHelper = new LoginHelper(_factory, _client);
_userRepository = _factory.GetService<IUserRepository>(); _userRepository = _factory.GetService<IUserRepository>();
@ -45,6 +47,27 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
return Task.CompletedTask; return Task.CompletedTask;
} }
[Theory]
[BitAutoData]
public async Task RegenerateKeysAsync_FeatureFlagTurnedOff_NotFound(KeyRegenerationRequestModel request)
{
// Localize factory to inject a false value for the feature flag.
var localFactory = new ApiApplicationFactory();
localFactory.UpdateConfiguration("globalSettings:launchDarkly:flagValues:pm-12241-private-key-regeneration",
"false");
var localClient = localFactory.CreateClient();
var localEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
var localLoginHelper = new LoginHelper(localFactory, localClient);
await localFactory.LoginWithNewAccount(localEmail);
await localLoginHelper.LoginAsync(localEmail);
request.UserKeyEncryptedUserPrivateKey = _mockEncryptedString;
var response = await localClient.PostAsJsonAsync("/accounts/key-management/regenerate-keys", request);
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task RegenerateKeysAsync_NotLoggedIn_Unauthorized(KeyRegenerationRequestModel request) public async Task RegenerateKeysAsync_NotLoggedIn_Unauthorized(KeyRegenerationRequestModel request)

View File

@ -2,8 +2,10 @@
using System.Security.Claims; using System.Security.Claims;
using Bit.Api.KeyManagement.Controllers; using Bit.Api.KeyManagement.Controllers;
using Bit.Api.KeyManagement.Models.Requests; using Bit.Api.KeyManagement.Models.Requests;
using Bit.Core;
using Bit.Core.Auth.Models.Data; using Bit.Core.Auth.Models.Data;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Commands.Interfaces; using Bit.Core.KeyManagement.Commands.Interfaces;
using Bit.Core.KeyManagement.Models.Data; using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.Repositories; using Bit.Core.Repositories;
@ -21,11 +23,35 @@ namespace Bit.Api.Test.KeyManagement.Controllers;
[JsonDocumentCustomize] [JsonDocumentCustomize]
public class AccountsKeyManagementControllerTests public class AccountsKeyManagementControllerTests
{ {
[Theory]
[BitAutoData]
public async Task RegenerateKeysAsync_FeatureFlagOff_Throws(
SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data)
{
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(false);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.RegenerateKeysAsync(data));
await sutProvider.GetDependency<IOrganizationUserRepository>().ReceivedWithAnyArgs(0)
.GetManyByUserAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IEmergencyAccessRepository>().ReceivedWithAnyArgs(0)
.GetManyDetailsByGranteeIdAsync(Arg.Any<Guid>());
await sutProvider.GetDependency<IRegenerateUserAsymmetricKeysCommand>().ReceivedWithAnyArgs(0)
.RegenerateKeysAsync(Arg.Any<UserAsymmetricKeys>(),
Arg.Any<ICollection<OrganizationUser>>(),
Arg.Any<ICollection<EmergencyAccessDetails>>());
}
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public async Task RegenerateKeysAsync_UserNull_Throws(SutProvider<AccountsKeyManagementController> sutProvider, public async Task RegenerateKeysAsync_UserNull_Throws(SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data) KeyRegenerationRequestModel data)
{ {
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull(); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).ReturnsNull();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.RegenerateKeysAsync(data)); await Assert.ThrowsAsync<UnauthorizedAccessException>(() => sutProvider.Sut.RegenerateKeysAsync(data));
@ -45,6 +71,8 @@ public class AccountsKeyManagementControllerTests
public async Task RegenerateKeysAsync_Success(SutProvider<AccountsKeyManagementController> sutProvider, public async Task RegenerateKeysAsync_Success(SutProvider<AccountsKeyManagementController> sutProvider,
KeyRegenerationRequestModel data, User user) KeyRegenerationRequestModel data, User user)
{ {
sutProvider.GetDependency<IFeatureService>().IsEnabled(Arg.Is(FeatureFlagKeys.PrivateKeyRegeneration))
.Returns(true);
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user); sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>()).Returns(user);
await sutProvider.Sut.RegenerateKeysAsync(data); await sutProvider.Sut.RegenerateKeysAsync(data);