mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 13:08:17 -05:00
[SG-841] Refactor GetOrganizationApiKeyCommand (#2436)
* Renamed and split up class to only query for an organization key * Added a command class to create an organization api key * Updated service registration and controller to include new changes * Updated test cases to reflect refactor * fixed lint issues * Fixed PR comment
This commit is contained in:
parent
5bcacf785f
commit
f74730dd2f
@ -33,8 +33,9 @@ public class OrganizationsController : Controller
|
|||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly ISsoConfigService _ssoConfigService;
|
private readonly ISsoConfigService _ssoConfigService;
|
||||||
private readonly IGetOrganizationApiKeyCommand _getOrganizationApiKeyCommand;
|
private readonly IGetOrganizationApiKeyQuery _getOrganizationApiKeyQuery;
|
||||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||||
|
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
||||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
@ -48,8 +49,9 @@ public class OrganizationsController : Controller
|
|||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
ISsoConfigService ssoConfigService,
|
ISsoConfigService ssoConfigService,
|
||||||
IGetOrganizationApiKeyCommand getOrganizationApiKeyCommand,
|
IGetOrganizationApiKeyQuery getOrganizationApiKeyQuery,
|
||||||
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
IRotateOrganizationApiKeyCommand rotateOrganizationApiKeyCommand,
|
||||||
|
ICreateOrganizationApiKeyCommand createOrganizationApiKeyCommand,
|
||||||
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
IOrganizationApiKeyRepository organizationApiKeyRepository,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -62,8 +64,9 @@ public class OrganizationsController : Controller
|
|||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
_ssoConfigService = ssoConfigService;
|
_ssoConfigService = ssoConfigService;
|
||||||
_getOrganizationApiKeyCommand = getOrganizationApiKeyCommand;
|
_getOrganizationApiKeyQuery = getOrganizationApiKeyQuery;
|
||||||
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
_rotateOrganizationApiKeyCommand = rotateOrganizationApiKeyCommand;
|
||||||
|
_createOrganizationApiKeyCommand = createOrganizationApiKeyCommand;
|
||||||
_organizationApiKeyRepository = organizationApiKeyRepository;
|
_organizationApiKeyRepository = organizationApiKeyRepository;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
@ -514,8 +517,9 @@ public class OrganizationsController : Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var organizationApiKey = await _getOrganizationApiKeyCommand
|
var organizationApiKey = await _getOrganizationApiKeyQuery
|
||||||
.GetOrganizationApiKeyAsync(organization.Id, model.Type);
|
.GetOrganizationApiKeyAsync(organization.Id, model.Type) ??
|
||||||
|
await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type);
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
@ -565,8 +569,9 @@ public class OrganizationsController : Controller
|
|||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var organizationApiKey = await _getOrganizationApiKeyCommand
|
var organizationApiKey = await _getOrganizationApiKeyQuery
|
||||||
.GetOrganizationApiKeyAsync(organization.Id, model.Type);
|
.GetOrganizationApiKeyAsync(organization.Id, model.Type) ??
|
||||||
|
await _createOrganizationApiKeyCommand.CreateAsync(organization.Id, model.Type);
|
||||||
|
|
||||||
var user = await _userService.GetUserByPrincipalAsync(User);
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
if (user == null)
|
if (user == null)
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys;
|
||||||
|
|
||||||
|
public class CreateOrganizationApiKeyCommand : ICreateOrganizationApiKeyCommand
|
||||||
|
{
|
||||||
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
|
|
||||||
|
public CreateOrganizationApiKeyCommand(IOrganizationApiKeyRepository organizationApiKeyRepository)
|
||||||
|
{
|
||||||
|
_organizationApiKeyRepository = organizationApiKeyRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<OrganizationApiKey> CreateAsync(Guid organizationId,
|
||||||
|
OrganizationApiKeyType organizationApiKeyType)
|
||||||
|
{
|
||||||
|
var apiKey = new OrganizationApiKey
|
||||||
|
{
|
||||||
|
OrganizationId = organizationId,
|
||||||
|
Type = organizationApiKeyType,
|
||||||
|
ApiKey = CoreHelpers.SecureRandomString(30),
|
||||||
|
RevisionDate = DateTime.UtcNow,
|
||||||
|
};
|
||||||
|
|
||||||
|
await _organizationApiKeyRepository.CreateAsync(apiKey);
|
||||||
|
return apiKey;
|
||||||
|
}
|
||||||
|
}
|
@ -2,15 +2,14 @@
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
using Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Bit.Core.Utilities;
|
|
||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys;
|
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys;
|
||||||
|
|
||||||
public class GetOrganizationApiKeyCommand : IGetOrganizationApiKeyCommand
|
public class GetOrganizationApiKeyQuery : IGetOrganizationApiKeyQuery
|
||||||
{
|
{
|
||||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
|
|
||||||
public GetOrganizationApiKeyCommand(IOrganizationApiKeyRepository organizationApiKeyRepository)
|
public GetOrganizationApiKeyQuery(IOrganizationApiKeyRepository organizationApiKeyRepository)
|
||||||
{
|
{
|
||||||
_organizationApiKeyRepository = organizationApiKeyRepository;
|
_organizationApiKeyRepository = organizationApiKeyRepository;
|
||||||
}
|
}
|
||||||
@ -25,20 +24,6 @@ public class GetOrganizationApiKeyCommand : IGetOrganizationApiKeyCommand
|
|||||||
var apiKeys = await _organizationApiKeyRepository
|
var apiKeys = await _organizationApiKeyRepository
|
||||||
.GetManyByOrganizationIdTypeAsync(organizationId, organizationApiKeyType);
|
.GetManyByOrganizationIdTypeAsync(organizationId, organizationApiKeyType);
|
||||||
|
|
||||||
if (apiKeys == null || !apiKeys.Any())
|
|
||||||
{
|
|
||||||
var apiKey = new OrganizationApiKey
|
|
||||||
{
|
|
||||||
OrganizationId = organizationId,
|
|
||||||
Type = organizationApiKeyType,
|
|
||||||
ApiKey = CoreHelpers.SecureRandomString(30),
|
|
||||||
RevisionDate = DateTime.UtcNow,
|
|
||||||
};
|
|
||||||
|
|
||||||
await _organizationApiKeyRepository.CreateAsync(apiKey);
|
|
||||||
return apiKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Currently we only allow one type of api key per organization
|
// NOTE: Currently we only allow one type of api key per organization
|
||||||
return apiKeys.Single();
|
return apiKeys.Single();
|
||||||
}
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
|
||||||
|
public interface ICreateOrganizationApiKeyCommand
|
||||||
|
{
|
||||||
|
Task<OrganizationApiKey> CreateAsync(Guid organizationId, OrganizationApiKeyType organizationApiKeyType);
|
||||||
|
}
|
@ -3,7 +3,7 @@ using Bit.Core.Enums;
|
|||||||
|
|
||||||
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
namespace Bit.Core.OrganizationFeatures.OrganizationApiKeys.Interfaces;
|
||||||
|
|
||||||
public interface IGetOrganizationApiKeyCommand
|
public interface IGetOrganizationApiKeyQuery
|
||||||
{
|
{
|
||||||
Task<OrganizationApiKey> GetOrganizationApiKeyAsync(Guid organizationId, OrganizationApiKeyType organizationApiKeyType);
|
Task<OrganizationApiKey> GetOrganizationApiKeyAsync(Guid organizationId, OrganizationApiKeyType organizationApiKeyType);
|
||||||
}
|
}
|
@ -24,7 +24,7 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
services.AddTokenizers();
|
services.AddTokenizers();
|
||||||
services.AddOrganizationConnectionCommands();
|
services.AddOrganizationConnectionCommands();
|
||||||
services.AddOrganizationSponsorshipCommands(globalSettings);
|
services.AddOrganizationSponsorshipCommands(globalSettings);
|
||||||
services.AddOrganizationApiKeyCommands();
|
services.AddOrganizationApiKeyCommandsQueries();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOrganizationConnectionCommands(this IServiceCollection services)
|
private static void AddOrganizationConnectionCommands(this IServiceCollection services)
|
||||||
@ -59,10 +59,11 @@ public static class OrganizationServiceCollectionExtensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddOrganizationApiKeyCommands(this IServiceCollection services)
|
private static void AddOrganizationApiKeyCommandsQueries(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddScoped<IGetOrganizationApiKeyCommand, GetOrganizationApiKeyCommand>();
|
services.AddScoped<IGetOrganizationApiKeyQuery, GetOrganizationApiKeyQuery>();
|
||||||
services.AddScoped<IRotateOrganizationApiKeyCommand, RotateOrganizationApiKeyCommand>();
|
services.AddScoped<IRotateOrganizationApiKeyCommand, RotateOrganizationApiKeyCommand>();
|
||||||
|
services.AddScoped<ICreateOrganizationApiKeyCommand, CreateOrganizationApiKeyCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddTokenizers(this IServiceCollection services)
|
private static void AddTokenizers(this IServiceCollection services)
|
||||||
|
@ -26,9 +26,10 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
private readonly ISsoConfigService _ssoConfigService;
|
private readonly ISsoConfigService _ssoConfigService;
|
||||||
private readonly IUserService _userService;
|
private readonly IUserService _userService;
|
||||||
private readonly IGetOrganizationApiKeyCommand _getOrganizationApiKeyCommand;
|
private readonly IGetOrganizationApiKeyQuery _getOrganizationApiKeyQuery;
|
||||||
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
private readonly IRotateOrganizationApiKeyCommand _rotateOrganizationApiKeyCommand;
|
||||||
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
private readonly IOrganizationApiKeyRepository _organizationApiKeyRepository;
|
||||||
|
private readonly ICreateOrganizationApiKeyCommand _createOrganizationApiKeyCommand;
|
||||||
|
|
||||||
private readonly OrganizationsController _sut;
|
private readonly OrganizationsController _sut;
|
||||||
|
|
||||||
@ -43,15 +44,16 @@ public class OrganizationsControllerTests : IDisposable
|
|||||||
_policyRepository = Substitute.For<IPolicyRepository>();
|
_policyRepository = Substitute.For<IPolicyRepository>();
|
||||||
_ssoConfigRepository = Substitute.For<ISsoConfigRepository>();
|
_ssoConfigRepository = Substitute.For<ISsoConfigRepository>();
|
||||||
_ssoConfigService = Substitute.For<ISsoConfigService>();
|
_ssoConfigService = Substitute.For<ISsoConfigService>();
|
||||||
_getOrganizationApiKeyCommand = Substitute.For<IGetOrganizationApiKeyCommand>();
|
_getOrganizationApiKeyQuery = Substitute.For<IGetOrganizationApiKeyQuery>();
|
||||||
_rotateOrganizationApiKeyCommand = Substitute.For<IRotateOrganizationApiKeyCommand>();
|
_rotateOrganizationApiKeyCommand = Substitute.For<IRotateOrganizationApiKeyCommand>();
|
||||||
_organizationApiKeyRepository = Substitute.For<IOrganizationApiKeyRepository>();
|
_organizationApiKeyRepository = Substitute.For<IOrganizationApiKeyRepository>();
|
||||||
_userService = Substitute.For<IUserService>();
|
_userService = Substitute.For<IUserService>();
|
||||||
|
_createOrganizationApiKeyCommand = Substitute.For<ICreateOrganizationApiKeyCommand>();
|
||||||
|
|
||||||
_sut = new OrganizationsController(_organizationRepository, _organizationUserRepository,
|
_sut = new OrganizationsController(_organizationRepository, _organizationUserRepository,
|
||||||
_policyRepository, _organizationService, _userService, _paymentService, _currentContext,
|
_policyRepository, _organizationService, _userService, _paymentService, _currentContext,
|
||||||
_ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyCommand, _rotateOrganizationApiKeyCommand,
|
_ssoConfigRepository, _ssoConfigService, _getOrganizationApiKeyQuery, _rotateOrganizationApiKeyCommand,
|
||||||
_organizationApiKeyRepository, _globalSettings);
|
_createOrganizationApiKeyCommand, _organizationApiKeyRepository, _globalSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.OrganizationFeatures.OrganizationApiKeys;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Test.Common.AutoFixture;
|
||||||
|
using Bit.Test.Common.AutoFixture.Attributes;
|
||||||
|
using NSubstitute;
|
||||||
|
using NSubstitute.ReceivedExtensions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
|
||||||
|
|
||||||
|
[SutProviderCustomize]
|
||||||
|
public class CreateOrganizationApiKeyCommandTest
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public async Task CreateAsync_CreatesOrganizationApiKey(SutProvider<CreateOrganizationApiKeyCommand> sutProvider,
|
||||||
|
Guid organizationId, OrganizationApiKeyType keyType)
|
||||||
|
{
|
||||||
|
await sutProvider.Sut.CreateAsync(organizationId, keyType);
|
||||||
|
|
||||||
|
await sutProvider.GetDependency<IOrganizationApiKeyRepository>().Received(1)
|
||||||
|
.CreateAsync(Arg.Is<OrganizationApiKey>(o => o.OrganizationId == organizationId
|
||||||
|
&& o.Type == keyType));
|
||||||
|
}
|
||||||
|
}
|
@ -10,11 +10,11 @@ using Xunit;
|
|||||||
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
|
namespace Bit.Core.Test.OrganizationFeatures.OrganizationApiKeys;
|
||||||
|
|
||||||
[SutProviderCustomize]
|
[SutProviderCustomize]
|
||||||
public class GetOrganizationApiKeyCommandTests
|
public class GetOrganizationApiKeyQueryTests
|
||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task GetOrganizationApiKey_HasOne_Returns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
|
public async Task GetOrganizationApiKey_HasOne_Returns(SutProvider<GetOrganizationApiKeyQuery> sutProvider,
|
||||||
Guid id, Guid organizationId, OrganizationApiKeyType keyType)
|
Guid id, Guid organizationId, OrganizationApiKeyType keyType)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
||||||
@ -38,7 +38,7 @@ public class GetOrganizationApiKeyCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task GetOrganizationApiKey_HasTwo_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
|
public async Task GetOrganizationApiKey_HasTwo_Throws(SutProvider<GetOrganizationApiKeyQuery> sutProvider,
|
||||||
Guid organizationId, OrganizationApiKeyType keyType)
|
Guid organizationId, OrganizationApiKeyType keyType)
|
||||||
{
|
{
|
||||||
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
||||||
@ -69,26 +69,7 @@ public class GetOrganizationApiKeyCommandTests
|
|||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public async Task GetOrganizationApiKey_HasNone_CreatesAndReturns(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
|
public async Task GetOrganizationApiKey_BadType_Throws(SutProvider<GetOrganizationApiKeyQuery> sutProvider,
|
||||||
Guid organizationId, OrganizationApiKeyType keyType)
|
|
||||||
{
|
|
||||||
sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
|
||||||
.GetManyByOrganizationIdTypeAsync(organizationId, keyType)
|
|
||||||
.Returns(Enumerable.Empty<OrganizationApiKey>());
|
|
||||||
|
|
||||||
var apiKey = await sutProvider.Sut.GetOrganizationApiKeyAsync(organizationId, keyType);
|
|
||||||
|
|
||||||
Assert.NotNull(apiKey);
|
|
||||||
Assert.Equal(organizationId, apiKey.OrganizationId);
|
|
||||||
Assert.Equal(keyType, apiKey.Type);
|
|
||||||
await sutProvider.GetDependency<IOrganizationApiKeyRepository>()
|
|
||||||
.Received(1)
|
|
||||||
.CreateAsync(Arg.Any<OrganizationApiKey>());
|
|
||||||
}
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[BitAutoData]
|
|
||||||
public async Task GetOrganizationApiKey_BadType_Throws(SutProvider<GetOrganizationApiKeyCommand> sutProvider,
|
|
||||||
Guid organizationId, OrganizationApiKeyType keyType)
|
Guid organizationId, OrganizationApiKeyType keyType)
|
||||||
{
|
{
|
||||||
keyType = (OrganizationApiKeyType)byte.MaxValue;
|
keyType = (OrganizationApiKeyType)byte.MaxValue;
|
Loading…
x
Reference in New Issue
Block a user