1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-05 21:18:13 -05:00

Cleaned up DTO models. Moved some validation steps around. A few quick fixes to address CR concerns. Still need to move a few things yet.

This commit is contained in:
jrmccannon 2025-03-26 10:56:33 -05:00
parent f3f2f41cfb
commit ad3131f66e
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
19 changed files with 288 additions and 294 deletions

View File

@ -13,7 +13,8 @@ public class ScimUserRequestModel : BaseScimUserModel
{ {
public ScimUserRequestModel() public ScimUserRequestModel()
: base(false) : base(false)
{ } {
}
public OrganizationUserInvite ToOrganizationUserInvite(ScimProviderType scimProvider) public OrganizationUserInvite ToOrganizationUserInvite(ScimProviderType scimProvider)
{ {
@ -28,7 +29,7 @@ public class ScimUserRequestModel : BaseScimUserModel
}; };
} }
public OrganizationUserSingleEmailInvite ToRequest( public InviteOrganizationUsersRequest ToRequest(
ScimProviderType scimProvider, ScimProviderType scimProvider,
InviteOrganization inviteOrganization, InviteOrganization inviteOrganization,
DateTimeOffset performedAt) DateTimeOffset performedAt)
@ -40,11 +41,18 @@ public class ScimUserRequestModel : BaseScimUserModel
throw new BadRequestException(); throw new BadRequestException();
} }
return new( return new InviteOrganizationUsersRequest(
invites:
[
new Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite(
email: email, email: email,
externalId: ExternalIdForInvite(),
accessSecretsManager: false // TODO do something about this
)
],
inviteOrganization: inviteOrganization, inviteOrganization: inviteOrganization,
performedAt: performedAt, performedBy: Guid.Empty, // SCIM does not have a user id
externalId: ExternalIdForInvite()); performedAt: performedAt);
} }
private string EmailForInvite(ScimProviderType scimProvider) private string EmailForInvite(ScimProviderType scimProvider)

View File

@ -19,5 +19,5 @@ public interface IInviteOrganizationUsersCommand
/// </param> /// </param>
/// <returns>Response from InviteScimOrganiation<see cref="ScimInviteOrganizationUsersResponse"/></returns> /// <returns>Response from InviteScimOrganiation<see cref="ScimInviteOrganizationUsersResponse"/></returns>
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync( Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(
OrganizationUserSingleEmailInvite request); InviteOrganizationUsersRequest request);
} }

View File

