diff --git a/src/Areas/Identity/Pages/Account/LogOut.cshtml b/src/Areas/Identity/Pages/Account/LogOut.cshtml index b3b66748..ed279e7c 100644 --- a/src/Areas/Identity/Pages/Account/LogOut.cshtml +++ b/src/Areas/Identity/Pages/Account/LogOut.cshtml @@ -1,27 +1,53 @@ @page @using NodeGuard.Data.Models @using Microsoft.AspNetCore.Identity +@using NodeGuard.Services +@using NodeGuard.Helpers +@using System.Security.Claims @attribute [IgnoreAntiforgeryToken] @inject SignInManager SignInManager +@inject IAuditService AuditService +@inject IHttpContextAccessor HttpContextAccessor @functions { public async Task OnPost() { if (SignInManager.IsSignedIn(User)) { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var username = User.Identity?.Name; await SignInManager.SignOutAsync(); + await AuditService.LogAsync( + AuditActionType.Logout, + AuditEventType.Success, + AuditObjectType.User, + userId, + userId, + username, + HttpContextAccessor.HttpContext.GetClientIpAddress(), + new { Username = username }); } return Redirect("~/"); } - public async Task OnGet() - { - if (SignInManager.IsSignedIn(User)) - { - await SignInManager.SignOutAsync(); - } - - return Redirect("~/"); - } + public async Task OnGet() + { + if (SignInManager.IsSignedIn(User)) + { + var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value; + var username = User.Identity?.Name; + await SignInManager.SignOutAsync(); + await AuditService.LogAsync( + AuditActionType.Logout, + AuditEventType.Success, + AuditObjectType.User, + userId, + userId, + username, + HttpContextAccessor.HttpContext.GetClientIpAddress(), + new { Username = username }); + } + return Redirect("~/"); + } } diff --git a/src/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Areas/Identity/Pages/Account/Login.cshtml.cs index 35ab49a2..0b2a1236 100644 --- a/src/Areas/Identity/Pages/Account/Login.cshtml.cs +++ b/src/Areas/Identity/Pages/Account/Login.cshtml.cs @@ -15,6 +15,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; +using NodeGuard.Services; +using NodeGuard.Helpers; namespace NodeGuard.Areas.Identity.Pages.Account { @@ -22,11 +24,15 @@ public class LoginModel : PageModel { private readonly SignInManager _signInManager; private readonly ILogger _logger; + private readonly IAuditService _auditService; + private readonly UserManager _userManager; - public LoginModel(SignInManager signInManager, ILogger logger) + public LoginModel(SignInManager signInManager, ILogger logger, IAuditService auditService, UserManager userManager) { _signInManager = signInManager; _logger = logger; + _auditService = auditService; + _userManager = userManager; } /// @@ -128,6 +134,16 @@ public async Task OnPostAsync(string returnUrl = null) if (result.Succeeded) { _logger.LogInformation("User logged in."); + var user = await _userManager.FindByNameAsync(Input.Username); + await _auditService.LogAsync( + AuditActionType.Login, + AuditEventType.Success, + AuditObjectType.User, + user?.Id, + user?.Id, + Input.Username, + HttpContext.GetClientIpAddress(), + new { Username = Input.Username }); return LocalRedirect(returnUrl); } if (result.RequiresTwoFactor) @@ -137,10 +153,28 @@ public async Task OnPostAsync(string returnUrl = null) if (result.IsLockedOut) { _logger.LogWarning("User account locked out."); + await _auditService.LogAsync( + AuditActionType.Login, + AuditEventType.Failure, + AuditObjectType.User, + null, + null, + Input.Username, + HttpContext.GetClientIpAddress(), + new { Username = Input.Username, Reason = "Account locked out" }); return RedirectToPage("./Lockout"); } else { + await _auditService.LogAsync( + AuditActionType.Login, + AuditEventType.Failure, + AuditObjectType.User, + null, + null, + Input.Username, + HttpContext.GetClientIpAddress(), + new { Username = Input.Username, Reason = "Invalid credentials" }); ModelState.AddModelError(string.Empty, "Invalid login attempt."); return Page(); } diff --git a/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs b/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs index 048cb491..a8838f55 100644 --- a/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs +++ b/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs @@ -7,6 +7,8 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Identity; +using NodeGuard.Services; +using NodeGuard.Helpers; namespace NodeGuard.Areas.Identity.Pages.Account { @@ -15,15 +17,18 @@ public class LoginWith2faModel : PageModel private readonly SignInManager _signInManager; private readonly UserManager _userManager; private readonly ILogger _logger; + private readonly IAuditService _auditService; public LoginWith2faModel( SignInManager signInManager, UserManager userManager, - ILogger logger) + ILogger logger, + IAuditService auditService) { _signInManager = signInManager; _userManager = userManager; _logger = logger; + _auditService = auditService; } /// @@ -109,16 +114,43 @@ public async Task OnPostAsync(bool rememberMe, string returnUrl = if (result.Succeeded) { _logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id); + await _auditService.LogAsync( + AuditActionType.TwoFactorLogin, + AuditEventType.Success, + AuditObjectType.User, + userId, + userId, + user.UserName, + HttpContext.GetClientIpAddress(), + new { Username = user.UserName }); return LocalRedirect(returnUrl); } else if (result.IsLockedOut) { _logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id); + await _auditService.LogAsync( + AuditActionType.TwoFactorLogin, + AuditEventType.Failure, + AuditObjectType.User, + userId, + userId, + user.UserName, + HttpContext.GetClientIpAddress(), + new { Username = user.UserName, Reason = "Account locked out" }); return RedirectToPage("./Lockout"); } else { _logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id); + await _auditService.LogAsync( + AuditActionType.TwoFactorLogin, + AuditEventType.Failure, + AuditObjectType.User, + userId, + userId, + user.UserName, + HttpContext.GetClientIpAddress(), + new { Username = user.UserName, Reason = "Invalid authenticator code" }); ModelState.AddModelError(string.Empty, "Invalid authenticator code."); return Page(); } diff --git a/src/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs b/src/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs index 6a7c8df9..1006e6fd 100644 --- a/src/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs +++ b/src/Areas/Identity/Pages/Account/Manage/Disable2fa.cshtml.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; +using NodeGuard.Services; namespace NodeGuard.Areas.Identity.Pages.Account.Manage { @@ -16,13 +17,16 @@ public class Disable2faModel : PageModel { private readonly UserManager _userManager; private readonly ILogger _logger; + private readonly IAuditService _auditService; public Disable2faModel( UserManager userManager, - ILogger logger) + ILogger logger, + IAuditService auditService) { _userManager = userManager; _logger = logger; + _auditService = auditService; } /// @@ -63,6 +67,14 @@ public async Task OnPostAsync() } _logger.LogInformation("User with ID '{UserId}' has disabled 2fa.", _userManager.GetUserId(User)); + + await _auditService.LogAsync( + AuditActionType.TwoFactorDisabled, + AuditEventType.Success, + AuditObjectType.User, + user.Id, + new { Username = user.UserName }); + StatusMessage = "2fa has been disabled. You can reenable 2fa when you setup an authenticator app"; return RedirectToPage("./TwoFactorAuthentication"); } diff --git a/src/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs b/src/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs index dff5b05f..7de753b0 100644 --- a/src/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs +++ b/src/Areas/Identity/Pages/Account/Manage/EnableAuthenticator.cshtml.cs @@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Extensions.Logging; +using NodeGuard.Services; namespace NodeGuard.Areas.Identity.Pages.Account.Manage { @@ -22,17 +23,20 @@ public class EnableAuthenticatorModel : PageModel private readonly UserManager _userManager; private readonly ILogger _logger; private readonly UrlEncoder _urlEncoder; + private readonly IAuditService _auditService; private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; public EnableAuthenticatorModel( UserManager userManager, ILogger logger, - UrlEncoder urlEncoder) + UrlEncoder urlEncoder, + IAuditService auditService) { _userManager = userManager; _logger = logger; _urlEncoder = urlEncoder; + _auditService = auditService; } /// @@ -129,6 +133,13 @@ public async Task OnPostAsync() var userId = await _userManager.GetUserIdAsync(user); _logger.LogInformation("User with ID '{UserId}' has enabled 2FA with an authenticator app.", userId); + await _auditService.LogAsync( + AuditActionType.TwoFactorEnabled, + AuditEventType.Success, + AuditObjectType.User, + userId, + new { Username = user.UserName }); + StatusMessage = "Your authenticator app has been verified."; if (await _userManager.CountRecoveryCodesAsync(user) == 0)