1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-22 12:15:36 +01:00

log user events

This commit is contained in:
Kyle Spearrin 2017-12-01 10:07:14 -05:00
parent f4586002c4
commit d94c2a8f50
7 changed files with 70 additions and 41 deletions

View File

@ -6,6 +6,9 @@
User_ChangedPassword = 1001,
User_Enabled2fa = 1002,
User_Disabled2fa = 1003,
User_Recovered2fa = 1004,
User_FailedLogIn = 1005,
User_FailedLogIn2fa = 1006,
Cipher_Created = 2000,
Cipher_Edited = 2001,

View File

@ -21,17 +21,20 @@ namespace Bit.Core.IdentityServer
private readonly IDeviceRepository _deviceRepository;
private readonly IDeviceService _deviceService;
private readonly IUserService _userService;
private readonly IEventService _eventService;
public ResourceOwnerPasswordValidator(
UserManager<User> userManager,
IDeviceRepository deviceRepository,
IDeviceService deviceService,
IUserService userService)
IUserService userService,
IEventService eventService)
{
_userManager = userManager;
_deviceRepository = deviceRepository;
_deviceService = deviceService;
_userService = userService;
_eventService = eventService;
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
@ -43,14 +46,14 @@ namespace Bit.Core.IdentityServer
if(string.IsNullOrWhiteSpace(context.UserName))
{
await BuildErrorResultAsync(false, context);
await BuildErrorResultAsync(false, context, null);
return;
}
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
if(user == null || !await _userManager.CheckPasswordAsync(user, context.Password))
{
await BuildErrorResultAsync(false, context);
await BuildErrorResultAsync(false, context, user);
return;
}
@ -66,7 +69,7 @@ namespace Bit.Core.IdentityServer
var verified = await VerifyTwoFactor(user, twoFactorProviderType, twoFactorToken);
if(!verified && twoFactorProviderType != TwoFactorProviderType.Remember)
{
await BuildErrorResultAsync(true, context);
await BuildErrorResultAsync(true, context, user);
return;
}
else if(!verified && twoFactorProviderType == TwoFactorProviderType.Remember)
@ -91,6 +94,8 @@ namespace Bit.Core.IdentityServer
private async Task BuildSuccessResultAsync(User user, ResourceOwnerPasswordValidationContext context,
Device device, bool sendRememberToken)
{
await _eventService.LogUserEventAsync(user.Id, EventType.User_LoggedIn);
var claims = new List<Claim>();
if(device != null)
@ -128,7 +133,7 @@ namespace Bit.Core.IdentityServer
var enabledProviders = user.GetTwoFactorProviders()?.Where(p => user.TwoFactorProviderIsEnabled(p.Key));
if(enabledProviders == null)
{
await BuildErrorResultAsync(false, context);
await BuildErrorResultAsync(false, context, user);
return;
}
@ -153,8 +158,15 @@ namespace Bit.Core.IdentityServer
}
}
private async Task BuildErrorResultAsync(bool twoFactorRequest, ResourceOwnerPasswordValidationContext context)
private async Task BuildErrorResultAsync(bool twoFactorRequest,
ResourceOwnerPasswordValidationContext context, User user)
{
if(user != null)
{
await _eventService.LogUserEventAsync(user.Id,
twoFactorRequest ? EventType.User_FailedLogIn2fa : EventType.User_FailedLogIn);
}
await Task.Delay(2000); // Delay for brute force.
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
customResponse: new Dictionary<string, object>

View File

@ -1,4 +1,5 @@
using Bit.Core.Enums;
using System;
using Bit.Core.Enums;
using Bit.Core.Models.Table;
using Bit.Core.Utilities;
@ -8,22 +9,23 @@ namespace Bit.Core.Models.Data
{
public CipherEvent(Cipher cipher, EventType type)
{
if(cipher.OrganizationId.HasValue)
{
PartitionKey = $"OrganizationId={cipher.OrganizationId.Value}";
}
else
{
PartitionKey = $"UserId={cipher.UserId.Value}";
}
RowKey = string.Format("Date={0}__CipherId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(), cipher.Id, type);
OrganizationId = cipher.OrganizationId;
UserId = cipher.UserId;
CipherId = cipher.Id;
Type = type;
Type = (int)type;
Timestamp = DateTime.UtcNow;
if(OrganizationId.HasValue)
{
PartitionKey = $"OrganizationId={OrganizationId}";
}
else
{
PartitionKey = $"UserId={UserId}";
}
RowKey = string.Format("Date={0}__CipherId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Timestamp.DateTime), CipherId, Type);
}
}
}

View File

@ -7,7 +7,7 @@ namespace Bit.Core.Models.Data
{
public class EventTableEntity : TableEntity
{
public EventType Type { get; set; }
public int Type { get; set; }
public Guid? UserId { get; set; }
public Guid? OrganizationId { get; set; }
public Guid? CipherId { get; set; }

View File

@ -8,23 +8,25 @@ namespace Bit.Core.Models.Data
{
public OrganizationEvent(Guid organizationId, EventType type)
{
PartitionKey = $"OrganizationId={organizationId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(), type);
OrganizationId = organizationId;
Type = type;
Type = (int)type;
Timestamp = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(Timestamp.DateTime), Type);
}
public OrganizationEvent(Guid organizationId, Guid userId, EventType type)
{
PartitionKey = $"OrganizationId={organizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(), userId, type);
OrganizationId = organizationId;
UserId = userId;
Type = type;
Type = (int)type;
Timestamp = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Timestamp.DateTime), UserId, Type);
}
}
}

