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

Refactor validation parameter to improve clarity and consistency. Added XML doc

This commit is contained in:
jrmccannon 2025-03-21 15:18:05 -05:00
parent e1eb458036
commit 87c181b662
No known key found for this signature in database
GPG Key ID: CF03F3DB01CE96A6
3 changed files with 46 additions and 32 deletions

View File

@ -3,7 +3,21 @@ using Bit.Core.Models.Commands;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers; namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
/// <summary>
/// Defines the contract for inviting organization users via SCIM (System for Cross-domain Identity Management).
/// Provides functionality for handling single email invitation requests within an organization context.
/// </summary>
public interface IInviteOrganizationUsersCommand public interface IInviteOrganizationUsersCommand
{ {
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(OrganizationUserSingleEmailInvite request); /// <summary>
/// Sends an invitation to add an organization user via SCIM (System for Cross-domain Identity Management) system.
/// This can be a Success or a Failure. Failure will contain the Error along with a representation of the errored value.
/// Success will be the successful return object.
/// </summary>
/// <param name="request">
/// Contains the details for inviting a single organization user via email.
/// </param>
/// <returns>Response from InviteScimOrganiation<see cref="ScimInviteOrganizationUsersResponse"/></returns>
Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteScimOrganizationUserAsync(
OrganizationUserSingleEmailInvite request);
} }

View File

