1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-09 21:52:17 -05:00

rename OrganizatinUserInvite for command, implement command

This commit is contained in:
Brandon 2025-05-08 13:26:52 -04:00
parent 1f5ea44a07
commit f47a65cfad
No known key found for this signature in database
GPG Key ID: A0E0EF0B207BA40D
10 changed files with 48 additions and 63 deletions

View File

@ -4,7 +4,7 @@ using Bit.Api.Models.Public.Response;
using Bit.Core.Context;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Services;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@ -15,18 +15,18 @@ namespace Bit.Api.AdminConsole.Public.Controllers;
[Authorize("Organization")]
public class OrganizationController : Controller
{
private readonly IOrganizationService _organizationService;
private readonly ICurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
private readonly IImportOrganizationUserCommand _importOrganizationUserCommand;
public OrganizationController(
IOrganizationService organizationService,
ICurrentContext currentContext,
GlobalSettings globalSettings)
GlobalSettings globalSettings,
IImportOrganizationUserCommand importOrganizationUserCommand)
{
_organizationService = organizationService;
_currentContext = currentContext;
_globalSettings = globalSettings;
_importOrganizationUserCommand = importOrganizationUserCommand;
}
/// <summary>
@ -47,7 +47,7 @@ public class OrganizationController : Controller
throw new BadRequestException("You cannot import this much data at once.");
}
await _organizationService.ImportAsync(
await _importOrganizationUserCommand.ImportAsync(
_currentContext.OrganizationId.Value,
model.Groups.Select(g => g.ToImportedGroup(_currentContext.OrganizationId.Value)),
model.Members.Where(u => !u.Deleted).Select(u => u.ToImportedOrganizationUser()),

View File

@ -1,12 +1,15 @@
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.Billing.Pricing;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.Models.Commands;
using Bit.Core.Models.Data.Organizations.OrganizationUsers;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Repositories;
@ -14,7 +17,6 @@ using Bit.Core.Services;
using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using OrganizationUserInvite = Bit.Core.Models.Business.OrganizationUserInvite;
public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
{
@ -26,6 +28,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
private readonly IReferenceEventService _referenceEventService;
private readonly ICurrentContext _currentContext;
private readonly IOrganizationService _organizationService;
private readonly IInviteOrganizationUsersCommand _inviteOrganizationUsersCommand;
private readonly IPricingClient _pricingClient;
public ImportOrganizationUserCommand(IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
@ -34,7 +38,9 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
IEventService eventService,
IReferenceEventService referenceEventService,
ICurrentContext currentContext,
IOrganizationService organizationService
IOrganizationService organizationService,
IInviteOrganizationUsersCommand inviteOrganizationUsersCommand,
IPricingClient pricingClient
)
{
_organizationRepository = organizationRepository;
@ -45,6 +51,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
_referenceEventService = referenceEventService;
_currentContext = currentContext;
_organizationService = organizationService;
_inviteOrganizationUsersCommand = inviteOrganizationUsersCommand;
_pricingClient = pricingClient;
}
public async Task ImportAsync(Guid organizationId,
@ -187,7 +195,7 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
var hasStandaloneSecretsManager = await _paymentService.HasSecretsManagerStandalone(organization);
var userInvites = new List<(OrganizationUserInvite, string)>();
var userInvites = new List<OrganizationUserInviteCommandModel>();
foreach (var user in newUsers)
{
if (!usersToAdd.Contains(user.ExternalId) || string.IsNullOrWhiteSpace(user.Email))
@ -197,14 +205,8 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
try
{
var invite = new OrganizationUserInvite
{
Emails = new List<string> { user.Email },
Type = OrganizationUserType.User,
Collections = new List<CollectionAccessSelection>(),
AccessSecretsManager = hasStandaloneSecretsManager
};
userInvites.Add((invite, user.ExternalId));
var invite = new OrganizationUserInviteCommandModel(user.Email, user.ExternalId);
userInvites.Add(new OrganizationUserInviteCommandModel(invite, hasStandaloneSecretsManager));
}
catch (BadRequestException)
{
@ -213,12 +215,23 @@ public class ImportOrganizationUserCommand : IImportOrganizationUserCommand
}
}
//@TODO: replace with command
var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites);
foreach (var invitedUser in invitedUsers)
{
importData.ExistingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id);
}
var commandResult = await InviteUsersAsync(userInvites, organization);
//@TODO: replace with command, add invited users from commandResult to importData
//var invitedUsers = await _organizationService.InviteUsersAsync(organization.Id, invitingUserId: null, systemUser: eventSystemUser, userInvites);
//foreach (var invitedUser in invitedUsers)
//{
// importData.ExistingExternalUsersIdDict.Add(invitedUser.ExternalId, invitedUser.Id);
//}
}
private async Task<CommandResult<ScimInviteOrganizationUsersResponse>> InviteUsersAsync(List<OrganizationUserInviteCommandModel> invites, Organization organization)
{
var plan = await _pricingClient.GetPlanOrThrow(organization.PlanType);
var inviteOrganization = new InviteOrganization(organization, plan);
var request = new InviteOrganizationUsersRequest(invites.ToArray(), inviteOrganization, Guid.Empty, DateTimeOffset.UtcNow);
return await _inviteOrganizationUsersCommand.InviteScimOrganizationUserAsync(request);
}
private async Task OverwriteExisting(

View File

@ -19,7 +19,6 @@ using Bit.Core.Tools.Enums;
using Bit.Core.Tools.Models.Business;
using Bit.Core.Tools.Services;
using Microsoft.Extensions.Logging;
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@ -146,7 +145,7 @@ public class InviteOrganizationUsersCommand(IEventService eventService,
organizationId: organization!.Id));
}
private async Task<IEnumerable<OrganizationUserInvite>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
private async Task<IEnumerable<OrganizationUserInviteCommandModel>> FilterExistingUsersAsync(InviteOrganizationUsersRequest request)
{
var existingEmails = new HashSet<string>(await organizationUserRepository.SelectKnownEmailsAsync(
request.InviteOrganization.OrganizationId, request.Invites.Select(i => i.Email), false),

View File

@ -7,7 +7,7 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public static class CreateOrganizationUserExtensions
{
public static CreateOrganizationUser MapToDataModel(this OrganizationUserInvite organizationUserInvite,
public static CreateOrganizationUser MapToDataModel(this OrganizationUserInviteCommandModel organizationUserInvite,
DateTimeOffset performedAt,
InviteOrganization organization) =>
new()

View File

@ -4,12 +4,12 @@ namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUse
public class InviteOrganizationUsersRequest
{
public OrganizationUserInvite[] Invites { get; } = [];
public OrganizationUserInviteCommandModel[] Invites { get; } = [];
public InviteOrganization InviteOrganization { get; }
public Guid PerformedBy { get; }
public DateTimeOffset PerformedAt { get; }
public InviteOrganizationUsersRequest(OrganizationUserInvite[] invites,
public InviteOrganizationUsersRequest(OrganizationUserInviteCommandModel[] invites,
InviteOrganization inviteOrganization,
Guid performedBy,
DateTimeOffset performedAt)

View File

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

View File

@ -7,7 +7,7 @@ using static Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Invite
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models;
public class OrganizationUserInvite
public class OrganizationUserInviteCommandModel
{
public string Email { get; private init; }
public CollectionAccessSelection[] AssignedCollections { get; private init; }
@ -17,7 +17,7 @@ public class OrganizationUserInvite
public bool AccessSecretsManager { get; private init; }
public Guid[] Groups { get; private init; }
public OrganizationUserInvite(string email, string externalId) :
public OrganizationUserInviteCommandModel(string email, string externalId) :
this(
email: email,
assignedCollections: [],
@ -29,7 +29,7 @@ public class OrganizationUserInvite
{
}
public OrganizationUserInvite(OrganizationUserInvite invite, bool accessSecretsManager) :
public OrganizationUserInviteCommandModel(OrganizationUserInviteCommandModel invite, bool accessSecretsManager) :
this(invite.Email,
invite.AssignedCollections,
invite.Groups,
@ -41,7 +41,7 @@ public class OrganizationUserInvite
}
public OrganizationUserInvite(string email,
public OrganizationUserInviteCommandModel(string email,
IEnumerable<CollectionAccessSelection> assignedCollections,
IEnumerable<Guid> groups,
OrganizationUserType type,

View File

@ -6,7 +6,6 @@ using Bit.Core.Models.Business;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.Repositories;
using Bit.Core.Services;
using OrganizationUserInvite = Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Models.OrganizationUserInvite;
namespace Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers.Validation;
@ -38,7 +37,7 @@ public class InviteOrganizationUsersValidator(
request = new InviteOrganizationUsersValidationRequest(request)
{
Invites = request.Invites
.Select(x => new OrganizationUserInvite(x, accessSecretsManager: true))
.Select(x => new OrganizationUserInviteCommandModel(x, accessSecretsManager: true))
.ToArray()
};
}

View File

@ -1,6 +1,5 @@
using System.Security.Claims;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.Auth.Enums;
using Bit.Core.Entities;
using Bit.Core.Enums;
@ -39,9 +38,6 @@ public interface IOrganizationService
Task<IEnumerable<Tuple<OrganizationUser, string>>> ResendInvitesAsync(Guid organizationId, Guid? invitingUserId, IEnumerable<Guid> organizationUsersId);
Task ResendInviteAsync(Guid organizationId, Guid? invitingUserId, Guid organizationUserId, bool initOrganization = false);
Task UpdateUserResetPasswordEnrollmentAsync(Guid organizationId, Guid userId, string resetPasswordKey, Guid? callingUserId);
Task ImportAsync(Guid organizationId, IEnumerable<ImportedGroup> groups,
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
bool overwriteExisting, EventSystemUser eventSystemUser);
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
Task RevokeUserAsync(OrganizationUser organizationUser, Guid? revokingUserId);
Task RevokeUserAsync(OrganizationUser organizationUser, EventSystemUser systemUser);

View File

@ -3,7 +3,6 @@ using System.Text.Json;
using Bit.Core.AdminConsole.Entities;
using Bit.Core.AdminConsole.Enums;
using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Models.Business;
using Bit.Core.AdminConsole.Models.Data.Organizations.Policies;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.AdminConsole.OrganizationFeatures.OrganizationUsers.InviteUsers;
@ -26,7 +25,6 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Business;
using Bit.Core.Models.Data;
using Bit.Core.OrganizationFeatures.OrganizationSubscriptions.Interface;
using Bit.Core.OrganizationFeatures.OrganizationUsers.Interfaces;
using Bit.Core.Platform.Push;
using Bit.Core.Repositories;
using Bit.Core.Settings;
@ -74,7 +72,6 @@ public class OrganizationService : IOrganizationService
private readonly IPricingClient _pricingClient;
private readonly IPolicyRequirementQuery _policyRequirementQuery;
private readonly ISendOrganizationInvitesCommand _sendOrganizationInvitesCommand;
private readonly IImportOrganizationUserCommand _importOrganizationUserCommand;
public OrganizationService(
IOrganizationRepository organizationRepository,
@ -108,8 +105,7 @@ public class OrganizationService : IOrganizationService
IHasConfirmedOwnersExceptQuery hasConfirmedOwnersExceptQuery,
IPricingClient pricingClient,
IPolicyRequirementQuery policyRequirementQuery,
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand,
IImportOrganizationUserCommand importOrganizationUserCommand
ISendOrganizationInvitesCommand sendOrganizationInvitesCommand
)
{
_organizationRepository = organizationRepository;
@ -144,7 +140,6 @@ public class OrganizationService : IOrganizationService
_pricingClient = pricingClient;
_policyRequirementQuery = policyRequirementQuery;
_sendOrganizationInvitesCommand = sendOrganizationInvitesCommand;
_importOrganizationUserCommand = importOrganizationUserCommand;
}
public async Task ReplacePaymentMethodAsync(Guid organizationId, string paymentToken,
@ -1203,23 +1198,6 @@ public class OrganizationService : IOrganizationService
EventType.OrganizationUser_ResetPassword_Enroll : EventType.OrganizationUser_ResetPassword_Withdraw);
}
public async Task ImportAsync(Guid organizationId,
IEnumerable<ImportedGroup> groups,
IEnumerable<ImportedOrganizationUser> newUsers,
IEnumerable<string> removeUserExternalIds,
bool overwriteExisting,
EventSystemUser eventSystemUser
)
{
// @TODO DEVELOPMENT FLAG FOR TESTING ---- REVERT THIS LATER
await _importOrganizationUserCommand.ImportAsync(organizationId,
groups,
newUsers,
removeUserExternalIds,
overwriteExisting,
eventSystemUser);
}
public async Task DeleteSsoUserAsync(Guid userId, Guid? organizationId)
{
await _ssoUserRepository.DeleteAsync(userId, organizationId);