@ -17,6 +17,7 @@ using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services; using Bit.Core.Tools.Services;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.CreateOrganizationUser; using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.CreateOrganizationUser;
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@ -41,21 +42,23 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
public const string InvalidResultType = "Invalid result type."; public const string InvalidResultType = "Invalid result type.";
public async Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(OrganizationUserSingleEmailInvite request) public async Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(InviteOrganizationUsersRequest request)
{ {
var hasSecretsManagerStandalone = await paymentService.HasSecretsManagerStandalone(request.InviteOrganization); var hasSecretsManagerStandalone = await paymentService.HasSecretsManagerStandalone(request.InviteOrganization);
// Maybe move this all back up
var orgUsers = await organizationUserRepository.GetManyDetailsByOrganizationAsync(request.InviteOrganization.OrganizationId); var orgUsers = await organizationUserRepository.GetManyDetailsByOrganizationAsync(request.InviteOrganization.OrganizationId);
if (orgUsers.Any(existingUser => if (orgUsers.Any(existingUser =>
request.Email.Equals(existingUser.Email, StringComparison.InvariantCultureIgnoreCase) || request.Invites.First().Email.Equals(existingUser.Email, StringComparison.InvariantCultureIgnoreCase) ||
request.ExternalId.Equals(existingUser.ExternalId, StringComparison.InvariantCultureIgnoreCase))) request.Invites.First().ExternalId.Equals(existingUser.ExternalId, StringComparison.InvariantCultureIgnoreCase)))
{ {
return new Failure<ScimInviteOrganizationUsersResponse>( return new Failure<ScimInviteOrganizationUsersResponse>(
new UserAlreadyExistsError(new ScimInviteOrganizationUsersResponse(request))); new UserAlreadyExistsError(new ScimInviteOrganizationUsersResponse(request)));
} }
// end of move
var result = await InviteOrganizationUsersAsync(new InviteOrganizationUsersRequest(request, hasSecretsManagerStandalone)); var result = await InviteOrganizationUsersAsync(request);
switch (result) switch (result)
{ {
@ -83,15 +86,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
private async Task<CommandResult<IEnumerable<OrganizationUser>>> InviteOrganizationUsersAsync(InviteOrganizationUsersRequest request) private async Task<CommandResult<IEnumerable<OrganizationUser>>> InviteOrganizationUsersAsync(InviteOrganizationUsersRequest request)
{ {
var existingEmails = new HashSet<string>(await organizationUserRepository.SelectKnownEmailsAsync( var invitesToSend = (await FilterExistingUsersAsync(request)).ToArray();
request.InviteOrganization.OrganizationId, request.Invites.SelectMany(i => i.Emails), false),
StringComparer.InvariantCultureIgnoreCase);
var invitesToSend = request.Invites
.SelectMany(invite => invite.Emails
.Where(email => !existingEmails.Contains(email))
.Select(email => OrganizationUserInviteDto.Create(email, invite, request.InviteOrganization.OrganizationId))
).ToArray();
if (invitesToSend.Length == 0) if (invitesToSend.Length == 0)
{ {
@ -116,10 +111,10 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
var validatedRequest = validationResult as Valid<InviteUserOrganizationValidationRequest>; var validatedRequest = validationResult as Valid<InviteUserOrganizationValidationRequest>;
var organizationUserToInviteEntities = invitesToSend var organizationUserToInviteEntities = invitesToSend
.Select(MapToDataModel(request.PerformedAt)) .Select(MapToDataModel(request.PerformedAt, validatedRequest!.Value.InviteOrganization))
.ToArray(); .ToArray();
var organization = await organizationRepository.GetByIdAsync(validatedRequest!.Value.InviteOrganization.OrganizationId); var organization = await organizationRepository.GetByIdAsync(validatedRequest.Value.InviteOrganization.OrganizationId);
try try
{ {
@ -151,6 +146,17 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
return new Success<IEnumerable<OrganizationUser>>(organizationUserToInviteEntities.Select(x => x.OrganizationUser)); return new Success<IEnumerable<OrganizationUser>>(organizationUserToInviteEntities.Select(x => x.OrganizationUser));
} }
private async Task<IEnumerable<OrganizationUserInvite>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
{
var existingEmails = new HashSet<string>(await organizationUserRepository.SelectKnownEmailsAsync(
request.InviteOrganization.OrganizationId, request.Invites.Select(i => i.Email), false),
StringComparer.InvariantCultureIgnoreCase);
return request.Invites
.Where(invite => !existingEmails.Contains(invite.Email))
.ToArray();
}
private async Task RevertPasswordManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization) private async Task RevertPasswordManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0) if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0)

View File

@ -1,4 +1,5 @@
using Bit.Core.Entities; using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Utilities; using Bit.Core.Utilities;
@ -11,13 +12,14 @@ public class CreateOrganizationUser
public CollectionAccessSelection[] Collections { get; set; } = []; public CollectionAccessSelection[] Collections { get; set; } = [];
public Guid[] Groups { get; set; } = []; public Guid[] Groups { get; set; } = [];
public static Func<OrganizationUserInviteDto, CreateOrganizationUser> MapToDataModel(DateTimeOffset performedAt) => public static Func<OrganizationUserInvite, CreateOrganizationUser> MapToDataModel(DateTimeOffset performedAt,
InviteOrganization organization) =>
o => new CreateOrganizationUser o => new CreateOrganizationUser
{ {
OrganizationUser = new OrganizationUser OrganizationUser = new OrganizationUser
{ {
Id = CoreHelpers.GenerateComb(), Id = CoreHelpers.GenerateComb(),
OrganizationId = o.OrganizationId, OrganizationId = organization.OrganizationId,
Email = o.Email.ToLowerInvariant(), Email = o.Email.ToLowerInvariant(),
Type = o.Type, Type = o.Type,
Status = OrganizationUserStatusType.Invited, Status = OrganizationUserStatusType.Invited,

View File

@ -19,11 +19,4 @@ public class InviteOrganizationUsersRequest
PerformedBy = performedBy; PerformedBy = performedBy;
PerformedAt = performedAt; PerformedAt = performedAt;
} }
public InviteOrganizationUsersRequest(OrganizationUserSingleEmailInvite request, bool hasStandaloneSecretsManager) :
this([OrganizationUserInvite.Create(request, hasStandaloneSecretsManager)],
request.InviteOrganization,
Guid.Empty,
request.PerformedAt)
{ }
} }

View File

@ -16,12 +16,14 @@ public class ScimInviteOrganizationUsersResponse
} }
public ScimInviteOrganizationUsersResponse(OrganizationUserSingleEmailInvite request) public ScimInviteOrganizationUsersResponse(InviteOrganizationUsersRequest request)
{ {
var userToInvite = request.Invites.First();
InvitedUser = new OrganizationUser InvitedUser = new OrganizationUser
{ {
Email = request.Email, Email = userToInvite.Email,
ExternalId = request.ExternalId ExternalId = userToInvite.ExternalId
}; };
} }
} }

View File

@ -20,7 +20,7 @@ public class InviteUserOrganizationValidationRequest
SecretsManagerSubscriptionUpdate = smSubscriptionUpdate; SecretsManagerSubscriptionUpdate = smSubscriptionUpdate;
} }
public OrganizationUserInviteDto[] Invites { get; init; } = []; public OrganizationUserInvite[] Invites { get; init; } = [];
public InviteOrganization InviteOrganization { get; init; } public InviteOrganization InviteOrganization { get; init; }
public Guid PerformedBy { get; init; } public Guid PerformedBy { get; init; }
public DateTimeOffset PerformedAt { get; init; } public DateTimeOffset PerformedAt { get; init; }

View File

