mirror of
https://github.com/bitwarden/server.git
synced 2026-01-31 14:13:18 +08:00
* 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>
232 lines
8.6 KiB
C#
232 lines
8.6 KiB
C#
using System.Threading.Tasks;
|
|
using System;
|
|
using Bit.Core.Enums;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.Models.Data;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using Bit.Core.Models.Table;
|
|
using Bit.Core.Context;
|
|
using Bit.Core.Settings;
|
|
|
|
namespace Bit.Core.Services
|
|
{
|
|
public class EventService : IEventService
|
|
{
|
|
private readonly IEventWriteService _eventWriteService;
|
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
|
private readonly IApplicationCacheService _applicationCacheService;
|
|
private readonly ICurrentContext _currentContext;
|
|
private readonly GlobalSettings _globalSettings;
|
|
|
|
public EventService(
|
|
IEventWriteService eventWriteService,
|
|
IOrganizationUserRepository organizationUserRepository,
|
|
IApplicationCacheService applicationCacheService,
|
|
ICurrentContext currentContext,
|
|
GlobalSettings globalSettings)
|
|
{
|
|
_eventWriteService = eventWriteService;
|
|
_organizationUserRepository = organizationUserRepository;
|
|
_applicationCacheService = applicationCacheService;
|
|
_currentContext = currentContext;
|
|
_globalSettings = globalSettings;
|
|
}
|
|
|
|
public async Task LogUserEventAsync(Guid userId, EventType type, DateTime? date = null)
|
|
{
|
|
var events = new List<IEvent>
|
|
{
|
|
new EventMessage(_currentContext)
|
|
{
|
|
UserId = userId,
|
|
ActingUserId = userId,
|
|
Type = type,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
}
|
|
};
|
|
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, userId);
|
|
var orgEvents = orgs.Where(o => CanUseEvents(orgAbilities, o.Id))
|
|
.Select(o => new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = o.Id,
|
|
UserId = userId,
|
|
ActingUserId = userId,
|
|
Type = type,
|
|
Date = DateTime.UtcNow
|
|
});
|
|
|
|
if (orgEvents.Any())
|
|
{
|
|
events.AddRange(orgEvents);
|
|
await _eventWriteService.CreateManyAsync(events);
|
|
}
|
|
else
|
|
{
|
|
await _eventWriteService.CreateAsync(events.First());
|
|
}
|
|
}
|
|
|
|
public async Task LogCipherEventAsync(Cipher cipher, EventType type, DateTime? date = null)
|
|
{
|
|
var e = await BuildCipherEventMessageAsync(cipher, type, date);
|
|
if (e != null)
|
|
{
|
|
await _eventWriteService.CreateAsync(e);
|
|
}
|
|
}
|
|
|
|
public async Task LogCipherEventsAsync(IEnumerable<Tuple<Cipher, EventType, DateTime?>> events)
|
|
{
|
|
var cipherEvents = new List<IEvent>();
|
|
foreach (var ev in events)
|
|
{
|
|
var e = await BuildCipherEventMessageAsync(ev.Item1, ev.Item2, ev.Item3);
|
|
if (e != null)
|
|
{
|
|
cipherEvents.Add(e);
|
|
}
|
|
}
|
|
await _eventWriteService.CreateManyAsync(cipherEvents);
|
|
}
|
|
|
|
private async Task<EventMessage> BuildCipherEventMessageAsync(Cipher cipher, EventType type, DateTime? date = null)
|
|
{
|
|
// Only logging organization cipher events for now.
|
|
if (!cipher.OrganizationId.HasValue || (!_currentContext?.UserId.HasValue ?? true))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (cipher.OrganizationId.HasValue)
|
|
{
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
if (!CanUseEvents(orgAbilities, cipher.OrganizationId.Value))
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = cipher.OrganizationId,
|
|
UserId = cipher.OrganizationId.HasValue ? null : cipher.UserId,
|
|
CipherId = cipher.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
};
|
|
}
|
|
|
|
public async Task LogCollectionEventAsync(Collection collection, EventType type, DateTime? date = null)
|
|
{
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
if (!CanUseEvents(orgAbilities, collection.OrganizationId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var e = new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = collection.OrganizationId,
|
|
CollectionId = collection.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
};
|
|
await _eventWriteService.CreateAsync(e);
|
|
}
|
|
|
|
public async Task LogGroupEventAsync(Group group, EventType type, DateTime? date = null)
|
|
{
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
if (!CanUseEvents(orgAbilities, group.OrganizationId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var e = new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = group.OrganizationId,
|
|
GroupId = group.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
};
|
|
await _eventWriteService.CreateAsync(e);
|
|
}
|
|
|
|
public async Task LogPolicyEventAsync(Policy policy, EventType type, DateTime? date = null)
|
|
{
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
if (!CanUseEvents(orgAbilities, policy.OrganizationId))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var e = new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = policy.OrganizationId,
|
|
PolicyId = policy.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
};
|
|
await _eventWriteService.CreateAsync(e);
|
|
}
|
|
|
|
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type,
|
|
DateTime? date = null) =>
|
|
await LogOrganizationUserEventsAsync(new[] { (organizationUser, type, date) });
|
|
|
|
public async Task LogOrganizationUserEventsAsync(IEnumerable<(OrganizationUser, EventType, DateTime?)> events)
|
|
{
|
|
var orgAbilities = await _applicationCacheService.GetOrganizationAbilitiesAsync();
|
|
var eventMessages = new List<IEvent>();
|
|
foreach (var (organizationUser, type, date) in events)
|
|
{
|
|
if (!CanUseEvents(orgAbilities, organizationUser.OrganizationId))
|
|
{
|
|
continue;
|
|
}
|
|
eventMessages.Add(new EventMessage
|
|
{
|
|
OrganizationId = organizationUser.OrganizationId,
|
|
UserId = organizationUser.UserId,
|
|
OrganizationUserId = organizationUser.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
});
|
|
}
|
|
|
|
await _eventWriteService.CreateManyAsync(eventMessages);
|
|
}
|
|
|
|
public async Task LogOrganizationEventAsync(Organization organization, EventType type, DateTime? date = null)
|
|
{
|
|
if (!organization.Enabled || !organization.UseEvents)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var e = new EventMessage(_currentContext)
|
|
{
|
|
OrganizationId = organization.Id,
|
|
Type = type,
|
|
ActingUserId = _currentContext?.UserId,
|
|
Date = date.GetValueOrDefault(DateTime.UtcNow)
|
|
};
|
|
await _eventWriteService.CreateAsync(e);
|
|
}
|
|
|
|
private bool CanUseEvents(IDictionary<Guid, OrganizationAbility> orgAbilities, Guid orgId)
|
|
{
|
|
return orgAbilities != null && orgAbilities.ContainsKey(orgId) &&
|
|
orgAbilities[orgId].Enabled && orgAbilities[orgId].UseEvents;
|
|
}
|
|
}
|
|
}
|