1
0
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:
Matt Gibson
2021-05-17 09:43:02 -05:00
committed by GitHub
parent 738a4c2bac
commit 785e788cb6
64 changed files with 1704 additions and 234 deletions

View File

@ -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 });