1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-24 17:17:40 +01:00

process messages with IEvent

This commit is contained in:
Kyle Spearrin 2017-12-08 23:09:50 -05:00
parent 83a7c98fae
commit 1bdf56d39f
18 changed files with 257 additions and 204 deletions

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>netcoreapp2.0;net47</TargetFrameworks> <TargetFrameworks>netcoreapp2.0;net47</TargetFrameworks>

View File

@ -1,34 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class CipherEvent : EventTableEntity
{
public CipherEvent(Cipher cipher, Guid? actingUserId, EventType type)
{
OrganizationId = cipher.OrganizationId;
UserId = cipher.UserId;
CipherId = cipher.Id;
Type = (int)type;
ActingUserId = actingUserId;
Date = DateTime.UtcNow;
if(OrganizationId.HasValue)
{
UserId = null;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__CipherId={1}__ActingUserId={2}__Type={3}",
CoreHelpers.DateTimeToTableStorageKey(Date), CipherId, ActingUserId, Type);
}
else
{
PartitionKey = $"UserId={UserId}";
RowKey = string.Format("Date={0}__CipherId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), CipherId, Type);
}
}
}
}

View File

@ -1,23 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class CollectionEvent : EventTableEntity
{
public CollectionEvent(Collection collection, Guid actingUserId, EventType type)
{
OrganizationId = collection.OrganizationId;
CollectionId = collection.Id;
Type = (int)type;
ActingUserId = actingUserId;
Date = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using Bit.Core.Enums;
namespace Bit.Core.Models.Data
{
public class Event : IEvent
{
public DateTime Date { get; set; }
public EventType Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? CipherId { get; set; }
public Guid? CollectionId { get; set; }
public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { get; set; }
public Guid? ActingUserId { get; set; }
}
}

View File

@ -1,20 +1,125 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table; using Microsoft.WindowsAzure.Storage.Table;
namespace Bit.Core.Models.Data namespace Bit.Core.Models.Data
{ {
public class EventTableEntity : TableEntity public class EventTableEntity : TableEntity, IEvent
{ {
public EventTableEntity() { }
public EventTableEntity(IEvent e)
{
Date = e.Date;
Type = e.Type;
UserId = e.UserId;
OrganizationId = e.OrganizationId;
CipherId = e.CipherId;
CollectionId = e.CollectionId;
GroupId = e.GroupId;
OrganizationUserId = e.OrganizationUserId;
ActingUserId = e.ActingUserId;
switch(e.Type)
{
case EventType.User_LoggedIn:
case EventType.User_ChangedPassword:
case EventType.User_Enabled2fa:
case EventType.User_Disabled2fa:
case EventType.User_Recovered2fa:
case EventType.User_FailedLogIn:
case EventType.User_FailedLogIn2fa:
if(e.OrganizationId.HasValue)
{
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), UserId, Type);
}
else
{
PartitionKey = $"UserId={UserId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(Date), Type);
}
break;
case EventType.Cipher_Created:
case EventType.Cipher_Updated:
case EventType.Cipher_Deleted:
case EventType.Cipher_AttachmentCreated:
case EventType.Cipher_AttachmentDeleted:
case EventType.Cipher_Shared:
case EventType.Cipher_UpdatedCollections:
if(OrganizationId.HasValue)
{
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__CipherId={1}__ActingUserId={2}__Type={3}",
CoreHelpers.DateTimeToTableStorageKey(Date), CipherId, ActingUserId, Type);
}
else
{
PartitionKey = $"UserId={UserId}";
RowKey = string.Format("Date={0}__CipherId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), CipherId, Type);
}
break;
case EventType.Collection_Created:
case EventType.Collection_Updated:
case EventType.Collection_Deleted:
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
break;
case EventType.Group_Created:
case EventType.Group_Updated:
case EventType.Group_Deleted:
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
break;
case EventType.OrganizationUser_Invited:
case EventType.OrganizationUser_Confirmed:
case EventType.OrganizationUser_Updated:
case EventType.OrganizationUser_Removed:
case EventType.OrganizationUser_UpdatedGroups:
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
break;
case EventType.Organization_Updated:
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
break;
default:
break;
}
}
public DateTime Date { get; set; } public DateTime Date { get; set; }
public int Type { get; set; } public EventType Type { get; set; }
public Guid? UserId { get; set; } public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; } public Guid? OrganizationId { get; set; }
public Guid? CipherId { get; set; } public Guid? CipherId { get; set; }
public ICollection<Guid> CipherIds { get; set; }
public Guid? CollectionId { get; set; } public Guid? CollectionId { get; set; }
public Guid? GroupId { get; set; } public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { get; set; } public Guid? OrganizationUserId { get; set; }
public Guid? ActingUserId { get; set; } public Guid? ActingUserId { get; set; }
public override IDictionary<string, EntityProperty> WriteEntity(OperationContext operationContext)
{
var result = base.WriteEntity(operationContext);
if(result.ContainsKey(nameof(Type)))
{
result[nameof(Type)] = new EntityProperty((int)Type);
}
else
{
result.Add(nameof(Type), new EntityProperty((int)Type));
}
return result;
}
} }
} }

