1
0
mirror of https://github.com/bitwarden/server synced 2026-01-02 16:43:25 +00:00

[PM-25652] Add endpoint to fetch key connector confirmation details (#6635)

* Add new endpoint and query for key connector

* Add unit tests
This commit is contained in:
Thomas Avery
2025-12-10 14:53:38 -06:00
committed by GitHub
parent 8064ae1e05
commit f86d1a51dd
9 changed files with 256 additions and 29 deletions

View File

@@ -3,9 +3,11 @@ using System.Net;
using Bit.Api.IntegrationTest.Factories;
using Bit.Api.IntegrationTest.Helpers;
using Bit.Api.KeyManagement.Models.Requests;
using Bit.Api.KeyManagement.Models.Responses;
using Bit.Api.Tools.Models.Request;
using Bit.Api.Vault.Models;
using Bit.Api.Vault.Models.Request;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Auth.Entities;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Request.Accounts;
@@ -286,20 +288,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
public async Task PostSetKeyConnectorKeyAsync_Success(string organizationSsoIdentifier,
SetKeyConnectorKeyRequestModel request)
{
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
PlanType.EnterpriseAnnually, _ownerEmail, passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
organization.UseKeyConnector = true;
organization.UseSso = true;
organization.Identifier = organizationSsoIdentifier;
await _organizationRepository.ReplaceAsync(organization);
var ssoUserEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
await _factory.LoginWithNewAccount(ssoUserEmail);
await _loginHelper.LoginAsync(ssoUserEmail);
await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, ssoUserEmail,
OrganizationUserType.User, userStatusType: OrganizationUserStatusType.Invited);
var (ssoUserEmail, organization) = await SetupKeyConnectorTestAsync(OrganizationUserStatusType.Invited, organizationSsoIdentifier);
var ssoUser = await _userRepository.GetByEmailAsync(ssoUserEmail);
Assert.NotNull(ssoUser);
@@ -340,19 +329,7 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
[Fact]
public async Task PostConvertToKeyConnectorAsync_Success()
{
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
PlanType.EnterpriseAnnually, _ownerEmail, passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
organization.UseKeyConnector = true;
organization.UseSso = true;
await _organizationRepository.ReplaceAsync(organization);
var ssoUserEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
await _factory.LoginWithNewAccount(ssoUserEmail);
await _loginHelper.LoginAsync(ssoUserEmail);
await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, ssoUserEmail,
OrganizationUserType.User, userStatusType: OrganizationUserStatusType.Accepted);
var (ssoUserEmail, organization) = await SetupKeyConnectorTestAsync(OrganizationUserStatusType.Accepted);
var response = await _client.PostAsJsonAsync("/accounts/convert-to-key-connector", new { });
response.EnsureSuccessStatusCode();
@@ -556,4 +533,41 @@ public class AccountsKeyManagementControllerTests : IClassFixture<ApiApplication
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfMemory, userNewState.KdfMemory);
Assert.Equal(request.AccountUnlockData.MasterPasswordUnlockData.KdfParallelism, userNewState.KdfParallelism);
}
[Fact]
public async Task GetKeyConnectorConfirmationDetailsAsync_Success()
{
var (ssoUserEmail, organization) = await SetupKeyConnectorTestAsync(OrganizationUserStatusType.Invited);
await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, ssoUserEmail,
OrganizationUserType.User, userStatusType: OrganizationUserStatusType.Accepted);
var response = await _client.GetAsync($"/accounts/key-connector/confirmation-details/{organization.Identifier}");
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<KeyConnectorConfirmationDetailsResponseModel>();
Assert.NotNull(result);
Assert.Equal(organization.Name, result.OrganizationName);
}
private async Task<(string, Organization)> SetupKeyConnectorTestAsync(OrganizationUserStatusType userStatusType,
string organizationSsoIdentifier = "test-sso-identifier")
{
var (organization, _) = await OrganizationTestHelpers.SignUpAsync(_factory,
PlanType.EnterpriseAnnually, _ownerEmail, passwordManagerSeats: 10,
paymentMethod: PaymentMethodType.Card);
organization.UseKeyConnector = true;
organization.UseSso = true;
organization.Identifier = organizationSsoIdentifier;
await _organizationRepository.ReplaceAsync(organization);
var ssoUserEmail = $"integration-test{Guid.NewGuid()}@bitwarden.com";
await _factory.LoginWithNewAccount(ssoUserEmail);
await _loginHelper.LoginAsync(ssoUserEmail);
await OrganizationTestHelpers.CreateUserAsync(_factory, organization.Id, ssoUserEmail,
OrganizationUserType.User, userStatusType: userStatusType);
return (ssoUserEmail, organization);
}
}

View File