View File

@ -8,23 +8,25 @@ namespace Bit.Core.Models.Data
{
public UserEvent(Guid userId, EventType type)
{
PartitionKey = $"UserId={userId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(), type);
UserId = userId;
Type = type;
Type = (int)type;
Timestamp = DateTime.UtcNow;
PartitionKey = $"UserId={UserId}";
RowKey = string.Format("Date={0}__Type={1}",
CoreHelpers.DateTimeToTableStorageKey(Timestamp.DateTime), Type);
}
public UserEvent(Guid userId, Guid organizationId, EventType type)
{
PartitionKey = $"OrganizationId={organizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(), userId, type);
OrganizationId = organizationId;
UserId = userId;
Type = type;
Type = (int)type;
Timestamp = DateTime.UtcNow;
PartitionKey = $"OrganizationId={OrganizationId}";
RowKey = string.Format("Date={0}__UserId={1}__Type={2}",
CoreHelpers.DateTimeToTableStorageKey(Timestamp.DateTime), UserId, Type);
}
}
}

View File

@ -37,6 +37,7 @@ namespace Bit.Core.Services
private readonly IPasswordHasher<User> _passwordHasher;
private readonly IEnumerable<IPasswordValidator<User>> _passwordValidators;
private readonly ILicensingService _licenseService;
private readonly IEventService _eventService;
private readonly CurrentContext _currentContext;
private readonly GlobalSettings _globalSettings;
@ -57,6 +58,7 @@ namespace Bit.Core.Services
IServiceProvider services,
ILogger<UserManager<User>> logger,
ILicensingService licenseService,
IEventService eventService,
CurrentContext currentContext,
GlobalSettings globalSettings)
: base(
@ -81,6 +83,7 @@ namespace Bit.Core.Services
_passwordHasher = passwordHasher;
_passwordValidators = passwordValidators;
_licenseService = licenseService;
_eventService = eventService;
_currentContext = currentContext;
_globalSettings = globalSettings;
}
@ -420,7 +423,9 @@ namespace Bit.Core.Services
user.RevisionDate = user.AccountRevisionDate = DateTime.UtcNow;
user.Key = key;
await _userRepository.ReplaceAsync(user);
await _eventService.LogUserEventAsync(user.Id, EventType.User_ChangedPassword);
return IdentityResult.Success;
}
@ -498,6 +503,7 @@ namespace Bit.Core.Services
user.TwoFactorRecoveryCode = CoreHelpers.SecureRandomString(32, upper: false, special: false);
}
await SaveUserAsync(user);
await _eventService.LogUserEventAsync(user.Id, EventType.User_Enabled2fa);
}
public async Task DisableTwoFactorProviderAsync(User user, TwoFactorProviderType type)
@ -511,6 +517,7 @@ namespace Bit.Core.Services
providers.Remove(type);
user.SetTwoFactorProviders(providers);
await SaveUserAsync(user);
await _eventService.LogUserEventAsync(user.Id, EventType.User_Disabled2fa);
}
public async Task<bool> RecoverTwoFactorAsync(string email, string masterPassword, string recoveryCode)
@ -535,6 +542,7 @@ namespace Bit.Core.Services
user.TwoFactorProviders = null;
user.TwoFactorRecoveryCode = CoreHelpers.SecureRandomString(32, upper: false, special: false);
await SaveUserAsync(user);
await _eventService.LogUserEventAsync(user.Id, EventType.User_Recovered2fa);
return true;
}