View File

@ -1,23 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class GroupEvent : EventTableEntity
{
public GroupEvent(Group group, Guid actingUserId, EventType type)
{
OrganizationId = group.OrganizationId;
GroupId = group.Id;
Type = (int)type;
ActingUserId = actingUserId;
Date = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
}
}
}

View File

@ -0,0 +1,18 @@
using System;
using Bit.Core.Enums;
namespace Bit.Core.Models.Data
{
public interface IEvent
{
Guid? ActingUserId { get; set; }
Guid? CipherId { get; set; }
Guid? CollectionId { get; set; }
DateTime Date { get; set; }
Guid? GroupId { get; set; }
Guid? OrganizationId { get; set; }
Guid? OrganizationUserId { get; set; }
EventType Type { get; set; }
Guid? UserId { get; set; }
}
}

View File

@ -1,22 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class OrganizationEvent : EventTableEntity
{
public OrganizationEvent(Organization organization, Guid actingUserId, EventType type)
{
OrganizationId = organization.Id;
Type = (int)type;
ActingUserId = actingUserId;
Date = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
}
}
}

View File

@ -1,24 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class OrganizationUserEvent : EventTableEntity
{
public OrganizationUserEvent(OrganizationUser organizationUser, Guid actingUserId, EventType type)
{
OrganizationId = organizationUser.OrganizationId;
UserId = organizationUser.UserId;
OrganizationUserId = organizationUser.Id;
Type = (int)type;
ActingUserId = actingUserId;
Date = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__ActingUserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), ActingUserId, Type);
}
}
}

View File

@ -1,32 +0,0 @@
using System;
using Bit.Core.Enums;
using Bit.Core.Utilities;
namespace Bit.Core.Models.Data
{
public class UserEvent : EventTableEntity
{
public UserEvent(Guid userId, EventType type)
{
UserId = userId;
Type = (int)type;
Date = DateTime.UtcNow;
PartitionKey = $"UserId={UserId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(Date), Type);
}
public UserEvent(Guid userId, Guid organizationId, EventType type)
{
OrganizationId = organizationId;
UserId = userId;
Type = (int)type;
Date = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Date), UserId, Type);
}
}
}

View File

@ -7,8 +7,8 @@ namespace Bit.Core.Repositories
{ {
public interface IEventRepository public interface IEventRepository
{ {
Task<ICollection<EventTableEntity>> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate); Task<ICollection<IEvent>> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate);
Task CreateAsync(EventTableEntity entity); Task CreateAsync(IEvent entity);
Task CreateManyAsync(IList<EventTableEntity> entities); Task CreateManyAsync(IList<IEvent> entities);
} }
} }

View File