@ -139,42 +139,42 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
return new Success<IEnumerable<OrganizationUser>>(organizationUserCollection.Select(x => x.OrganizationUser)); return new Success<IEnumerable<OrganizationUser>>(organizationUserCollection.Select(x => x.OrganizationUser));
} }
private async Task RevertPasswordManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task RevertPasswordManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (valid.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0) if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd > 0)
{ {
// When reverting seats, we have to tell payments service that the seats are going back down by what we attempted to add. // When reverting seats, we have to tell payments service that the seats are going back down by what we attempted to add.
// However, this might lead to a problem if we don't actually update stripe but throw any ways. // However, this might lead to a problem if we don't actually update stripe but throw any ways.
// stripe could not be updated, and then we would decrement the number of seats in stripe accidentally. // stripe could not be updated, and then we would decrement the number of seats in stripe accidentally.
var seatsToRemove = -valid.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd; var seatsToRemove = -validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd;
await paymentService.AdjustSeatsAsync(organization, valid.Value.InviteOrganization.Plan, -seatsToRemove); await paymentService.AdjustSeatsAsync(organization, validatedResult.Value.InviteOrganization.Plan, -seatsToRemove);
organization.Seats = (short?)valid.Value.PasswordManagerSubscriptionUpdate.Seats; organization.Seats = (short?)validatedResult.Value.PasswordManagerSubscriptionUpdate.Seats;
await organizationRepository.ReplaceAsync(organization); await organizationRepository.ReplaceAsync(organization);
await applicationCacheService.UpsertOrganizationAbilityAsync(organization); await applicationCacheService.UpsertOrganizationAbilityAsync(organization);
} }
} }
private async Task RevertSecretsManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task RevertSecretsManagerChangesAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (valid.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd < 0) if (validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd < 0)
{ {
var updateRevert = new SecretsManagerSubscriptionUpdate(organization, valid.Value.InviteOrganization.Plan, false) var updateRevert = new SecretsManagerSubscriptionUpdate(organization, validatedResult.Value.InviteOrganization.Plan, false)
{ {
SmSeats = valid.Value.SecretsManagerSubscriptionUpdate.Seats SmSeats = validatedResult.Value.SecretsManagerSubscriptionUpdate.Seats
}; };
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(updateRevert); await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(updateRevert);
} }
} }
private async Task PublishReferenceEventAsync(Valid<InviteUserOrganizationValidationRequest> valid, private async Task PublishReferenceEventAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult,
Organization organization) => Organization organization) =>
await referenceEventService.RaiseEventAsync( await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, currentContext) new ReferenceEvent(ReferenceEventType.InvitedUsers, organization, currentContext)
{ {
Users = valid.Value.Invites.Length Users = validatedResult.Value.Invites.Length
}); });
private async Task SendInvitesAsync(IEnumerable<CreateOrganizationUser> users, Organization organization) => private async Task SendInvitesAsync(IEnumerable<CreateOrganizationUser> users, Organization organization) =>
@ -183,14 +183,14 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
users.Select(x => x.OrganizationUser), users.Select(x => x.OrganizationUser),
organization)); organization));
private async Task SendAdditionalEmailsAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task SendAdditionalEmailsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
await SendPasswordManagerMaxSeatLimitEmailsAsync(valid, organization); await SendPasswordManagerMaxSeatLimitEmailsAsync(validatedResult, organization);
} }
private async Task SendPasswordManagerMaxSeatLimitEmailsAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task SendPasswordManagerMaxSeatLimitEmailsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (!valid.Value.PasswordManagerSubscriptionUpdate.MaxSeatsReached) if (!validatedResult.Value.PasswordManagerSubscriptionUpdate.MaxSeatsReached)
{ {
return; return;
} }
@ -198,12 +198,12 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
try try
{ {
var ownerEmails = (await organizationUserRepository var ownerEmails = (await organizationUserRepository
.GetManyByMinimumRoleAsync(valid.Value.InviteOrganization.OrganizationId, OrganizationUserType.Owner)) .GetManyByMinimumRoleAsync(validatedResult.Value.InviteOrganization.OrganizationId, OrganizationUserType.Owner))
.Select(x => x.Email) .Select(x => x.Email)
.Distinct(); .Distinct();
await mailService.SendOrganizationMaxSeatLimitReachedEmailAsync(organization, await mailService.SendOrganizationMaxSeatLimitReachedEmailAsync(organization,
valid.Value.PasswordManagerSubscriptionUpdate.MaxAutoScaleSeats!.Value, ownerEmails); validatedResult.Value.PasswordManagerSubscriptionUpdate.MaxAutoScaleSeats!.Value, ownerEmails);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -211,29 +211,29 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
} }
} }
private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task AdjustSecretsManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (valid.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0) if (validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0)
{ {
return; return;
} }
var subscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, valid.Value.InviteOrganization.Plan, true) var subscriptionUpdate = new SecretsManagerSubscriptionUpdate(organization, validatedResult.Value.InviteOrganization.Plan, true)
.AdjustSeats(valid.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd); .AdjustSeats(validatedResult.Value.SecretsManagerSubscriptionUpdate.SeatsRequiredToAdd);
await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(subscriptionUpdate); await updateSecretsManagerSubscriptionCommand.UpdateSubscriptionAsync(subscriptionUpdate);
} }
private async Task AdjustPasswordManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> valid, Organization organization) private async Task AdjustPasswordManagerSeatsAsync(Valid<InviteUserOrganizationValidationRequest> validatedResult, Organization organization)
{ {
if (valid.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0) if (validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd <= 0)
{ {
return; return;
} }
await paymentService.AdjustSeatsAsync(organization, valid.Value.InviteOrganization.Plan, valid.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd); await paymentService.AdjustSeatsAsync(organization, validatedResult.Value.InviteOrganization.Plan, validatedResult.Value.PasswordManagerSubscriptionUpdate.SeatsRequiredToAdd);
organization.Seats = (short?)valid.Value.PasswordManagerSubscriptionUpdate.UpdatedSeatTotal; organization.Seats = (short?)validatedResult.Value.PasswordManagerSubscriptionUpdate.UpdatedSeatTotal;
await organizationRepository.ReplaceAsync(organization); // could optimize this with only a property update await organizationRepository.ReplaceAsync(organization); // could optimize this with only a property update
await applicationCacheService.UpsertOrganizationAbilityAsync(organization); await applicationCacheService.UpsertOrganizationAbilityAsync(organization);
@ -241,10 +241,10 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
await referenceEventService.RaiseEventAsync( await referenceEventService.RaiseEventAsync(
new ReferenceEvent(ReferenceEventType.AdjustSeats, organization, currentContext) new ReferenceEvent(ReferenceEventType.AdjustSeats, organization, currentContext)
{ {
PlanName = valid.Value.InviteOrganization.Plan.Name, PlanName = validatedResult.Value.InviteOrganization.Plan.Name,
PlanType = valid.Value.InviteOrganization.Plan.Type, PlanType = validatedResult.Value.InviteOrganization.Plan.Type,
Seats = valid.Value.PasswordManagerSubscriptionUpdate.UpdatedSeatTotal, Seats = validatedResult.Value.PasswordManagerSubscriptionUpdate.UpdatedSeatTotal,
PreviousSeats = valid.Value.PasswordManagerSubscriptionUpdate.Seats PreviousSeats = validatedResult.Value.PasswordManagerSubscriptionUpdate.Seats
}); });
} }
} }

View File

@ -61,7 +61,7 @@ public class PasswordManagerInviteUserValidationTests
var result = PasswordManagerInviteUserValidation.Validate(subscriptionUpdate); var result = PasswordManagerInviteUserValidation.Validate(subscriptionUpdate);
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result); Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>).ErrorMessageString); Assert.Equal(PasswordManagerSeatLimitHasBeenReachedError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
} }
[Theory] [Theory]
@ -80,6 +80,6 @@ public class PasswordManagerInviteUserValidationTests
var result = PasswordManagerInviteUserValidation.Validate(subscriptionUpdate); var result = PasswordManagerInviteUserValidation.Validate(subscriptionUpdate);
Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result); Assert.IsType<Invalid<PasswordManagerSubscriptionUpdate>>(result);
Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>).ErrorMessageString); Assert.Equal(PasswordManagerPlanDoesNotAllowAdditionalSeatsError.Code, (result as Invalid<PasswordManagerSubscriptionUpdate>)!.ErrorMessageString);
} }
} }