@ -9,50 +9,53 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public class OrganizationUserInvite public class OrganizationUserInvite
{ {
public string[] Emails { get; private init; } = []; public string Email { get; private init; }
public CollectionAccessSelection[] AssignedCollections { get; private init; } = []; public CollectionAccessSelection[] AssignedCollections { get; private init; }
public OrganizationUserType Type { get; private init; } = OrganizationUserType.User; public OrganizationUserType Type { get; private init; }
public Permissions Permissions { get; private init; } = new(); public Permissions Permissions { get; private init; }
public string ExternalId { get; private init; } = string.Empty; public string ExternalId { get; private init; }
public bool AccessSecretsManager { get; private init; } public bool AccessSecretsManager { get; private init; }
public Guid[] Groups { get; private init; } = []; public Guid[] Groups { get; private init; }
public static OrganizationUserInvite Create(string[] emails, public OrganizationUserInvite(string email, string externalId, bool accessSecretsManager) :
IEnumerable<CollectionAccessSelection> accessibleCollections, this(
email: email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: accessSecretsManager)
{
}
public OrganizationUserInvite(string email,
IEnumerable<CollectionAccessSelection> assignedCollections,
IEnumerable<Guid> groups,
OrganizationUserType type, OrganizationUserType type,
Permissions permissions, Permissions permissions,
string externalId, string externalId,
bool accessSecretsManager) bool accessSecretsManager)
{ {
ValidateEmailAddresses(emails); ValidateEmailAddress(email);
if (accessibleCollections?.Any(ValidateCollectionConfiguration) ?? false) var collections = assignedCollections?.ToArray() ?? [];
if (collections.Any(ValidateCollectionConfiguration))
{ {
throw new BadRequestException(InvalidCollectionConfigurationErrorMessage); throw new BadRequestException(InvalidCollectionConfigurationErrorMessage);
} }
return new OrganizationUserInvite Email = email;
{ AssignedCollections = collections;
Emails = emails, Groups = groups.ToArray();
AssignedCollections = accessibleCollections.ToArray(), Type = type;
Type = type, Permissions = permissions ?? new Permissions();
Permissions = permissions, ExternalId = externalId;
ExternalId = externalId, AccessSecretsManager = accessSecretsManager;
AccessSecretsManager = accessSecretsManager
};
} }
public static OrganizationUserInvite Create(OrganizationUserSingleEmailInvite invite, bool hasStandaloneSecretsManager) => private static void ValidateEmailAddress(string email)
Create([invite.Email],
invite.AccessibleCollections,
invite.Type,
invite.Permissions,
invite.ExternalId,
hasStandaloneSecretsManager);
private static void ValidateEmailAddresses(string[] emails)
{
foreach (var email in emails)
{ {
if (!email.IsValidEmail()) if (!email.IsValidEmail())
{ {
@ -60,4 +63,3 @@ public class OrganizationUserInvite
} }
} }
} }
}

View File

@ -1,31 +0,0 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
public class OrganizationUserInviteDto
{
public string Email { get; private init; } = string.Empty;
public CollectionAccessSelection[] AssignedCollections { get; private init; } = [];
public string ExternalId { get; private init; } = string.Empty;
public Permissions Permissions { get; private init; } = new();
public OrganizationUserType Type { get; private init; } = OrganizationUserType.User;
public bool AccessSecretsManager { get; private init; }
public Guid OrganizationId { get; private init; } = Guid.Empty;
public Guid[] Groups { get; private init; } = [];
public static OrganizationUserInviteDto Create(string email, OrganizationUserInvite invite, Guid organizationId)
{
return new OrganizationUserInviteDto
{
Email = email,
AssignedCollections = invite.AssignedCollections,
ExternalId = invite.ExternalId,
Type = invite.Type,
Permissions = invite.Permissions,
AccessSecretsManager = invite.AccessSecretsManager,
OrganizationId = organizationId,
Groups = invite.Groups
};
}
}

View File

@ -1,55 +0,0 @@
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Core.Utilities;
using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.InviteOrganizationUserErrorMessages;
using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.InviteOrganizationUserFunctions;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
public record OrganizationUserSingleEmailInvite
{
public string Email { get; init; } = string.Empty;
public CollectionAccessSelection[] AccessibleCollections { get; init; } = [];
public Permissions Permissions { get; init; } = new();
public OrganizationUserType Type { get; init; } = OrganizationUserType.User;
public InviteOrganization InviteOrganization { get; private init; }
public DateTimeOffset PerformedAt { get; private init; }
public string ExternalId { get; private init; } = string.Empty;
public OrganizationUserSingleEmailInvite(string email,
InviteOrganization inviteOrganization,
DateTimeOffset performedAt,
string externalId) : this(
email: email,
accessibleCollections: [],
type: OrganizationUserType.User,
permissions: new Permissions())
{
InviteOrganization = inviteOrganization;
PerformedAt = performedAt;
ExternalId = externalId;
}
public OrganizationUserSingleEmailInvite(string email,
IEnumerable<CollectionAccessSelection> accessibleCollections,
OrganizationUserType type,
Permissions permissions)
{
if (!email.IsValidEmail())
{
throw new BadRequestException(InvalidEmailErrorMessage);
}
if (accessibleCollections?.Any(ValidateCollectionConfiguration) ?? false)
{
throw new BadRequestException(InvalidCollectionConfigurationErrorMessage);
}
Email = email;
AccessibleCollections = accessibleCollections.ToArray();
Type = type;
Permissions = permissions;
}
}