@ -24,7 +24,7 @@ namespace Bit.Core.Repositories.TableStorage
protected CloudTable Table { get; set; } protected CloudTable Table { get; set; }
public async Task<ICollection<EventTableEntity>> GetManyByUserAsync(Guid userId, public async Task<ICollection<IEvent>> GetManyByUserAsync(Guid userId,
DateTime startDate, DateTime endDate) DateTime startDate, DateTime endDate)
{ {
var start = CoreHelpers.DateTimeToTableStorageKey(startDate); var start = CoreHelpers.DateTimeToTableStorageKey(startDate);
@ -50,34 +50,40 @@ namespace Bit.Core.Repositories.TableStorage
results.AddRange(queryResults.Results); results.AddRange(queryResults.Results);
} while(continuationToken != null); } while(continuationToken != null);
return results; return results.Select(r => r as IEvent).ToList();
} }
public async Task CreateAsync(EventTableEntity entity) public async Task CreateAsync(IEvent e)
{ {
await Table.ExecuteAsync(TableOperation.Insert(entity)); if(!(e is EventTableEntity entity))
{
throw new ArgumentException(nameof(e));
} }
public async Task CreateManyAsync(IList<EventTableEntity> entities) await CreateEntityAsync(entity);
}
public async Task CreateManyAsync(IList<IEvent> e)
{ {
if(!entities?.Any() ?? true) if(!e?.Any() ?? true)
{ {
return; return;
} }
if(entities.Count == 1) if(e.Count == 1)
{ {
await CreateAsync(entities.First()); await CreateAsync(e.First());
return; return;
} }
var entityGroups = entities.GroupBy(e => e.PartitionKey); var entities = e.Where(ev => ev is EventTableEntity).Select(ev => ev as EventTableEntity);
var entityGroups = entities.GroupBy(ent => ent.PartitionKey);
foreach(var group in entityGroups) foreach(var group in entityGroups)
{ {
var groupEntities = group.ToList(); var groupEntities = group.ToList();
if(groupEntities.Count == 1) if(groupEntities.Count == 1)
{ {
await CreateAsync(groupEntities.First()); await CreateEntityAsync(groupEntities.First());
continue; continue;
} }
@ -101,5 +107,10 @@ namespace Bit.Core.Repositories.TableStorage
} }
} }
} }
public async Task CreateEntityAsync(ITableEntity entity)
{
await Table.ExecuteAsync(TableOperation.Insert(entity));
}
} }
} }

View File

@ -6,7 +6,7 @@ namespace Bit.Core.Services
{ {
public interface IEventWriteService public interface IEventWriteService
{ {
Task CreateAsync(EventTableEntity entity); Task CreateAsync(IEvent e);
Task CreateManyAsync(IList<EventTableEntity> entities); Task CreateManyAsync(IList<IEvent> e);
} }
} }

View File

@ -29,16 +29,16 @@ namespace Bit.Core.Services
_globalSettings = globalSettings; _globalSettings = globalSettings;
} }
public async Task CreateAsync(EventTableEntity entity) public async Task CreateAsync(IEvent e)
{ {
var json = JsonConvert.SerializeObject(entity, _jsonSettings); var json = JsonConvert.SerializeObject(e, _jsonSettings);
var message = new CloudQueueMessage(json); var message = new CloudQueueMessage(json);
await _queue.AddMessageAsync(message); await _queue.AddMessageAsync(message);
} }
public async Task CreateManyAsync(IList<EventTableEntity> entities) public async Task CreateManyAsync(IList<IEvent> e)
{ {
var json = JsonConvert.SerializeObject(entities, _jsonSettings); var json = JsonConvert.SerializeObject(e, _jsonSettings);
var message = new CloudQueueMessage(json); var message = new CloudQueueMessage(json);
await _queue.AddMessageAsync(message); await _queue.AddMessageAsync(message);
} }

View File

