From de3256222c950a42fefacfe2f99abc17f8c84d55 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20A=2EP?=
<53834183+Jossec101@users.noreply.github.com>
Date: Tue, 27 Jan 2026 11:52:19 +0100
Subject: [PATCH] [GEN-1859] Refactor audit logging parameters to gather user
info implicity
Remove duplicate user ID, username, and IP address parameters from audit logging calls across authentication and channel management components to simplify the audit interface.
stack-info: PR: https://github.com/Elenpay/NodeGuard/pull/475, branch: Jossec101/stack/18
---
.../Identity/Pages/Account/LogOut.cshtml | 6 -
.../Identity/Pages/Account/Login.cshtml.cs | 9 -
.../Pages/Account/LoginWith2fa.cshtml.cs | 16 +-
src/Pages/ChannelRequests.razor | 30 ---
src/Pages/Channels.razor | 24 +--
src/Pages/Nodes.razor | 15 --
src/Pages/Wallets.razor | 48 -----
src/Services/AuditService.cs | 65 +++----
src/Services/IAuditService.cs | 13 --
src/Shared/NewSwapModal.razor | 3 -
.../Services/AuditServiceTests.cs | 182 ++++++++++++++----
11 files changed, 179 insertions(+), 232 deletions(-)
diff --git a/src/Areas/Identity/Pages/Account/LogOut.cshtml b/src/Areas/Identity/Pages/Account/LogOut.cshtml
index ed279e7c..71f50307 100644
--- a/src/Areas/Identity/Pages/Account/LogOut.cshtml
+++ b/src/Areas/Identity/Pages/Account/LogOut.cshtml
@@ -21,9 +21,6 @@
AuditEventType.Success,
AuditObjectType.User,
userId,
- userId,
- username,
- HttpContextAccessor.HttpContext.GetClientIpAddress(),
new { Username = username });
}
@@ -42,9 +39,6 @@
AuditEventType.Success,
AuditObjectType.User,
userId,
- userId,
- username,
- HttpContextAccessor.HttpContext.GetClientIpAddress(),
new { Username = username });
}
diff --git a/src/Areas/Identity/Pages/Account/Login.cshtml.cs b/src/Areas/Identity/Pages/Account/Login.cshtml.cs
index 0b2a1236..7aa24292 100644
--- a/src/Areas/Identity/Pages/Account/Login.cshtml.cs
+++ b/src/Areas/Identity/Pages/Account/Login.cshtml.cs
@@ -140,9 +140,6 @@ await _auditService.LogAsync(
AuditEventType.Success,
AuditObjectType.User,
user?.Id,
- user?.Id,
- Input.Username,
- HttpContext.GetClientIpAddress(),
new { Username = Input.Username });
return LocalRedirect(returnUrl);
}
@@ -158,9 +155,6 @@ await _auditService.LogAsync(
AuditEventType.Failure,
AuditObjectType.User,
null,
- null,
- Input.Username,
- HttpContext.GetClientIpAddress(),
new { Username = Input.Username, Reason = "Account locked out" });
return RedirectToPage("./Lockout");
}
@@ -171,9 +165,6 @@ await _auditService.LogAsync(
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 a8838f55..a2342ab9 100644
--- a/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs
+++ b/src/Areas/Identity/Pages/Account/LoginWith2fa.cshtml.cs
@@ -8,7 +8,6 @@
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Identity;
using NodeGuard.Services;
-using NodeGuard.Helpers;
namespace NodeGuard.Areas.Identity.Pages.Account
{
@@ -118,10 +117,7 @@ await _auditService.LogAsync(
AuditActionType.TwoFactorLogin,
AuditEventType.Success,
AuditObjectType.User,
- userId,
- userId,
- user.UserName,
- HttpContext.GetClientIpAddress(),
+ objectId: userId,
new { Username = user.UserName });
return LocalRedirect(returnUrl);
}
@@ -132,10 +128,7 @@ await _auditService.LogAsync(
AuditActionType.TwoFactorLogin,
AuditEventType.Failure,
AuditObjectType.User,
- userId,
- userId,
- user.UserName,
- HttpContext.GetClientIpAddress(),
+ objectId: userId,
new { Username = user.UserName, Reason = "Account locked out" });
return RedirectToPage("./Lockout");
}
@@ -146,10 +139,7 @@ await _auditService.LogAsync(
AuditActionType.TwoFactorLogin,
AuditEventType.Failure,
AuditObjectType.User,
- userId,
- userId,
- user.UserName,
- HttpContext.GetClientIpAddress(),
+ objectId: userId,
new { Username = user.UserName, Reason = "Invalid authenticator code" });
ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
return Page();
diff --git a/src/Pages/ChannelRequests.razor b/src/Pages/ChannelRequests.razor
index 92e3dd82..f522affa 100644
--- a/src/Pages/ChannelRequests.razor
+++ b/src/Pages/ChannelRequests.razor
@@ -724,9 +724,6 @@
AuditEventType.Success,
AuditObjectType.ChannelOperationRequest,
request.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { Amount = amount, SourceNodeId = _selectedSourceNodeId, DestNodeId = _selectedDestNode?.Id, Description = $"Channel open request created. Amount: {amount} BTC, SourceNodeId: {_selectedSourceNodeId}, DestNodeId: {_selectedDestNode?.Id}" });
if (_selectedUTXOs.Count > 0)
@@ -742,9 +739,6 @@
AuditEventType.Failure,
AuditObjectType.ChannelOperationRequest,
null,
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { Error = createChannelResult.Item2, Description = $"Failed to create channel open request. Error: {createChannelResult.Item2}" });
}
_utxoSelectorModalRef.ClearModal();
@@ -803,9 +797,6 @@
AuditEventType.Failure,
AuditObjectType.ChannelOperationRequest,
_selectedRequest.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = _selectedRequest.Id, Status = _selectedStatus, Description = $"Failed to {_selectedStatus.ToString().ToLower()} channel operation request" });
}
else
@@ -817,9 +808,6 @@
AuditEventType.Success,
AuditObjectType.ChannelOperationRequest,
_selectedRequest.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = _selectedRequest.Id, Status = _selectedStatus, Reason = _selectedRequest.ClosingReason, Description = $"Channel operation request {_selectedStatus.ToString().ToLower()}" });
await FetchRequests();
}
@@ -892,9 +880,6 @@
AuditEventType.Success,
AuditObjectType.ChannelOperationRequest,
_selectedRequest.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = _selectedRequest.Id, Description = "PSBT signature collected for channel operation request" });
_selectedRequest = await ChannelOperationRequestRepository.GetById(_selectedRequest.Id);
@@ -913,9 +898,6 @@
AuditEventType.Failure,
AuditObjectType.ChannelOperationRequest,
_selectedRequest.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = _selectedRequest.Id, Description = "Failed to save PSBT signature for channel operation request" });
}
@@ -955,9 +937,6 @@
AuditEventType.Success,
AuditObjectType.ChannelOperationRequest,
_selectedRequest.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { Amount = new Money(_selectedRequest.SatsAmount).ToUnit(MoneyUnit.BTC), SourceNodeId = _selectedRequest.SourceNodeId, DestNodeId = _selectedRequest.DestNodeId, IsHotWallet = true, Description = "Hot wallet channel open request created" });
}
else
@@ -968,9 +947,6 @@
AuditEventType.Failure,
AuditObjectType.ChannelOperationRequest,
null,
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { Error = createChannelResult.Item2, IsHotWallet = true, Description = "Failed to create hot wallet channel open request" });
_utxoSelectorModalRef.ClearModal();
await _approveOperationConfirmationModal.CloseModal();
@@ -1070,9 +1046,6 @@
AuditEventType.Success,
AuditObjectType.ChannelOperationRequest,
request.Id.ToString(),
- LoggedUser.Id,
- LoggedUser.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = request.Id, NewStatus = ChannelOperationRequestStatus.Failed, Description = "Channel operation request marked as failed" });
}
catch (Exception? e)
@@ -1083,9 +1056,6 @@
AuditEventType.Failure,
AuditObjectType.ChannelOperationRequest,
_selectedRequestForMarkingAsFailed?.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- HttpContextAccessor.HttpContext?.GetClientIpAddress(),
new { RequestId = _selectedRequestForMarkingAsFailed?.Id, Description = "Failed to mark channel operation request as failed" });
}
finally
diff --git a/src/Pages/Channels.razor b/src/Pages/Channels.razor
index 4764adbb..42d9cfcf 100644
--- a/src/Pages/Channels.razor
+++ b/src/Pages/Channels.razor
@@ -505,14 +505,14 @@
{
ToastService.ShowError("Something went wrong");
await AuditService.LogAsync(actionType, AuditEventType.Failure, AuditObjectType.Channel,
- channel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ channel.Id.ToString(),
$"Failed to {(forceClose ? "force " : "")}close channel. ChanId: {channel.ChanId}");
}
else
{
ToastService.ShowSuccess("Channel closed successfully");
await AuditService.LogAsync(actionType, AuditEventType.Success, AuditObjectType.Channel,
- channel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ channel.Id.ToString(),
$"Channel {(forceClose ? "force " : "")}closed. ChanId: {channel.ChanId}");
await FetchData();
}
@@ -615,14 +615,14 @@
{
ToastService.ShowError("Something went wrong");
await AuditService.LogAsync(AuditActionType.Update, AuditEventType.Failure, AuditObjectType.Channel,
- _selectedChannel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _selectedChannel.Id.ToString(),
$"Failed to update channel. ChanId: {_selectedChannel.ChanId}");
}
else
{
ToastService.ShowSuccess("Channel updated successfully");
await AuditService.LogAsync(AuditActionType.Update, AuditEventType.Success, AuditObjectType.Channel,
- _selectedChannel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _selectedChannel.Id.ToString(),
$"Channel updated. ChanId: {_selectedChannel.ChanId}");
}
}
@@ -637,14 +637,14 @@
{
ToastService.ShowError("Something went wrong");
await AuditService.LogAsync(AuditActionType.Create, AuditEventType.Failure, AuditObjectType.LiquidityRule,
- _currentLiquidityRule.ChannelId.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _currentLiquidityRule.ChannelId.ToString(),
$"Failed to add liquidity rule for channel. ChannelId: {_currentLiquidityRule.ChannelId}");
}
else
{
ToastService.ShowSuccess("Liquidity rule added successfully");
await AuditService.LogAsync(AuditActionType.Create, AuditEventType.Success, AuditObjectType.LiquidityRule,
- _currentLiquidityRule.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _currentLiquidityRule.Id.ToString(),
$"Liquidity rule created. ChannelId: {_currentLiquidityRule.ChannelId}, MinLocal: {_currentLiquidityRule.MinimumLocalBalance}%, MinRemote: {_currentLiquidityRule.MinimumRemoteBalance}%, Target: {_currentLiquidityRule.RebalanceTarget}%");
}
}
@@ -655,14 +655,14 @@
{
ToastService.ShowError("Something went wrong");
await AuditService.LogAsync(AuditActionType.Update, AuditEventType.Failure, AuditObjectType.LiquidityRule,
- _currentLiquidityRule.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _currentLiquidityRule.Id.ToString(),
$"Failed to update liquidity rule. RuleId: {_currentLiquidityRule.Id}");
}
else
{
ToastService.ShowSuccess("Liquidity rule updated successfully");
await AuditService.LogAsync(AuditActionType.Update, AuditEventType.Success, AuditObjectType.LiquidityRule,
- _currentLiquidityRule.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _currentLiquidityRule.Id.ToString(),
$"Liquidity rule updated. ChannelId: {_currentLiquidityRule.ChannelId}, MinLocal: {_currentLiquidityRule.MinimumLocalBalance}%, MinRemote: {_currentLiquidityRule.MinimumRemoteBalance}%, Target: {_currentLiquidityRule.RebalanceTarget}%");
}
}
@@ -735,14 +735,14 @@
{
ToastService.ShowError("Something went wrong");
await AuditService.LogAsync(actionType, AuditEventType.Failure, AuditObjectType.Channel,
- _selectedChannel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _selectedChannel.Id.ToString(),
$"Failed to {(enabledLiquidityMngmt ? "enable" : "disable")} liquidity management. ChanId: {_selectedChannel.ChanId}");
}
else
{
ToastService.ShowSuccess("Channel updated successfully");
await AuditService.LogAsync(actionType, AuditEventType.Success, AuditObjectType.Channel,
- _selectedChannel.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ _selectedChannel.Id.ToString(),
$"Liquidity management {(enabledLiquidityMngmt ? "enabled" : "disabled")}. ChanId: {_selectedChannel.ChanId}");
}
}
@@ -908,14 +908,14 @@
{
ToastService.ShowSuccess("Channel marked as closed");
await AuditService.LogAsync(AuditActionType.MarkAsClosed, AuditEventType.Success, AuditObjectType.Channel,
- contextItem.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ contextItem.Id.ToString(),
$"Channel marked as closed. ChanId: {contextItem.ChanId}");
}
else
{
ToastService.ShowError("Something went wrong: " + result.Item2);
await AuditService.LogAsync(AuditActionType.MarkAsClosed, AuditEventType.Failure, AuditObjectType.Channel,
- contextItem.Id.ToString(), LoggedUser?.Id, LoggedUser?.UserName, null,
+ contextItem.Id.ToString(),
$"Failed to mark channel as closed. ChanId: {contextItem.ChanId}. Error: {result.Item2}");
}
diff --git a/src/Pages/Nodes.razor b/src/Pages/Nodes.razor
index 0ab51323..ecbe8fe0 100644
--- a/src/Pages/Nodes.razor
+++ b/src/Pages/Nodes.razor
@@ -615,9 +615,6 @@
AuditEventType.Success,
AuditObjectType.Node,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, PubKey = arg.Item.PubKey });
}
else
@@ -629,9 +626,6 @@
AuditEventType.Failure,
AuditObjectType.Node,
null,
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, PubKey = arg.Item.PubKey, Error = addResult.Item2 });
}
}
@@ -656,9 +650,6 @@
AuditEventType.Success,
AuditObjectType.Node,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, PubKey = arg.Item.PubKey });
}
else
@@ -669,9 +660,6 @@
AuditEventType.Failure,
AuditObjectType.Node,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, PubKey = arg.Item.PubKey, Error = updateResult.Item2 });
}
}
@@ -722,9 +710,6 @@
AuditEventType.Success,
AuditObjectType.Node,
node.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { NodeName = node.Name, PubKey = node.PubKey });
_nodes = await NodeRepository.GetAll();
diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor
index 5326dbd9..a4febb82 100644
--- a/src/Pages/Wallets.razor
+++ b/src/Pages/Wallets.razor
@@ -916,9 +916,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, IsHotWallet = arg.Item.IsHotWallet, MofN = arg.Item.MofN });
await GetData();
}
@@ -930,9 +927,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
null,
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, Error = addResult.Item2 });
_wallets.Remove(arg.Item);
}
@@ -958,9 +952,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, Error = message });
}
else
@@ -971,9 +962,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name });
await GetData();
}
@@ -1011,9 +999,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, IsHotWallet = arg.Item.IsHotWallet });
}
else
@@ -1024,9 +1009,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
arg.Item.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = arg.Item.Name, Error = updateResult.Item2 });
}
@@ -1092,9 +1074,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
_selectedWallet.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { WalletName = _selectedWallet.Name, KeyId = _selectedWalletKey.Id });
}
else
@@ -1105,9 +1084,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
_selectedWallet.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { WalletName = _selectedWallet.Name, KeyId = _selectedWalletKey.Id, Error = updateResult.Item2 });
}
}
@@ -1327,9 +1303,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
walletId.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = walletName });
}
else
@@ -1340,9 +1313,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
walletId.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = walletName, Error = result.Item2 });
}
@@ -1428,9 +1398,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
null,
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = _name, IsWatchOnly = _IsImportWalletModalWatchOnly, Error = result.Item2 });
return;
}
@@ -1445,9 +1412,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
null,
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { Name = _name, IsWatchOnly = _IsImportWalletModalWatchOnly });
//Close modal
@@ -1609,9 +1573,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.Wallet,
_sourceTransferWallet?.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new {
SourceWallet = _sourceWalletName,
TargetWallet = _targetWalletName,
@@ -1627,9 +1588,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.Wallet,
_sourceTransferWallet?.Id.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new {
SourceWallet = _sourceWalletName,
TargetWallet = _targetWalletName,
@@ -1886,9 +1844,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Failure,
AuditObjectType.UTXO,
utxo.Outpoint.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { WalletId = _selectedWallet?.Id, Outpoint = utxo.Outpoint.ToString() });
return;
}
@@ -1898,9 +1853,6 @@ OnSubmit="TransferFundsHotWallet"/>
AuditEventType.Success,
AuditObjectType.UTXO,
utxo.Outpoint.ToString(),
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new { WalletId = _selectedWallet?.Id, Outpoint = utxo.Outpoint.ToString() });
// Updated the UI
diff --git a/src/Services/AuditService.cs b/src/Services/AuditService.cs
index 4a083d77..3e535e38 100644
--- a/src/Services/AuditService.cs
+++ b/src/Services/AuditService.cs
@@ -46,10 +46,31 @@ public async Task LogAsync(
AuditEventType eventType,
AuditObjectType objectAffected,
string? objectId = null,
- string? userId = null,
- string? username = null,
- string? ipAddress = null,
object? details = null)
+ {
+ var (userId, username, ipAddress) = ExtractContextInfo();
+ await LogInternalAsync(actionType, eventType, objectAffected, objectId, userId, username, ipAddress, details);
+ }
+
+ public async Task LogSystemAsync(
+ AuditActionType actionType,
+ AuditEventType eventType,
+ AuditObjectType objectAffected,
+ string? objectId = null,
+ object? details = null)
+ {
+ await LogInternalAsync(actionType, eventType, objectAffected, objectId, null, "SYSTEM", null, details);
+ }
+
+ private async Task LogInternalAsync(
+ AuditActionType actionType,
+ AuditEventType eventType,
+ AuditObjectType objectAffected,
+ string? objectId,
+ string? userId,
+ string? username,
+ string? ipAddress,
+ object? details)
{
try
{
@@ -92,44 +113,6 @@ public async Task LogAsync(
}
}
- public async Task LogAsync(
- AuditActionType actionType,
- AuditEventType eventType,
- AuditObjectType objectAffected,
- string? objectId = null,
- object? details = null)
- {
- var (userId, username, ipAddress) = ExtractContextInfo();
-
- await LogAsync(
- actionType,
- eventType,
- objectAffected,
- objectId,
- userId,
- username,
- ipAddress,
- details);
- }
-
- public async Task LogSystemAsync(
- AuditActionType actionType,
- AuditEventType eventType,
- AuditObjectType objectAffected,
- string? objectId = null,
- object? details = null)
- {
- await LogAsync(
- actionType,
- eventType,
- objectAffected,
- objectId,
- null, // No user ID for system operations
- "SYSTEM",
- null, // No IP address for system operations
- details);
- }
-
private (string? UserId, string? Username, string? IpAddress) ExtractContextInfo()
{
string? userId = null;
diff --git a/src/Services/IAuditService.cs b/src/Services/IAuditService.cs
index abd9879d..9cacc154 100644
--- a/src/Services/IAuditService.cs
+++ b/src/Services/IAuditService.cs
@@ -23,19 +23,6 @@ namespace NodeGuard.Services;
public interface IAuditService
{
- ///
- /// Log an audit event with all required fields
- ///
- Task LogAsync(
- AuditActionType actionType,
- AuditEventType eventType,
- AuditObjectType objectAffected,
- string? objectId = null,
- string? userId = null,
- string? username = null,
- string? ipAddress = null,
- object? details = null);
-
///
/// Log an audit event using HttpContext for user and IP extraction
///
diff --git a/src/Shared/NewSwapModal.razor b/src/Shared/NewSwapModal.razor
index bb98c50f..508d9958 100644
--- a/src/Shared/NewSwapModal.razor
+++ b/src/Shared/NewSwapModal.razor
@@ -314,9 +314,6 @@
AuditEventType.Success,
AuditObjectType.SwapOut,
response.Id,
- LoggedUser?.Id,
- LoggedUser?.UserName,
- null,
new
{
NodeId = _selectedNode.Id,
diff --git a/test/NodeGuard.Tests/Services/AuditServiceTests.cs b/test/NodeGuard.Tests/Services/AuditServiceTests.cs
index 53238834..7b9b4f46 100644
--- a/test/NodeGuard.Tests/Services/AuditServiceTests.cs
+++ b/test/NodeGuard.Tests/Services/AuditServiceTests.cs
@@ -93,55 +93,93 @@ private DefaultHttpContext CreateHttpContextWithUser(
return httpContext;
}
- #region LogAsync_FullParameters
+ #region LogAsync_AutoContext
[Fact]
- public async Task LogAsync_FullParameters_SuccessfulLogging_CallsRepositoryAndLogger()
+ public async Task LogAsync_AutoContext_WithAuthenticatedUser_ExtractsUserInfoFromClaims()
{
// Arrange
SetupSuccessfulRepository();
+ var httpContext = CreateHttpContextWithUser("user-123", "johndoe");
+ _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext);
var service = CreateAuditService();
- var details = new { Operation = "Test", Value = 123 };
// Act
await service.LogAsync(
AuditActionType.Create,
AuditEventType.Success,
- AuditObjectType.User,
- "object-123",
- "user-456",
- "johndoe",
- "10.0.0.1",
- details);
+ AuditObjectType.Wallet,
+ "wallet-456",
+ new { Amount = 50000 });
// Assert
_auditLogRepositoryMock.Verify(
x => x.AddAsync(It.Is(log =>
- log.ActionType == AuditActionType.Create &&
- log.EventType == AuditEventType.Success &&
- log.ObjectAffected == AuditObjectType.User &&
- log.ObjectId == "object-123" &&
- log.UserId == "user-456" &&
+ log.UserId == "user-123" &&
log.Username == "johndoe" &&
- log.IpAddress == "10.0.0.1" &&
- log.Details != null)),
+ log.IpAddress == "192.168.1.100")),
Times.Once);
+ }
- _loggerMock.Verify(
- x => x.Log(
- LogLevel.Information,
- It.IsAny(),
- It.Is((v, t) => v.ToString()!.Contains("AUDIT:")),
- null,
- It.IsAny>()),
+ [Fact]
+ public async Task LogAsync_AutoContext_NoHttpContext_HandlesGracefully()
+ {
+ // Arrange
+ SetupSuccessfulRepository();
+ _httpContextAccessorMock.Setup(x => x.HttpContext).Returns((HttpContext?)null);
+ var service = CreateAuditService();
+
+ // Act
+ await service.LogAsync(
+ AuditActionType.Create,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ "wallet-456",
+ new { Amount = 50000 });
+
+ // Assert
+ _auditLogRepositoryMock.Verify(
+ x => x.AddAsync(It.Is(log =>
+ log.UserId == null &&
+ log.Username == null &&
+ log.IpAddress == null)),
Times.Once);
}
[Fact]
- public async Task LogAsync_FullParameters_RepositoryFails_LogsErrorButDoesNotThrow()
+ public async Task LogAsync_AutoContext_UnauthenticatedUser_HandlesGracefully()
+ {
+ // Arrange
+ SetupSuccessfulRepository();
+ var httpContext = new DefaultHttpContext();
+ httpContext.Connection.RemoteIpAddress = System.Net.IPAddress.Parse("192.168.1.200");
+ _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext);
+ var service = CreateAuditService();
+
+ // Act
+ await service.LogAsync(
+ AuditActionType.Update,
+ AuditEventType.Success,
+ AuditObjectType.Channel,
+ "channel-789",
+ null);
+
+ // Assert
+ _auditLogRepositoryMock.Verify(
+ x => x.AddAsync(It.Is(log =>
+ log.UserId == null &&
+ log.Username == null &&
+ log.IpAddress == "192.168.1.200")),
+ Times.Once);
+ }
+
+ [Fact]
+ public async Task LogAsync_AutoContext_RepositoryFails_LogsErrorButDoesNotThrow()
{
// Arrange
SetupFailedRepository("Database connection failed");
+ var httpContext = CreateHttpContextWithUser();
+ _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext);
var service = CreateAuditService();
// Act
@@ -166,9 +204,11 @@ public async Task LogAsync_FullParameters_RepositoryFails_LogsErrorButDoesNotThr
}
[Fact]
- public async Task LogAsync_FullParameters_ExceptionDuringLogging_CatchesAndLogsError()
+ public async Task LogAsync_AutoContext_ExceptionDuringLogging_CatchesAndLogsError()
{
// Arrange
+ var httpContext = CreateHttpContextWithUser();
+ _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext);
var service = CreateAuditService();
_auditLogRepositoryMock
.Setup(x => x.AddAsync(It.IsAny()))
@@ -180,9 +220,6 @@ public async Task LogAsync_FullParameters_ExceptionDuringLogging_CatchesAndLogsE
AuditEventType.Success,
AuditObjectType.User,
"user-123",
- "user-456",
- "johndoe",
- "10.0.0.1",
new { Test = "data" });
// Assert
@@ -197,40 +234,71 @@ public async Task LogAsync_FullParameters_ExceptionDuringLogging_CatchesAndLogsE
It.IsAny>()),
Times.Once);
}
-
+
#endregion
- #region LogAsync_AutoContext
+ #region LogSystemAsync
[Fact]
- public async Task LogAsync_AutoContext_WithAuthenticatedUser_ExtractsUserInfoFromClaims()
+ public async Task LogSystemAsync_SuccessfulLogging_CreatesAuditLogWithSystemUser()
{
// Arrange
SetupSuccessfulRepository();
- var httpContext = CreateHttpContextWithUser("user-123", "johndoe");
- _httpContextAccessorMock.Setup(x => x.HttpContext).Returns(httpContext);
var service = CreateAuditService();
// Act
- await service.LogAsync(
+ await service.LogSystemAsync(
AuditActionType.Create,
AuditEventType.Success,
AuditObjectType.Wallet,
- "wallet-456",
- new { Amount = 50000 });
+ "wallet-123",
+ new { AutoGenerated = true });
// Assert
_auditLogRepositoryMock.Verify(
x => x.AddAsync(It.Is(log =>
- log.UserId == "user-123" &&
- log.Username == "johndoe" &&
- log.IpAddress == "192.168.1.100")),
+ log.ActionType == AuditActionType.Create &&
+ log.EventType == AuditEventType.Success &&
+ log.ObjectAffected == AuditObjectType.Wallet &&
+ log.ObjectId == "wallet-123" &&
+ log.UserId == null &&
+ log.Username == "SYSTEM" &&
+ log.IpAddress == null &&
+ log.Details != null)),
+ Times.Once);
+
+ _loggerMock.Verify(
+ x => x.Log(
+ LogLevel.Information,
+ It.IsAny(),
+ It.Is((v, t) => v.ToString()!.Contains("AUDIT:") && v.ToString()!.Contains("SYSTEM")),
+ null,
+ It.IsAny>()),
Times.Once);
}
-
- #endregion
- #region LogSystemAsync
+ [Fact]
+ public async Task LogSystemAsync_WithoutDetails_LogsSuccessfully()
+ {
+ // Arrange
+ SetupSuccessfulRepository();
+ var service = CreateAuditService();
+
+ // Act
+ await service.LogSystemAsync(
+ AuditActionType.Update,
+ AuditEventType.Success,
+ AuditObjectType.Channel,
+ "channel-456",
+ null);
+
+ // Assert
+ _auditLogRepositoryMock.Verify(
+ x => x.AddAsync(It.Is(log =>
+ log.Username == "SYSTEM" &&
+ log.Details == null)),
+ Times.Once);
+ }
[Fact]
public async Task LogSystemAsync_RepositoryFails_LogsErrorButDoesNotThrow()
@@ -259,5 +327,35 @@ public async Task LogSystemAsync_RepositoryFails_LogsErrorButDoesNotThrow()
Times.Once);
}
+ [Fact]
+ public async Task LogSystemAsync_ExceptionDuringLogging_CatchesAndLogsError()
+ {
+ // Arrange
+ var service = CreateAuditService();
+ _auditLogRepositoryMock
+ .Setup(x => x.AddAsync(It.IsAny()))
+ .ThrowsAsync(new InvalidOperationException("Database error"));
+
+ // Act
+ var act = async () => await service.LogSystemAsync(
+ AuditActionType.Delete,
+ AuditEventType.Success,
+ AuditObjectType.Node,
+ "node-789",
+ new { Reason = "Automated cleanup" });
+
+ // Assert
+ await act.Should().NotThrowAsync();
+
+ _loggerMock.Verify(
+ x => x.Log(
+ LogLevel.Error,
+ It.IsAny(),
+ It.Is((v, t) => v.ToString()!.Contains("Error logging audit event")),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Once);
+ }
+
#endregion
}