mirror of
https://github.com/bitwarden/server.git
synced 2025-05-03 10:42:21 -05:00
Added integration test around enabling feature and sending invite via scim. Did a bit of refactoring on the SM validation. Fixed couple bugs found.
This commit is contained in:
parent
bd5189491e
commit
001a5dea86
@ -1,9 +1,12 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Bit.Core;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Scim.IntegrationTest.Factories;
|
using Bit.Scim.IntegrationTest.Factories;
|
||||||
using Bit.Scim.Models;
|
using Bit.Scim.Models;
|
||||||
using Bit.Scim.Utilities;
|
using Bit.Scim.Utilities;
|
||||||
using Bit.Test.Common.Helpers;
|
using Bit.Test.Common.Helpers;
|
||||||
|
using NSubstitute;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Scim.IntegrationTest.Controllers.v2;
|
namespace Bit.Scim.IntegrationTest.Controllers.v2;
|
||||||
@ -276,9 +279,15 @@ public class UsersControllerTests : IClassFixture<ScimApplicationFactory>, IAsyn
|
|||||||
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
|
AssertHelper.AssertPropertyEqual(expectedResponse, responseModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task Post_Success()
|
[InlineData(true)]
|
||||||
|
[InlineData(false)]
|
||||||
|
public async Task Post_Success(bool isScimInviteUserOptimizationEnabled)
|
||||||
{
|
{
|
||||||
|
_factory.SubstituteService((IFeatureService featureService)
|
||||||
|
=> featureService.IsEnabled(FeatureFlagKeys.ScimInviteUserOptimization)
|
||||||
|
.Returns(isScimInviteUserOptimizationEnabled));
|
||||||
|
|
||||||
var email = "user5@example.com";
|
var email = "user5@example.com";
|
||||||
var displayName = "Test User 5";
|
var displayName = "Test User 5";
|
||||||
var externalId = "UE";
|
var externalId = "UE";
|
||||||
|
@ -96,7 +96,8 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
|
|||||||
var valid = validationResult as Valid<InviteUserOrganizationValidationRequest>;
|
var valid = validationResult as Valid<InviteUserOrganizationValidationRequest>;
|
||||||
|
|
||||||
var organizationUserCollection = invitesToSend
|
var organizationUserCollection = invitesToSend
|
||||||
.Select(MapToDataModel(request.PerformedAt));
|
.Select(MapToDataModel(request.PerformedAt))
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
var organization = await organizationRepository.GetByIdAsync(valid.Value.Organization.OrganizationId);
|
var organization = await organizationRepository.GetByIdAsync(valid.Value.Organization.OrganizationId);
|
||||||
try
|
try
|
||||||
|
@ -5,6 +5,20 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
|
|||||||
|
|
||||||
public class InviteUserOrganizationValidationRequest
|
public class InviteUserOrganizationValidationRequest
|
||||||
{
|
{
|
||||||
|
public InviteUserOrganizationValidationRequest() { }
|
||||||
|
|
||||||
|
public InviteUserOrganizationValidationRequest(InviteUserOrganizationValidationRequest request, PasswordManagerSubscriptionUpdate subscriptionUpdate, SecretsManagerSubscriptionUpdate smSubscriptionUpdate)
|
||||||
|
{
|
||||||
|
Invites = request.Invites;
|
||||||
|
Organization = request.Organization;
|
||||||
|
PerformedBy = request.PerformedBy;
|
||||||
|
PerformedAt = request.PerformedAt;
|
||||||
|
OccupiedPmSeats = request.OccupiedPmSeats;
|
||||||
|
OccupiedSmSeats = request.OccupiedSmSeats;
|
||||||
|
PasswordManagerSubscriptionUpdate = subscriptionUpdate;
|
||||||
|
SecretsManagerSubscriptionUpdate = smSubscriptionUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
public OrganizationUserInviteDto[] Invites { get; init; } = [];
|
public OrganizationUserInviteDto[] Invites { get; init; } = [];
|
||||||
public OrganizationDto Organization { get; init; }
|
public OrganizationDto Organization { get; init; }
|
||||||
public Guid PerformedBy { get; init; }
|
public Guid PerformedBy { get; init; }
|
||||||
|
@ -51,7 +51,8 @@ public class InviteUsersValidation(
|
|||||||
|
|
||||||
var provider = await providerRepository.GetByOrganizationIdAsync(request.Organization.OrganizationId);
|
var provider = await providerRepository.GetByOrganizationIdAsync(request.Organization.OrganizationId);
|
||||||
|
|
||||||
if (InvitingUserOrganizationProviderValidation.Validate(ProviderDto.FromProviderEntity(provider)) is
|
if (provider is not null &&
|
||||||
|
InvitingUserOrganizationProviderValidation.Validate(ProviderDto.FromProviderEntity(provider)) is
|
||||||
Invalid<ProviderDto> invalidProviderValidation)
|
Invalid<ProviderDto> invalidProviderValidation)
|
||||||
{
|
{
|
||||||
return new Invalid<InviteUserOrganizationValidationRequest>(invalidProviderValidation.ErrorMessageString);
|
return new Invalid<InviteUserOrganizationValidationRequest>(invalidProviderValidation.ErrorMessageString);
|
||||||
@ -65,7 +66,10 @@ public class InviteUsersValidation(
|
|||||||
return new Invalid<InviteUserOrganizationValidationRequest>(invalidPaymentValidation.ErrorMessageString);
|
return new Invalid<InviteUserOrganizationValidationRequest>(invalidPaymentValidation.ErrorMessageString);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Valid<InviteUserOrganizationValidationRequest>(null);
|
return new Valid<InviteUserOrganizationValidationRequest>(new InviteUserOrganizationValidationRequest(
|
||||||
|
request,
|
||||||
|
subscriptionUpdate,
|
||||||
|
smSubscriptionUpdate));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValidationResult<IGlobalSettings> ValidateEnvironment(IGlobalSettings globalSettings) =>
|
public static ValidationResult<IGlobalSettings> ValidateEnvironment(IGlobalSettings globalSettings) =>
|
||||||
|
@ -7,6 +7,11 @@ public static class InvitingUserOrganizationValidation
|
|||||||
{
|
{
|
||||||
public static ValidationResult<OrganizationDto> Validate(OrganizationDto organization)
|
public static ValidationResult<OrganizationDto> Validate(OrganizationDto organization)
|
||||||
{
|
{
|
||||||
|
if (organization.Seats is null)
|
||||||
|
{
|
||||||
|
return new Valid<OrganizationDto>(organization);
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
if (string.IsNullOrWhiteSpace(organization.GatewayCustomerId))
|
||||||
{
|
{
|
||||||
return new Invalid<OrganizationDto>(NoPaymentMethodFoundError);
|
return new Invalid<OrganizationDto>(NoPaymentMethodFoundError);
|
||||||
|
@ -5,41 +5,39 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
|
|||||||
|
|
||||||
public static class SecretsManagerInviteUserValidation
|
public static class SecretsManagerInviteUserValidation
|
||||||
{
|
{
|
||||||
// NOTE This is only validating adding new users
|
public static ValidationResult<SecretsManagerSubscriptionUpdate> Validate(
|
||||||
public static ValidationResult<SecretsManagerSubscriptionUpdate> Validate(SecretsManagerSubscriptionUpdate subscriptionUpdate)
|
SecretsManagerSubscriptionUpdate subscriptionUpdate) =>
|
||||||
{
|
subscriptionUpdate switch
|
||||||
if (subscriptionUpdate.UseSecretsManger is false)
|
|
||||||
{
|
{
|
||||||
return new Invalid<SecretsManagerSubscriptionUpdate>(OrganizationNoSecretsManager);
|
{ UseSecretsManger: false, AdditionalSeats: > 0 } =>
|
||||||
}
|
new Invalid<SecretsManagerSubscriptionUpdate>(OrganizationNoSecretsManager),
|
||||||
|
|
||||||
if (subscriptionUpdate.Seats == null)
|
{ UseSecretsManger: false, AdditionalSeats: 0 } or { UseSecretsManger: true, Seats: null } =>
|
||||||
{
|
new Valid<SecretsManagerSubscriptionUpdate>(subscriptionUpdate),
|
||||||
return new Valid<SecretsManagerSubscriptionUpdate>(subscriptionUpdate); // no need to adjust seats...continue on
|
|
||||||
}
|
|
||||||
|
|
||||||
// max additional seats is never set...maybe remove this
|
{ UseSecretsManger: true, SecretsManagerPlan.HasAdditionalSeatsOption: false } =>
|
||||||
if (subscriptionUpdate.SecretsManagerPlan is { HasAdditionalSeatsOption: false } ||
|
new Invalid<SecretsManagerSubscriptionUpdate>(
|
||||||
subscriptionUpdate.SecretsManagerPlan.MaxAdditionalSeats is not null &&
|
string.Format(SecretsManagerAdditionalSeatLimitReached,
|
||||||
subscriptionUpdate.AdditionalSeats > subscriptionUpdate.SecretsManagerPlan.MaxAdditionalSeats)
|
|
||||||
{
|
|
||||||
return new Invalid<SecretsManagerSubscriptionUpdate>(
|
|
||||||
string.Format(SecretsManagerAdditionalSeatLimitReached,
|
|
||||||
subscriptionUpdate.SecretsManagerPlan.BaseSeats +
|
subscriptionUpdate.SecretsManagerPlan.BaseSeats +
|
||||||
subscriptionUpdate.SecretsManagerPlan.MaxAdditionalSeats.GetValueOrDefault()));
|
subscriptionUpdate.SecretsManagerPlan.MaxAdditionalSeats.GetValueOrDefault())),
|
||||||
}
|
|
||||||
|
|
||||||
if (subscriptionUpdate.UpdatedSeatTotal is not null && subscriptionUpdate.MaxAutoScaleSeats is not null &&
|
{ UseSecretsManger: true, SecretsManagerPlan.MaxAdditionalSeats: var planMaxSeats }
|
||||||
subscriptionUpdate.UpdatedSeatTotal > subscriptionUpdate.MaxAutoScaleSeats)
|
when planMaxSeats < subscriptionUpdate.AdditionalSeats =>
|
||||||
{
|
new Invalid<SecretsManagerSubscriptionUpdate>(
|
||||||
return new Invalid<SecretsManagerSubscriptionUpdate>(SecretsManagerSeatLimitReached);
|
string.Format(SecretsManagerAdditionalSeatLimitReached,
|
||||||
}
|
subscriptionUpdate.SecretsManagerPlan.BaseSeats +
|
||||||
|
subscriptionUpdate.SecretsManagerPlan.MaxAdditionalSeats.GetValueOrDefault())),
|
||||||
|
|
||||||
if (subscriptionUpdate.PasswordManagerUpdatedSeatTotal < subscriptionUpdate.UpdatedSeatTotal)
|
{ UseSecretsManger: true, UpdatedSeatTotal: var updateSeatTotal, MaxAutoScaleSeats: var maxAutoScaleSeats }
|
||||||
{
|
when updateSeatTotal > maxAutoScaleSeats =>
|
||||||
return new Invalid<SecretsManagerSubscriptionUpdate>(SecretsManagerCannotExceedPasswordManager);
|
new Invalid<SecretsManagerSubscriptionUpdate>(SecretsManagerSeatLimitReached),
|
||||||
}
|
|
||||||
|
|
||||||
return new Valid<SecretsManagerSubscriptionUpdate>(subscriptionUpdate);
|
{
|
||||||
}
|
PasswordManagerUpdatedSeatTotal: var passwordManagerUpdatedSeatTotal,
|
||||||
|
UpdatedSeatTotal: var secretsManagerUpdatedSeatTotal
|
||||||
|
} when passwordManagerUpdatedSeatTotal < secretsManagerUpdatedSeatTotal =>
|
||||||
|
new Invalid<SecretsManagerSubscriptionUpdate>(SecretsManagerCannotExceedPasswordManager),
|
||||||
|
|
||||||
|
_ => new Valid<SecretsManagerSubscriptionUpdate>(subscriptionUpdate)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ public class SecretsManagerInviteUserValidationTests
|
|||||||
{
|
{
|
||||||
[Theory]
|
[Theory]
|
||||||
[BitAutoData]
|
[BitAutoData]
|
||||||
public void Validate_GivenOrganizationDoesNotHaveSecretsManager_ThenShouldNotBeAllowedToAddSecretsManagerUsers(
|
public void Validate_GivenOrganizationDoesNotHaveSecretsManagerAndNotTryingToAddSecretsManagerUser_ThenTheRequestIsValid(
|
||||||
Organization organization)
|
Organization organization)
|
||||||
{
|
{
|
||||||
organization.UseSecretsManager = false;
|
organization.UseSecretsManager = false;
|
||||||
@ -38,6 +38,35 @@ public class SecretsManagerInviteUserValidationTests
|
|||||||
|
|
||||||
var result = SecretsManagerInviteUserValidation.Validate(update);
|
var result = SecretsManagerInviteUserValidation.Validate(update);
|
||||||
|
|
||||||
|
Assert.IsType<Valid<SecretsManagerSubscriptionUpdate>>(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[BitAutoData]
|
||||||
|
public void Validate_GivenOrganizationDoesNotHaveSecretsManagerAndTryingToAddSecretsManagerUser_ThenShouldReturnInvalidMessage(
|
||||||
|
Organization organization)
|
||||||
|
{
|
||||||
|
organization.UseSecretsManager = false;
|
||||||
|
|
||||||
|
var organizationDto = OrganizationDto.FromOrganization(organization);
|
||||||
|
var subscriptionUpdate = PasswordManagerSubscriptionUpdate.Create(organizationDto, 0, 0);
|
||||||
|
|
||||||
|
var invite = OrganizationUserInvite.Create(["email@test.com"], [], OrganizationUserType.User, new Permissions(), string.Empty, true);
|
||||||
|
|
||||||
|
var request = new InviteUserOrganizationValidationRequest
|
||||||
|
{
|
||||||
|
Invites = [OrganizationUserInviteDto.Create(invite.Emails.First(), invite, organizationDto.OrganizationId)],
|
||||||
|
Organization = organizationDto,
|
||||||
|
PerformedBy = Guid.Empty,
|
||||||
|
PerformedAt = default,
|
||||||
|
OccupiedPmSeats = 0,
|
||||||
|
OccupiedSmSeats = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
var update = SecretsManagerSubscriptionUpdate.Create(request, subscriptionUpdate);
|
||||||
|
|
||||||
|
var result = SecretsManagerInviteUserValidation.Validate(update);
|
||||||
|
|
||||||
Assert.IsType<Invalid<SecretsManagerSubscriptionUpdate>>(result);
|
Assert.IsType<Invalid<SecretsManagerSubscriptionUpdate>>(result);
|
||||||
Assert.Equal(InviteUserValidationErrorMessages.OrganizationNoSecretsManager, result.ErrorMessageString);
|
Assert.Equal(InviteUserValidationErrorMessages.OrganizationNoSecretsManager, result.ErrorMessageString);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user