@ -30,18 +30,39 @@ namespace Bit.Core.Services
public async Task LogUserEventAsync(Guid userId, EventType type) public async Task LogUserEventAsync(Guid userId, EventType type)
{ {
var events = new List<EventTableEntity> { new UserEvent(userId, type) }; var now = DateTime.UtcNow;
var events = new List<IEvent>
{
new Event
{
UserId = userId,
Type = type,
Date = now
}
};
IEnumerable<UserEvent> orgEvents; IEnumerable<IEvent> orgEvents;
if(_currentContext.UserId.HasValue) if(_currentContext.UserId.HasValue)
{ {
orgEvents = _currentContext.Organizations.Select(o => new UserEvent(userId, o.Id, type)); orgEvents = _currentContext.Organizations.Select(o => new Event
{
OrganizationId = o.Id,
UserId = userId,
Type = type,
Date = DateTime.UtcNow
});
} }
else else
{ {
var orgs = await _organizationUserRepository.GetManyByUserAsync(userId); var orgs = await _organizationUserRepository.GetManyByUserAsync(userId);
orgEvents = orgs.Where(o => o.Status == OrganizationUserStatusType.Confirmed) orgEvents = orgs.Where(o => o.Status == OrganizationUserStatusType.Confirmed)
.Select(o => new UserEvent(userId, o.Id, type)); .Select(o => new Event
{
OrganizationId = o.Id,
UserId = userId,
Type = type,
Date = DateTime.UtcNow
});
} }
if(orgEvents.Any()) if(orgEvents.Any())
@ -62,31 +83,67 @@ namespace Bit.Core.Services
return; return;
} }
var e = new CipherEvent(cipher, _currentContext?.UserId, type); var e = new Event
{
OrganizationId = cipher.OrganizationId,
UserId = cipher.OrganizationId.HasValue ? null : cipher.UserId,
CipherId = cipher.Id,
Type = type,
ActingUserId = _currentContext?.UserId,
Date = DateTime.UtcNow
};
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
public async Task LogCollectionEventAsync(Collection collection, EventType type) public async Task LogCollectionEventAsync(Collection collection, EventType type)
{ {
var e = new CollectionEvent(collection, _currentContext.UserId.Value, type); var e = new Event
{
OrganizationId = collection.OrganizationId,
CollectionId = collection.Id,
Type = type,
ActingUserId = _currentContext.UserId.Value,
Date = DateTime.UtcNow
};
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
public async Task LogGroupEventAsync(Group group, EventType type) public async Task LogGroupEventAsync(Group group, EventType type)
{ {
var e = new GroupEvent(group, _currentContext.UserId.Value, type); var e = new Event
{
OrganizationId = group.OrganizationId,
GroupId = group.Id,
Type = type,
ActingUserId = _currentContext.UserId.Value,
Date = DateTime.UtcNow
};
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type) public async Task LogOrganizationUserEventAsync(OrganizationUser organizationUser, EventType type)
{ {
var e = new OrganizationUserEvent(organizationUser, _currentContext.UserId.Value, type); var e = new Event
{
OrganizationId = organizationUser.OrganizationId,
UserId = organizationUser.UserId,
OrganizationUserId = organizationUser.Id,
Type = type,
ActingUserId = _currentContext.UserId.Value,
Date = DateTime.UtcNow
};
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
public async Task LogOrganizationEventAsync(Organization organization, EventType type) public async Task LogOrganizationEventAsync(Organization organization, EventType type)
{ {
var e = new OrganizationEvent(organization, _currentContext.UserId.Value, type); var e = new Event
{
OrganizationId = organization.Id,
Type = type,
ActingUserId = _currentContext.UserId.Value,
Date = DateTime.UtcNow
};
await _eventWriteService.CreateAsync(e); await _eventWriteService.CreateAsync(e);
} }
} }

View File

@ -15,14 +15,14 @@ namespace Bit.Core.Services
_eventRepository = eventRepository; _eventRepository = eventRepository;
} }
public async Task CreateAsync(EventTableEntity entity) public async Task CreateAsync(IEvent e)
{ {
await _eventRepository.CreateAsync(entity); await _eventRepository.CreateAsync(e);
} }
public async Task CreateManyAsync(IList<EventTableEntity> entities) public async Task CreateManyAsync(IList<IEvent> e)
{ {
await _eventRepository.CreateManyAsync(entities); await _eventRepository.CreateManyAsync(e);
} }
} }
} }

View File

@ -6,12 +6,12 @@ namespace Bit.Core.Services
{ {
public class NoopEventWriteService : IEventWriteService public class NoopEventWriteService : IEventWriteService
{ {
public Task CreateAsync(EventTableEntity entity) public Task CreateAsync(IEvent e)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }
public Task CreateManyAsync(IList<EventTableEntity> entities) public Task CreateManyAsync(IList<IEvent> e)
{ {
return Task.FromResult(0); return Task.FromResult(0);
} }

View File

@ -2,6 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Configuration; using System.Configuration;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
@ -29,7 +30,7 @@ namespace Bit.EventsProcessor
} }
public async static Task ProcessQueueMessageAsync([QueueTrigger("event")] string message, public async static Task ProcessQueueMessageAsync([QueueTrigger("event")] string message,
TextWriter logger, CancellationToken token) TextWriter logger, CancellationToken cancellationToken)
{ {
if(_eventWriteService == null || message == null || message.Length == 0) if(_eventWriteService == null || message == null || message.Length == 0)
{ {
@ -38,16 +39,17 @@ namespace Bit.EventsProcessor
try try
{ {
var jToken = JToken.Parse(message); var token = JToken.Parse(message);
if(jToken is JArray) if(token is JArray)
{ {
var entities = jToken.ToObject<IList<EventTableEntity>>(); var events = token.ToObject<List<Event>>()
await _eventWriteService.CreateManyAsync(entities); .Select(e => new EventTableEntity(e) as IEvent).ToList();
await _eventWriteService.CreateManyAsync(events);
} }
else if(jToken is JObject) else if(token is JObject)
{ {
var entity = jToken.ToObject<EventTableEntity>(); var e = token.ToObject<Event>();
await _eventWriteService.CreateAsync(entity); await _eventWriteService.CreateAsync(new EventTableEntity(e));
} }
} }
catch(JsonReaderException) catch(JsonReaderException)