View File

@ -4,6 +4,9 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public static class InviteOrganizationUserFunctions public static class InviteOrganizationUserFunctions
{ {
/// <summary>
/// This
/// </summary>
public static Func<CollectionAccessSelection, bool> ValidateCollectionConfiguration => collectionAccessSelection => public static Func<CollectionAccessSelection, bool> ValidateCollectionConfiguration => collectionAccessSelection =>
collectionAccessSelection.Manage && (collectionAccessSelection.ReadOnly || collectionAccessSelection.HidePasswords); collectionAccessSelection.Manage && (collectionAccessSelection.ReadOnly || collectionAccessSelection.HidePasswords);
} }

View File

@ -25,13 +25,6 @@ public class InviteUsersValidator(
{ {
public async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateAsync(InviteUserOrganizationValidationRequest request) public async Task<ValidationResult<InviteUserOrganizationValidationRequest>> ValidateAsync(InviteUserOrganizationValidationRequest request)
{ {
var organizationValidationResult = InviteUserOrganizationValidator.Validate(request.InviteOrganization);
if (organizationValidationResult is Invalid<InviteOrganization> organizationValidation)
{
return organizationValidation.Map(request);
}
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request); var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(request);
var passwordManagerValidationResult = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate); var passwordManagerValidationResult = PasswordManagerInviteUserValidator.Validate(subscriptionUpdate);
@ -45,6 +38,13 @@ public class InviteUsersValidator(
return invalidEnvironment.Map(request); return invalidEnvironment.Map(request);
} }
var organizationValidationResult = InviteUserOrganizationValidator.Validate(request.InviteOrganization, passwordManagerValidationResult as Valid<PasswordManagerSubscriptionUpdate>);
if (organizationValidationResult is Invalid<InviteOrganization> organizationValidation)
{
return organizationValidation.Map(request);
}
var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(request, subscriptionUpdate); var smSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(request, subscriptionUpdate);
var secretsManagerValidationResult = SecretsManagerInviteUserValidation.Validate(smSubscriptionUpdate); var secretsManagerValidationResult = SecretsManagerInviteUserValidation.Validate(smSubscriptionUpdate);

View File

@ -1,13 +1,15 @@
using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
using Bit.Core.AdminConsole.Shared.Validation; using Bit.Core.AdminConsole.Shared.Validation;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
public static class InviteUserOrganizationValidator public static class InviteUserOrganizationValidator
{ {
public static ValidationResult<InviteOrganization> Validate(InviteOrganization inviteOrganization) public static ValidationResult<InviteOrganization> Validate(InviteOrganization inviteOrganization,
Valid<PasswordManagerSubscriptionUpdate> passwordManagerValidationResult)
{ {
if (inviteOrganization.Seats is null) if (inviteOrganization.Seats is null || passwordManagerValidationResult.Value.SeatsRequiredToAdd is 0)
{ {
return new Valid<InviteOrganization>(inviteOrganization); return new Valid<InviteOrganization>(inviteOrganization);
} }

View File

@ -1,52 +0,0 @@
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Data;
using Bit.Test.Common.AutoFixture.Attributes;
using Xunit;
using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.InviteOrganizationUserErrorMessages;
namespace Bit.Core.Test.AdminConsole.Models;
public class InviteOrganizationUserRequestTests
{
[Theory]
[BitAutoData]
public void Create_WhenPassedInvalidEmail_ThrowsException(string email, OrganizationUserType type, Permissions permissions)
{
var action = () => new OrganizationUserSingleEmailInvite(email, [], type, permissions);
var exception = Assert.Throws<BadRequestException>(action);
Assert.Equal(InvalidEmailErrorMessage, exception.Message);
}
[Theory]
[BitAutoData]
public void Create_WhenPassedInvalidCollectionAccessConfiguration_ThrowsException(OrganizationUserType type, Permissions permissions)
{
var validEmail = "test@email.com";
var invalidCollectionConfiguration = new CollectionAccessSelection { Manage = true, HidePasswords = true };
var action = () => new OrganizationUserSingleEmailInvite(validEmail, [invalidCollectionConfiguration], type, permissions);
var exception = Assert.Throws<BadRequestException>(action);
Assert.Equal(InvalidCollectionConfigurationErrorMessage, exception.Message);
}
[Theory]
[BitAutoData]
public void Create_WhenPassedValidArguments_ReturnsInvite(OrganizationUserType type, Permissions permissions)
{
const string validEmail = "test@email.com";
var validCollectionConfiguration = new CollectionAccessSelection { Id = Guid.NewGuid(), Manage = true };
var invite = new OrganizationUserSingleEmailInvite(validEmail, [validCollectionConfiguration], type, permissions);
Assert.NotNull(invite);
Assert.Equal(validEmail, invite.Email);
Assert.Contains(validCollectionConfiguration, invite.AccessibleCollections);
}
}

View File

@ -13,17 +13,16 @@ public class InviteOrganizationUsersRequestTests
{ {
[Theory] [Theory]
[BitAutoData] [BitAutoData]
public void Create_WhenPassedInvalidEmails_ThrowsException(string[] emails, OrganizationUserType type, Permissions permissions, string externalId) public void Constructor_WhenPassedInvalidEmail_ThrowsException(string email, OrganizationUserType type, Permissions permissions, string externalId)
{ {
var action = () => OrganizationUserInvite.Create(emails, [], type, permissions, externalId, false); var exception = Assert.Throws<BadRequestException>(() =>
new OrganizationUserInvite(email, [], [], type, permissions, externalId, false));
var exception = Assert.Throws<BadRequestException>(action);
Assert.Contains(InvalidEmailErrorMessage, exception.Message); Assert.Contains(InvalidEmailErrorMessage, exception.Message);
} }
[Fact] [Fact]
public void Create_WhenPassedInvalidCollectionAccessConfiguration_ThrowsException() public void Constructor_WhenPassedInvalidCollectionAccessConfiguration_ThrowsException()
{ {
const string validEmail = "test@email.com"; const string validEmail = "test@email.com";
@ -33,23 +32,36 @@ public class InviteOrganizationUsersRequestTests
HidePasswords = true HidePasswords = true
}; };
var action = () => OrganizationUserInvite.Create([validEmail], [invalidCollectionConfiguration], default, default, default, false); var exception = Assert.Throws<BadRequestException>(() =>
new OrganizationUserInvite(
var exception = Assert.Throws<BadRequestException>(action); email: validEmail,
assignedCollections: [invalidCollectionConfiguration],
groups: [],
type: default,
permissions: new Permissions(),
externalId: string.Empty,
accessSecretsManager: false));
Assert.Equal(InvalidCollectionConfigurationErrorMessage, exception.Message); Assert.Equal(InvalidCollectionConfigurationErrorMessage, exception.Message);
} }
[Fact] [Fact]
public void Create_WhenPassedValidArguments_ReturnsInvite() public void Constructor_WhenPassedValidArguments_ReturnsInvite()
{ {
const string validEmail = "test@email.com"; const string validEmail = "test@email.com";
var validCollectionConfiguration = new CollectionAccessSelection { Id = Guid.NewGuid(), Manage = true }; var validCollectionConfiguration = new CollectionAccessSelection { Id = Guid.NewGuid(), Manage = true };
var invite = OrganizationUserInvite.Create([validEmail], [validCollectionConfiguration], default, default, default, false); var invite = new OrganizationUserInvite(
email: validEmail,
assignedCollections: [validCollectionConfiguration],
groups: [],
type: default,
permissions: null,
externalId: null,
accessSecretsManager: false);
Assert.NotNull(invite); Assert.NotNull(invite);
Assert.Contains(validEmail, invite.Emails); Assert.Contains(validEmail, invite.Email);
Assert.Contains(validCollectionConfiguration, invite.AssignedCollections); Assert.Contains(validCollectionConfiguration, invite.AssignedCollections);
} }
} }

View File

@ -7,15 +7,11 @@ namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.OrganizationUsers.Invi
public static class InviteUserOrganizationValidationRequestHelpers public static class InviteUserOrganizationValidationRequestHelpers
{ {
public static InviteUserOrganizationValidationRequest GetInviteValidationRequestMock(OrganizationUserSingleEmailInvite request, public static InviteUserOrganizationValidationRequest GetInviteValidationRequestMock(InviteOrganizationUsersRequest request,
InviteOrganization inviteOrganization) => InviteOrganization inviteOrganization) =>
new() new()
{ {
Invites = Invites = request.Invites,
[
OrganizationUserInviteDto.Create(request.Email,
OrganizationUserInvite.Create(request, inviteOrganization.UseSecretsManager), inviteOrganization.OrganizationId)
],
InviteOrganization = inviteOrganization, InviteOrganization = inviteOrganization,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = request.PerformedAt, PerformedAt = request.PerformedAt,

View File

@ -12,6 +12,7 @@ using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Core.Entities; using Bit.Core.Entities;
using Bit.Core.Enums; using Bit.Core.Enums;
using Bit.Core.Models.Commands; using Bit.Core.Models.Commands;
using Bit.Core.Models.Data;
using Bit.Core.Models.Data.Organizations.OrganizationUsers; using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.Models.StaticStore; using Bit.Core.Models.StaticStore;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface; using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
@ -42,12 +43,22 @@ public class InviteOrganizationUserCommandTests
// Arrange // Arrange
user.Email = address.Address; user.Email = address.Address;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(user.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: user.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
@ -55,7 +66,7 @@ public class InviteOrganizationUserCommandTests
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto))); .Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -90,12 +101,22 @@ public class InviteOrganizationUserCommandTests
// Arrange // Arrange
orgUser.Email = address.Address; orgUser.Email = address.Address;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(orgUser.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: orgUser.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
@ -107,7 +128,7 @@ public class InviteOrganizationUserCommandTests
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto))); .Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -118,7 +139,7 @@ public class InviteOrganizationUserCommandTests
await sutProvider.GetDependency<IOrganizationUserRepository>() await sutProvider.GetDependency<IOrganizationUserRepository>()
.Received(1) .Received(1)
.CreateManyAsync(Arg.Is<IEnumerable<CreateOrganizationUser>>(users => .CreateManyAsync(Arg.Is<IEnumerable<CreateOrganizationUser>>(users =>
users.Any(user => user.OrganizationUser.Email == request.Email))); users.Any(user => user.OrganizationUser.Email == request.Invites.First().Email)));
await sutProvider.GetDependency<ISendOrganizationInvitesCommand>() await sutProvider.GetDependency<ISendOrganizationInvitesCommand>()
.Received(1) .Received(1)
@ -142,12 +163,22 @@ public class InviteOrganizationUserCommandTests
user.Email = address.Address; user.Email = address.Address;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(user.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: user.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
sutProvider.GetDependency<IOrganizationUserRepository>() sutProvider.GetDependency<IOrganizationUserRepository>()
.SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(organization.Id, Arg.Any<IEnumerable<string>>(), false)
@ -196,20 +227,30 @@ public class InviteOrganizationUserCommandTests
organization.MaxAutoscaleSeats = 2; organization.MaxAutoscaleSeats = 2;
ownerDetails.Type = OrganizationUserType.Owner; ownerDetails.Type = OrganizationUserType.Owner;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(user.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: user.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>(); var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
orgUserRepository orgUserRepository
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(inviteOrganization.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
.Returns([]); .Returns([]);
orgUserRepository orgUserRepository
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner) .GetManyByMinimumRoleAsync(inviteOrganization.OrganizationId, OrganizationUserType.Owner)
.Returns([ownerDetails]); .Returns([ownerDetails]);
sutProvider.GetDependency<IOrganizationRepository>() sutProvider.GetDependency<IOrganizationRepository>()
@ -218,8 +259,8 @@ public class InviteOrganizationUserCommandTests
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto) .Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
.WithPasswordManagerUpdate(new PasswordManagerSubscriptionUpdate(organizationDto, organization.Seats.Value, 1)))); .WithPasswordManagerUpdate(new PasswordManagerSubscriptionUpdate(inviteOrganization, organization.Seats.Value, 1))));
// Act // Act
var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request); var result = await sutProvider.Sut.InviteScimOrganizationUserAsync(request);
@ -227,12 +268,12 @@ public class InviteOrganizationUserCommandTests
// Assert // Assert
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result); Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
Assert.NotNull(organizationDto.MaxAutoScaleSeats); Assert.NotNull(inviteOrganization.MaxAutoScaleSeats);
await sutProvider.GetDependency<IMailService>() await sutProvider.GetDependency<IMailService>()
.Received(1) .Received(1)
.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, .SendOrganizationMaxSeatLimitReachedEmailAsync(organization,
organizationDto.MaxAutoScaleSeats.Value, inviteOrganization.MaxAutoScaleSeats.Value,
Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == ownerDetails.Email))); Arg.Is<IEnumerable<string>>(emails => emails.Any(email => email == ownerDetails.Email)));
} }
@ -253,22 +294,32 @@ public class InviteOrganizationUserCommandTests
organization.MaxAutoscaleSeats = 2; organization.MaxAutoscaleSeats = 2;
ownerDetails.Type = OrganizationUserType.Owner; ownerDetails.Type = OrganizationUserType.Owner;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(user.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: user.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
var passwordManagerUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, organization.Seats.Value, 1); var passwordManagerUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, organization.Seats.Value, 1);
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>(); var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
orgUserRepository orgUserRepository
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(inviteOrganization.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
.Returns([]); .Returns([]);
orgUserRepository orgUserRepository
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner) .GetManyByMinimumRoleAsync(inviteOrganization.OrganizationId, OrganizationUserType.Owner)
.Returns([ownerDetails]); .Returns([ownerDetails]);
var orgRepository = sutProvider.GetDependency<IOrganizationRepository>(); var orgRepository = sutProvider.GetDependency<IOrganizationRepository>();
@ -278,7 +329,7 @@ public class InviteOrganizationUserCommandTests
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto) .Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
.WithPasswordManagerUpdate(passwordManagerUpdate))); .WithPasswordManagerUpdate(passwordManagerUpdate)));
// Act // Act
@ -288,7 +339,7 @@ public class InviteOrganizationUserCommandTests
Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result); Assert.IsType<Success<ScimInviteOrganizationUsersResponse>>(result);
await sutProvider.GetDependency<IPaymentService>() await sutProvider.GetDependency<IPaymentService>()
.AdjustSeatsAsync(organization, organizationDto.Plan, passwordManagerUpdate.SeatsRequiredToAdd); .AdjustSeatsAsync(organization, inviteOrganization.Plan, passwordManagerUpdate.SeatsRequiredToAdd);
await orgRepository.Received(1).ReplaceAsync(Arg.Is<Organization>(x => x.Seats == passwordManagerUpdate.UpdatedSeatTotal)); await orgRepository.Received(1).ReplaceAsync(Arg.Is<Organization>(x => x.Seats == passwordManagerUpdate.UpdatedSeatTotal));
@ -315,15 +366,25 @@ public class InviteOrganizationUserCommandTests
organization.MaxAutoscaleSeats = 2; organization.MaxAutoscaleSeats = 2;
ownerDetails.Type = OrganizationUserType.Owner; ownerDetails.Type = OrganizationUserType.Owner;
var organizationDto = new InviteOrganization(organization, new FreePlan()); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var request = new OrganizationUserSingleEmailInvite(user.Email, var request = new InviteOrganizationUsersRequest(
organizationDto, invites: [
timeProvider.GetUtcNow(), new OrganizationUserInvite(
externalId); email: user.Email,
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: externalId,
accessSecretsManager: true)
],
inviteOrganization: inviteOrganization,
performedBy: Guid.Empty,
timeProvider.GetUtcNow());
var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate( var secretsManagerSubscriptionUpdate = new SecretsManagerSubscriptionUpdate(
organizationDto, inviteOrganization,
organization.SmSeats.Value, organization.SmSeats.Value,
1, 1,
organization.Seats.Value); organization.Seats.Value);
@ -331,10 +392,10 @@ public class InviteOrganizationUserCommandTests
var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>(); var orgUserRepository = sutProvider.GetDependency<IOrganizationUserRepository>();
orgUserRepository orgUserRepository
.SelectKnownEmailsAsync(organizationDto.OrganizationId, Arg.Any<IEnumerable<string>>(), false) .SelectKnownEmailsAsync(inviteOrganization.OrganizationId, Arg.Any<IEnumerable<string>>(), false)
.Returns([]); .Returns([]);
orgUserRepository orgUserRepository
.GetManyByMinimumRoleAsync(organizationDto.OrganizationId, OrganizationUserType.Owner) .GetManyByMinimumRoleAsync(inviteOrganization.OrganizationId, OrganizationUserType.Owner)
.Returns([ownerDetails]); .Returns([ownerDetails]);
var orgRepository = sutProvider.GetDependency<IOrganizationRepository>(); var orgRepository = sutProvider.GetDependency<IOrganizationRepository>();
@ -344,7 +405,7 @@ public class InviteOrganizationUserCommandTests
sutProvider.GetDependency<IInviteUsersValidator>() sutProvider.GetDependency<IInviteUsersValidator>()
.ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>()) .ValidateAsync(Arg.Any<InviteUserOrganizationValidationRequest>())
.Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, organizationDto) .Returns(new Valid<InviteUserOrganizationValidationRequest>(GetInviteValidationRequestMock(request, inviteOrganization)
.WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate))); .WithSecretsManagerUpdate(secretsManagerSubscriptionUpdate)));
// Act // Act

