mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 21:18:13 -05:00
[SM-771] Add new endpoint for bulk enabling users for Secrets Manager (#3020)
* Add new endpoint for bulk enabling users for sm * Review updates
This commit is contained in:
parent
481004394f
commit
74ab7e8672
@ -9,6 +9,7 @@ using Bit.Core.Models.Business;
|
|||||||
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
|
||||||
using Bit.Core.Models.Data.Organizations.Policies;
|
using Bit.Core.Models.Data.Organizations.Policies;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
@ -19,6 +20,7 @@ namespace Bit.Api.Controllers;
|
|||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class OrganizationUsersController : Controller
|
public class OrganizationUsersController : Controller
|
||||||
{
|
{
|
||||||
|
private readonly IEnableAccessSecretsManagerCommand _enableAccessSecretsManagerCommand;
|
||||||
private readonly IOrganizationRepository _organizationRepository;
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
@ -29,6 +31,7 @@ public class OrganizationUsersController : Controller
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
|
|
||||||
public OrganizationUsersController(
|
public OrganizationUsersController(
|
||||||
|
IEnableAccessSecretsManagerCommand enableAccessSecretsManagerCommand,
|
||||||
IOrganizationRepository organizationRepository,
|
IOrganizationRepository organizationRepository,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IOrganizationService organizationService,
|
IOrganizationService organizationService,
|
||||||
@ -38,6 +41,7 @@ public class OrganizationUsersController : Controller
|
|||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
ICurrentContext currentContext)
|
ICurrentContext currentContext)
|
||||||
{
|
{
|
||||||
|
_enableAccessSecretsManagerCommand = enableAccessSecretsManagerCommand;
|
||||||
_organizationRepository = organizationRepository;
|
_organizationRepository = organizationRepository;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_organizationService = organizationService;
|
_organizationService = organizationService;
|
||||||
@ -420,6 +424,29 @@ public class OrganizationUsersController : Controller
|
|||||||
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
|
return await RestoreOrRevokeUsersAsync(orgId, model, (orgId, orgUserIds, restoringUserId) => _organizationService.RestoreUsersAsync(orgId, orgUserIds, restoringUserId, _userService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpPatch("enable-secrets-manager")]
|
||||||
|
[HttpPut("enable-secrets-manager")]
|
||||||
|
public async Task<ListResponseModel<OrganizationUserBulkResponseModel>> BulkEnableSecretsManagerAsync(Guid orgId,
|
||||||
|
[FromBody] OrganizationUserBulkRequestModel model)
|
||||||
|
{
|
||||||
|
if (!await _currentContext.ManageUsers(orgId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var orgUsers = (await _organizationUserRepository.GetManyAsync(model.Ids))
|
||||||
|
.Where(ou => ou.OrganizationId == orgId).ToList();
|
||||||
|
if (orgUsers.Count == 0)
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Users invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = await _enableAccessSecretsManagerCommand.EnableUsersAsync(orgUsers);
|
||||||
|
|
||||||
|
return new ListResponseModel<OrganizationUserBulkResponseModel>(results.Select(r =>
|
||||||
|
new OrganizationUserBulkResponseModel(r.organizationUser.Id, r.error)));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task RestoreOrRevokeUserAsync(
|
private async Task RestoreOrRevokeUserAsync(
|
||||||
Guid orgId,
|
Guid orgId,
|
||||||
Guid id,
|
Guid id,
|
||||||
|
@ -15,6 +15,8 @@ using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterpri
|
|||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Cloud;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.Interfaces;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
|
using Bit.Core.OrganizationFeatures.OrganizationSponsorships.FamiliesForEnterprise.SelfHosted;
|
||||||
|
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
|
||||||
|
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
using Bit.Core.Tokens;
|
using Bit.Core.Tokens;
|
||||||
@ -29,6 +31,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
public static void AddOrganizationServices(this IServiceCollection services, IGlobalSettings globalSettings)
|
public static void AddOrganizationServices(this IServiceCollection services, IGlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
services.AddScoped<IOrganizationService, OrganizationService>();
|
services.AddScoped<IOrganizationService, OrganizationService>();
|
||||||
|
services.AddScoped<IEnableAccessSecretsManagerCommand, EnableAccessSecretsManagerCommand>();
|
||||||
services.AddTokenizers();
|
services.AddTokenizers();
|
||||||
services.AddOrganizationGroupCommands();
|
services.AddOrganizationGroupCommands();
|
||||||
services.AddOrganizationConnectionCommands();
|
services.AddOrganizationConnectionCommands();
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
|
||||||
|
|
||||||
|
namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
|
||||||
|
|
||||||
|
public class EnableAccessSecretsManagerCommand : IEnableAccessSecretsManagerCommand
|
||||||
|
{
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
|
||||||
|
public EnableAccessSecretsManagerCommand(IOrganizationUserRepository organizationUserRepository)
|
||||||
|
{
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<(OrganizationUser organizationUser, string error)>> EnableUsersAsync(
|
||||||
|
IEnumerable<OrganizationUser> organizationUsers)
|
||||||
|
{
|
||||||
|
var results = new List<(OrganizationUser organizationUser, string error)>();
|
||||||
|
var usersToEnable = new List<OrganizationUser>();
|
||||||
|
|
||||||
|
foreach (var orgUser in organizationUsers)
|
||||||
|
{
|
||||||
|
if (orgUser.AccessSecretsManager)
|
||||||
|
{
|
||||||
|
results.Add((orgUser, "User already has access to Secrets Manager"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
orgUser.AccessSecretsManager = true;
|
||||||
|
usersToEnable.Add(orgUser);
|
||||||
|
results.Add((orgUser, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (usersToEnable.Any())
|
||||||
|
{
|
||||||
|
await _organizationUserRepository.ReplaceManyAsync(usersToEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
|
||||||
|
namespace Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager.Interfaces;
|
||||||
|
|
||||||
|
public interface IEnableAccessSecretsManagerCommand
|
||||||
|
{
|
||||||
|
Task<List<(OrganizationUser organizationUser, string error)>> EnableUsersAsync(
|
||||||
|
IEnumerable<OrganizationUser> organizationUsers);
|
||||||
|
}
|
@ -0,0 +1,81 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.SecretsManager.Commands.EnableAccessSecretsManager;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using Bit.Test.Common.Helpers;
|
||||||
|
using NSubstitute;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.SecretsManager.Commands.EnableAccessSecretsManager;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class EnableAccessSecretsManagerCommandTests
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task EnableUsers_UsersAlreadyEnabled_DoesNotCallRepository(
|
||||||
|
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
|
||||||
|
{
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
item.AccessSecretsManager = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.EnableUsersAsync(data);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationUserRepository>().DidNotReceiveWithAnyArgs()
|
||||||
|
.ReplaceManyAsync(default);
|
||||||
|
|
||||||
|
Assert.Equal(data.Count, result.Count);
|
||||||
|
Assert.Equal(data.Count,
|
||||||
|
result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task EnableUsers_OneUserNotEnabled_CallsRepositoryForOne(
|
||||||
|
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
|
||||||
|
{
|
||||||
|
var firstUser = new List<OrganizationUser>();
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
if (item == data.First())
|
||||||
|
{
|
||||||
|
item.AccessSecretsManager = false;
|
||||||
|
firstUser.Add(item);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item.AccessSecretsManager = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.EnableUsersAsync(data);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||||
|
.ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(firstUser)));
|
||||||
|
|
||||||
|
Assert.Equal(data.Count, result.Count);
|
||||||
|
Assert.Equal(data.Count - 1,
|
||||||
|
result.Where(x => x.error == "User already has access to Secrets Manager").ToList().Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task EnableUsers_Success(
|
||||||
|
SutProvider<EnableAccessSecretsManagerCommand> sutProvider, ICollection<OrganizationUser> data)
|
||||||
|
{
|
||||||
|
foreach (var item in data)
|
||||||
|
{
|
||||||
|
item.AccessSecretsManager = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = await sutProvider.Sut.EnableUsersAsync(data);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationUserRepository>().Received(1)
|
||||||
|
.ReplaceManyAsync(Arg.Is(AssertHelper.AssertPropertyEqual(data)));
|
||||||
|
|
||||||
|
Assert.Equal(data.Count, result.Count);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user