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:
parent
f4586002c4
commit
d94c2a8f50
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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; }
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user