View File

@ -1,6 +1,7 @@
using Bit.Core.AdminConsole.Entities; using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business; using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization; using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.Organization;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation.PasswordManager;
using Bit.Core.AdminConsole.Shared.Validation; using Bit.Core.AdminConsole.Shared.Validation;
using Bit.Core.Billing.Models.StaticStore.Plans; using Bit.Core.Billing.Models.StaticStore.Plans;
using Bit.Test.Common.AutoFixture.Attributes; using Bit.Test.Common.AutoFixture.Attributes;
@ -14,7 +15,11 @@ public class InviteUserOrganizationValidationTests
[BitAutoData] [BitAutoData]
public void Validate_WhenOrganizationIsFreeTier_ShouldReturnValidResponse(Organization organization) public void Validate_WhenOrganizationIsFreeTier_ShouldReturnValidResponse(Organization organization)
{ {
var result = InviteUserOrganizationValidator.Validate(new InviteOrganization(organization, new FreePlan())); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var validSubscriptionUpdate = new Valid<PasswordManagerSubscriptionUpdate>(
new PasswordManagerSubscriptionUpdate(inviteOrganization, 0, 0));
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
Assert.IsType<Valid<InviteOrganization>>(result); Assert.IsType<Valid<InviteOrganization>>(result);
} }
@ -25,8 +30,13 @@ public class InviteUserOrganizationValidationTests
Organization organization) Organization organization)
{ {
organization.GatewayCustomerId = string.Empty; organization.GatewayCustomerId = string.Empty;
organization.Seats = 3;
var result = InviteUserOrganizationValidator.Validate(new InviteOrganization(organization, new FreePlan())); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var validSubscriptionUpdate = new Valid<PasswordManagerSubscriptionUpdate>(
new PasswordManagerSubscriptionUpdate(inviteOrganization, 3, 1));
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
Assert.IsType<Invalid<InviteOrganization>>(result); Assert.IsType<Invalid<InviteOrganization>>(result);
Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString); Assert.Equal(OrganizationNoPaymentMethodFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);
@ -38,8 +48,14 @@ public class InviteUserOrganizationValidationTests
Organization organization) Organization organization)
{ {
organization.GatewaySubscriptionId = string.Empty; organization.GatewaySubscriptionId = string.Empty;
organization.Seats = 3;
organization.MaxAutoscaleSeats = 4;
var result = InviteUserOrganizationValidator.Validate(new InviteOrganization(organization, new FreePlan())); var inviteOrganization = new InviteOrganization(organization, new FreePlan());
var validSubscriptionUpdate = new Valid<PasswordManagerSubscriptionUpdate>(
new PasswordManagerSubscriptionUpdate(inviteOrganization, 3, 1));
var result = InviteUserOrganizationValidator.Validate(inviteOrganization, validSubscriptionUpdate);
Assert.IsType<Invalid<InviteOrganization>>(result); Assert.IsType<Invalid<InviteOrganization>>(result);
Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString); Assert.Equal(OrganizationNoSubscriptionFoundError.Code, (result as Invalid<InviteOrganization>)!.ErrorMessageString);

View File

@ -28,7 +28,7 @@ public class SecretsManagerInviteUserValidationTests
var request = new InviteUserOrganizationValidationRequest var request = new InviteUserOrganizationValidationRequest
{ {
Invites = [new OrganizationUserInviteDto()], Invites = [new OrganizationUserInvite("test@email.com", "", false)],
InviteOrganization = organizationDto, InviteOrganization = organizationDto,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = default, PerformedAt = default,
@ -53,11 +53,18 @@ public class SecretsManagerInviteUserValidationTests
var organizationDto = new InviteOrganization(organization, new FreePlan()); var organizationDto = new InviteOrganization(organization, new FreePlan());
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, 0, 0); var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, 0, 0);
var invite = OrganizationUserInvite.Create(["email@test.com"], [], OrganizationUserType.User, new Permissions(), string.Empty, true); var invite = new OrganizationUserInvite(
email: "email@test.com",
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: string.Empty,
accessSecretsManager: true);
var request = new InviteUserOrganizationValidationRequest var request = new InviteUserOrganizationValidationRequest
{ {
Invites = [OrganizationUserInviteDto.Create(invite.Emails.First(), invite, organizationDto.OrganizationId)], Invites = [invite],
InviteOrganization = organizationDto, InviteOrganization = organizationDto,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = default, PerformedAt = default,
@ -86,7 +93,14 @@ public class SecretsManagerInviteUserValidationTests
var request = new InviteUserOrganizationValidationRequest var request = new InviteUserOrganizationValidationRequest
{ {
Invites = [new OrganizationUserInviteDto()], Invites = [new OrganizationUserInvite(
email: "email@test.com",
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: string.Empty,
accessSecretsManager: true)],
InviteOrganization = organizationDto, InviteOrganization = organizationDto,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = default, PerformedAt = default,
@ -116,7 +130,14 @@ public class SecretsManagerInviteUserValidationTests
var request = new InviteUserOrganizationValidationRequest var request = new InviteUserOrganizationValidationRequest
{ {
Invites = [OrganizationUserInviteDto.Create("email@test.com", OrganizationUserInvite.Create(["email@test.com"], [], OrganizationUserType.User, new Permissions(), string.Empty, true), organization.Id)], Invites = [new OrganizationUserInvite(
email: "email@test.com",
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: string.Empty,
accessSecretsManager: true)],
InviteOrganization = organizationDto, InviteOrganization = organizationDto,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = default, PerformedAt = default,
@ -137,18 +158,26 @@ public class SecretsManagerInviteUserValidationTests
public void Validate_GivenPasswordManagerSeatsAreTheSameAsSecretsManagerSeats_WhenAttemptingToAddASecretManagerSeatOnly_ThenShouldNotBeAllowedToAddSecretsManagerUsers( public void Validate_GivenPasswordManagerSeatsAreTheSameAsSecretsManagerSeats_WhenAttemptingToAddASecretManagerSeatOnly_ThenShouldNotBeAllowedToAddSecretsManagerUsers(
Organization organization) Organization organization)
{ {
organization.Seats = 0;
organization.SmSeats = 4; organization.SmSeats = 4;
organization.MaxAutoscaleSmSeats = 5; organization.MaxAutoscaleSmSeats = 5;
organization.UseSecretsManager = true; organization.UseSecretsManager = true;
organization.PlanType = PlanType.EnterpriseAnnually; organization.PlanType = PlanType.EnterpriseAnnually;
var organizationDto = new InviteOrganization(organization, new Enterprise2023Plan(isAnnual: true)); var inviteOrganization = new InviteOrganization(organization, new Enterprise2023Plan(isAnnual: true));
var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(organizationDto, 0, 0); var subscriptionUpdate = new PasswordManagerSubscriptionUpdate(inviteOrganization, 0, 0);
var request = new InviteUserOrganizationValidationRequest var request = new InviteUserOrganizationValidationRequest
{ {
Invites = [OrganizationUserInviteDto.Create("email@test.com", OrganizationUserInvite.Create(["email@test.com"], [], OrganizationUserType.User, new Permissions(), string.Empty, true), organization.Id)], Invites = [new OrganizationUserInvite(
InviteOrganization = organizationDto, email: "email@test.com",
assignedCollections: [],
groups: [],
type: OrganizationUserType.User,
permissions: new Permissions(),
externalId: string.Empty,
accessSecretsManager: true)],
InviteOrganization = inviteOrganization,
PerformedBy = Guid.Empty, PerformedBy = Guid.Empty,
PerformedAt = default, PerformedAt = default,
OccupiedPmSeats = 0, OccupiedPmSeats = 0,