@@ -16,6 +16,7 @@ using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Commands.Interfaces;
using Bit.Core.KeyManagement.Models.Api.Request;
using Bit.Core.KeyManagement.Models.Data;
using Bit.Core.KeyManagement.Queries.Interfaces;
using Bit.Core.KeyManagement.UserKey;
using Bit.Core.Repositories;
using Bit.Core.Services;
@@ -363,4 +364,39 @@ public class AccountsKeyManagementControllerTests
await sutProvider.GetDependency<IUserService>().Received(1)
.ConvertToKeyConnectorAsync(Arg.Is(expectedUser));
}
[Theory]
[BitAutoData]
public async Task GetKeyConnectorConfirmationDetailsAsync_NoUser_Throws(
SutProvider<AccountsKeyManagementController> sutProvider, string orgSsoIdentifier)
{
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.ReturnsNull();
await Assert.ThrowsAsync<UnauthorizedAccessException>(() =>
sutProvider.Sut.GetKeyConnectorConfirmationDetailsAsync(orgSsoIdentifier));
await sutProvider.GetDependency<IKeyConnectorConfirmationDetailsQuery>().ReceivedWithAnyArgs(0)
.Run(Arg.Any<string>(), Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task GetKeyConnectorConfirmationDetailsAsync_Success(
SutProvider<AccountsKeyManagementController> sutProvider, User expectedUser, string orgSsoIdentifier)
{
sutProvider.GetDependency<IUserService>().GetUserByPrincipalAsync(Arg.Any<ClaimsPrincipal>())
.Returns(expectedUser);
sutProvider.GetDependency<IKeyConnectorConfirmationDetailsQuery>().Run(orgSsoIdentifier, expectedUser.Id)
.Returns(
new KeyConnectorConfirmationDetails { OrganizationName = "test" }
);
var result = await sutProvider.Sut.GetKeyConnectorConfirmationDetailsAsync(orgSsoIdentifier);
Assert.NotNull(result);
Assert.Equal("test", result.OrganizationName);
await sutProvider.GetDependency<IKeyConnectorConfirmationDetailsQuery>().Received(1)
.Run(orgSsoIdentifier, expectedUser.Id);
}
}

View File

@@ -0,0 +1,86 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
using Bit.Core.KeyManagement.Queries;
using Bit.Core.Repositories;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
using NSubstitute;
using Xunit;
namespace Bit.Core.Test.KeyManagement.Queries;
[SutProviderCustomize]
public class KeyConnectorConfirmationDetailsQueryTests
{
[Theory]
[BitAutoData]
public async Task Run_OrganizationNotFound_Throws(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
Guid userId, string orgSsoIdentifier)
{
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
await sutProvider.GetDependency<IOrganizationUserRepository>()
.ReceivedWithAnyArgs(0)
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task Run_OrganizationNotKeyConnector_Throws(
SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
Guid userId, string orgSsoIdentifier, Organization org)
{
org.Identifier = orgSsoIdentifier;
org.UseKeyConnector = false;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
await sutProvider.GetDependency<IOrganizationUserRepository>()
.ReceivedWithAnyArgs(0)
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>());
}
[Theory]
[BitAutoData]
public async Task Run_OrganizationUserNotFound_Throws(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider,
Guid userId, string orgSsoIdentifier
, Organization org)
{
org.Identifier = orgSsoIdentifier;
org.UseKeyConnector = true;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
sutProvider.GetDependency<IOrganizationUserRepository>()
.GetByOrganizationAsync(Arg.Any<Guid>(), Arg.Any<Guid>()).Returns(Task.FromResult<OrganizationUser>(null));
await Assert.ThrowsAsync<NotFoundException>(() => sutProvider.Sut.Run(orgSsoIdentifier, userId));
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.GetByOrganizationAsync(org.Id, userId);
}
[Theory]
[BitAutoData]
public async Task Run_Success(SutProvider<KeyConnectorConfirmationDetailsQuery> sutProvider, Guid userId,
string orgSsoIdentifier
, Organization org, OrganizationUser orgUser)
{
org.Identifier = orgSsoIdentifier;
org.UseKeyConnector = true;
orgUser.OrganizationId = org.Id;
orgUser.UserId = userId;
sutProvider.GetDependency<IOrganizationRepository>().GetByIdentifierAsync(orgSsoIdentifier).Returns(org);
sutProvider.GetDependency<IOrganizationUserRepository>().GetByOrganizationAsync(org.Id, userId)
.Returns(orgUser);
var result = await sutProvider.Sut.Run(orgSsoIdentifier, userId);
Assert.Equal(org.Name, result.OrganizationName);
await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1)
.GetByOrganizationAsync(org.Id, userId);
}
}