1
0
mirror of https://github.com/bitwarden/server.git synced 2024-12-23 17:07:42 +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>
<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.Collections.Generic;
using Bit.Core.Enums;
using Bit.Core.Utilities;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
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 int Type { get; set; }
public EventType Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? CipherId { get; set; }
public ICollection<Guid> CipherIds { get; set; }
public Guid? CollectionId { get; set; }
public Guid? GroupId { get; set; }
public Guid? OrganizationUserId { 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
{
Task<ICollection<EventTableEntity>> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate);
Task CreateAsync(EventTableEntity entity);
Task CreateManyAsync(IList<EventTableEntity> entities);
Task<ICollection<IEvent>> GetManyByUserAsync(Guid userId, DateTime startDate, DateTime endDate);
Task CreateAsync(IEvent entity);
Task CreateManyAsync(IList<IEvent> entities);
}
}

View File

@ -24,7 +24,7 @@ namespace Bit.Core.Repositories.TableStorage
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)
{
var start = CoreHelpers.DateTimeToTableStorageKey(startDate);
@ -50,34 +50,40 @@ namespace Bit.Core.Repositories.TableStorage
results.AddRange(queryResults.Results);
} 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;
}
if(entities.Count == 1)
if(e.Count == 1)
{
await CreateAsync(entities.First());
await CreateAsync(e.First());
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)
{
var groupEntities = group.ToList();
if(groupEntities.Count == 1)
{
await CreateAsync(groupEntities.First());
await CreateEntityAsync(groupEntities.First());
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
{
Task CreateAsync(EventTableEntity entity);
Task CreateManyAsync(IList<EventTableEntity> entities);
Task CreateAsync(IEvent e);
Task CreateManyAsync(IList<IEvent> e);
}
}

View File

@ -29,16 +29,16 @@ namespace Bit.Core.Services
_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);
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);
await _queue.AddMessageAsync(message);
}

View File

@ -30,18 +30,39 @@ namespace Bit.Core.Services
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)
{
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
{
var orgs = await _organizationUserRepository.GetManyByUserAsync(userId);
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())
@ -62,31 +83,67 @@ namespace Bit.Core.Services
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);
}
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);
}
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);
}
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);
}
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);
}
}

View File

@ -15,14 +15,14 @@ namespace Bit.Core.Services
_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 Task CreateAsync(EventTableEntity entity)
public Task CreateAsync(IEvent e)
{
return Task.FromResult(0);
}
public Task CreateManyAsync(IList<EventTableEntity> entities)
public Task CreateManyAsync(IList<IEvent> e)
{
return Task.FromResult(0);
}

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Bit.Core.Models.Data;
@ -29,7 +30,7 @@ namespace Bit.EventsProcessor
}
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)
{
@ -38,16 +39,17 @@ namespace Bit.EventsProcessor
try
{
var jToken = JToken.Parse(message);
if(jToken is JArray)
var token = JToken.Parse(message);
if(token is JArray)
{
var entities = jToken.ToObject<IList<EventTableEntity>>();
await _eventWriteService.CreateManyAsync(entities);
var events = token.ToObject<List<Event>>()
.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>();
await _eventWriteService.CreateAsync(entity);
var e = token.ToObject<Event>();
await _eventWriteService.CreateAsync(new EventTableEntity(e));
}
}
catch(JsonReaderException)