mirror of
https://github.com/bitwarden/server.git
synced 2025-07-02 08:32:50 -05:00
Support large organization sync (#1311)
* Increase organization max seat size from 30k to 2b (#1274) * Increase organization max seat size from 30k to 2b * PR review. Do not modify unless state matches expected * Organization sync simultaneous event reporting (#1275) * Split up azure messages according to max size * Allow simultaneous login of organization user events * Early resolve small event lists * Clarify logic Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Improve readability This comes at the cost of multiple serializations, but the improvement in wire-time should more than make up for this on message where serialization time matters Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com> * Queue emails (#1286) * Extract common Azure queue methods * Do not use internal entity framework namespace * Prefer IEnumerable to IList unless needed All of these implementations were just using `Count == 1`, which is easily replicated. This will be used when abstracting Azure queues * Add model for azure queue message * Abstract Azure queue for reuse * Creat service to enqueue mail messages for later processing Azure queue mail service uses Azure queues. Blocking just blocks until all the work is done -- This is how emailing works today * Provide mail queue service to DI * Queue organization invite emails for later processing All emails can later be added to this queue * Create Admin hosted service to process enqueued mail messages * Prefer constructors to static generators * Mass delete organization users (#1287) * Add delete many to Organization Users * Correct formatting * Remove erroneous migration * Clarify parameter name * Formatting fixes * Simplify bump account revision sproc * Formatting fixes * Match file names to objects * Indicate if large import is expected * Early pull all existing users we were planning on inviting (#1290) * Early pull all existing users we were planning on inviting * Improve sproc name * Batch upsert org users (#1289) * Add UpsertMany sprocs to OrganizationUser * Add method to create TVPs from any object. Uses DbOrder attribute to generate. Sproc will fail unless TVP column order matches that of the db type * Combine migrations * Correct formatting * Include sql objects in sql project * Keep consisten parameter names * Batch deletes for performance * Correct formatting * consolidate migrations * Use batch methods in OrganizationImport * Declare @BatchSize * Transaction names limited to 32 chars Drop sproc before creating it if it exists * Update import tests * Allow for more users in org upgrades * Fix formatting * Improve class hierarchy structure * Use name tuple types * Fix formatting * Front load all reflection * Format constructor * Simplify ToTvp as class-specific extension Co-authored-by: Chad Scharf <3904944+cscharf@users.noreply.github.com>
This commit is contained in:
@ -19,6 +19,7 @@ namespace Bit.Core.Services
|
||||
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IMailDeliveryService _mailDeliveryService;
|
||||
private readonly IMailEnqueuingService _mailEnqueuingService;
|
||||
private readonly Dictionary<string, Func<object, string>> _templateCache =
|
||||
new Dictionary<string, Func<object, string>>();
|
||||
|
||||
@ -26,10 +27,12 @@ namespace Bit.Core.Services
|
||||
|
||||
public HandlebarsMailService(
|
||||
GlobalSettings globalSettings,
|
||||
IMailDeliveryService mailDeliveryService)
|
||||
IMailDeliveryService mailDeliveryService,
|
||||
IMailEnqueuingService mailEnqueuingService)
|
||||
{
|
||||
_globalSettings = globalSettings;
|
||||
_mailDeliveryService = mailDeliveryService;
|
||||
_mailEnqueuingService = mailEnqueuingService;
|
||||
}
|
||||
|
||||
public async Task SendVerifyEmailEmailAsync(string email, Guid userId, string token)
|
||||
@ -168,23 +171,32 @@ namespace Bit.Core.Services
|
||||
await _mailDeliveryService.SendEmailAsync(message);
|
||||
}
|
||||
|
||||
public async Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token)
|
||||
public Task SendOrganizationInviteEmailAsync(string organizationName, OrganizationUser orgUser, string token) =>
|
||||
BulkSendOrganizationInviteEmailAsync(organizationName, new[] { (orgUser, token) });
|
||||
|
||||
public async Task BulkSendOrganizationInviteEmailAsync(string organizationName, IEnumerable<(OrganizationUser orgUser, string token)> invites)
|
||||
{
|
||||
var message = CreateDefaultMessage($"Join {organizationName}", orgUser.Email);
|
||||
var model = new OrganizationUserInvitedViewModel
|
||||
MailQueueMessage CreateMessage(string email, object model)
|
||||
{
|
||||
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName),
|
||||
Email = WebUtility.UrlEncode(orgUser.Email),
|
||||
OrganizationId = orgUser.OrganizationId.ToString(),
|
||||
OrganizationUserId = orgUser.Id.ToString(),
|
||||
Token = WebUtility.UrlEncode(token),
|
||||
OrganizationNameUrlEncoded = WebUtility.UrlEncode(organizationName),
|
||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||
SiteName = _globalSettings.SiteName
|
||||
};
|
||||
await AddMessageContentAsync(message, "OrganizationUserInvited", model);
|
||||
message.Category = "OrganizationUserInvited";
|
||||
await _mailDeliveryService.SendEmailAsync(message);
|
||||
var message = CreateDefaultMessage($"Join {organizationName}", email);
|
||||
return new MailQueueMessage(message, "OrganizationUserInvited", model);
|
||||
}
|
||||
|
||||
var messageModels = invites.Select(invite => CreateMessage(invite.orgUser.Email,
|
||||
new OrganizationUserInvitedViewModel
|
||||
{
|
||||
OrganizationName = CoreHelpers.SanitizeForEmail(organizationName),
|
||||
Email = WebUtility.UrlEncode(invite.orgUser.Email),
|
||||
OrganizationId = invite.orgUser.OrganizationId.ToString(),
|
||||
OrganizationUserId = invite.orgUser.Id.ToString(),
|
||||
Token = WebUtility.UrlEncode(invite.token),
|
||||
OrganizationNameUrlEncoded = WebUtility.UrlEncode(organizationName),
|
||||
WebVaultUrl = _globalSettings.BaseServiceUri.VaultWithHash,
|
||||
SiteName = _globalSettings.SiteName,
|
||||
}
|
||||
));
|
||||
|
||||
await EnqueueMailAsync(messageModels);
|
||||
}
|
||||
|
||||
public async Task SendOrganizationUserRemovedForPolicyTwoStepEmailAsync(string organizationName, string email)
|
||||
@ -341,6 +353,21 @@ namespace Bit.Core.Services
|
||||
await _mailDeliveryService.SendEmailAsync(message);
|
||||
}
|
||||
|
||||
public async Task SendEnqueuedMailMessageAsync(IMailQueueMessage queueMessage)
|
||||
{
|
||||
var message = CreateDefaultMessage(queueMessage.Subject, queueMessage.ToEmails);
|
||||
message.BccEmails = queueMessage.BccEmails;
|
||||
message.Category = queueMessage.Category;
|
||||
await AddMessageContentAsync(message, queueMessage.TemplateName, queueMessage.Model);
|
||||
await _mailDeliveryService.SendEmailAsync(message);
|
||||
}
|
||||
|
||||
private Task EnqueueMailAsync(IMailQueueMessage queueMessage) =>
|
||||
_mailEnqueuingService.EnqueueAsync(queueMessage, SendEnqueuedMailMessageAsync);
|
||||
|
||||
private Task EnqueueMailAsync(IEnumerable<IMailQueueMessage> queueMessages) =>
|
||||
_mailEnqueuingService.EnqueueManyAsync(queueMessages, SendEnqueuedMailMessageAsync);
|
||||
|
||||
private MailMessage CreateDefaultMessage(string subject, string toEmail)
|
||||
{
|
||||
return CreateDefaultMessage(subject, new List<string> { toEmail });
|
||||
|
Reference in New Issue
Block a user