diff --git a/src/Pages/SetupInternalWallet.razor b/src/Pages/SetupInternalWallet.razor
index bf2cb73b..9cc3feeb 100644
--- a/src/Pages/SetupInternalWallet.razor
+++ b/src/Pages/SetupInternalWallet.razor
@@ -1,5 +1,7 @@
@page "/setup-internal-wallet"
@using System.Text.RegularExpressions
+@using NodeGuard.Data.Models
+@using NodeGuard.Services
Internal wallet setup
@attribute [Authorize(Roles = "Superadmin")]
@@ -85,6 +87,9 @@
[Inject]
public IToastService ToastService { get; set; }
+ [Inject]
+ public IAuditService AuditService { get; set; }
+
private InternalWallet? _internalWallet;
private bool _isRemoteSignerModeEnabled = false;
@@ -154,6 +159,12 @@
if (updateResult.Item1)
{
ToastService.ShowSuccess("Internal wallet setup completed");
+ await AuditService.LogAsync(
+ AuditActionType.GenerateInternalWallet,
+ AuditEventType.Success,
+ AuditObjectType.InternalWallet,
+ _internalWallet.Id.ToString(),
+ new { IsRemoteSigner = _isRemoteSignerModeEnabled });
NavigationManager.NavigateTo("/");
}
else
diff --git a/src/Pages/Users.razor b/src/Pages/Users.razor
index d5272c5f..41dbbd7a 100644
--- a/src/Pages/Users.razor
+++ b/src/Pages/Users.razor
@@ -117,6 +117,7 @@
@inject IToastService ToastService
@inject INodeRepository NodeRepository
@inject ILocalStorageService LocalStorageService
+@inject IAuditService AuditService
@attribute [Authorize(Roles = "Superadmin")]
@code {
private List _users = new();
@@ -211,13 +212,24 @@
if (updateResult.Item1 && updateUserRoles.Item1)
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Create,
+ AuditEventType.Success,
+ AuditObjectType.User,
+ arg.Item.Id,
+ new { Username = arg.Item.UserName, Roles = string.Join(", ", _selectedRoles) });
await GetData();
}
else
{
ToastService.ShowError("Something went wrong");
_users.Remove(arg.Item);
-
+ await AuditService.LogAsync(
+ AuditActionType.Create,
+ AuditEventType.Failure,
+ AuditObjectType.User,
+ null,
+ new { Username = arg.Item.UserName });
}
}
@@ -276,10 +288,22 @@
if (updateResult2.Item1 && clearResult.Item1 && updateUserRoles.Item1)
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Update,
+ AuditEventType.Success,
+ AuditObjectType.User,
+ arg.Item.Id,
+ new { Username = arg.Item.UserName, Roles = string.Join(", ", _selectedRoles) });
}
else
{
ToastService.ShowError("Something went wrong");
+ await AuditService.LogAsync(
+ AuditActionType.Update,
+ AuditEventType.Failure,
+ AuditObjectType.User,
+ arg.Item.Id,
+ new { Username = arg.Item.UserName });
}
await GetData();
@@ -348,11 +372,22 @@
if (lockResult.Item1)
{
ToastService.ShowSuccess("User unlocked");
+ await AuditService.LogAsync(
+ AuditActionType.UnlockUser,
+ AuditEventType.Success,
+ AuditObjectType.User,
+ contextItem.Id,
+ new { Username = contextItem.UserName });
}
else
{
ToastService.ShowError("Something went wrong");
-
+ await AuditService.LogAsync(
+ AuditActionType.UnlockUser,
+ AuditEventType.Failure,
+ AuditObjectType.User,
+ contextItem.Id,
+ new { Username = contextItem.UserName });
}
await GetData();
@@ -369,11 +404,22 @@
if (lockResult.Item1)
{
ToastService.ShowSuccess("User locked out");
+ await AuditService.LogAsync(
+ AuditActionType.LockUser,
+ AuditEventType.Success,
+ AuditObjectType.User,
+ contextItem.Id,
+ new { Username = contextItem.UserName });
}
else
{
ToastService.ShowError("Something went wrong");
-
+ await AuditService.LogAsync(
+ AuditActionType.LockUser,
+ AuditEventType.Failure,
+ AuditObjectType.User,
+ contextItem.Id,
+ new { Username = contextItem.UserName });
}
await GetData();
diff --git a/src/Pages/Wallets.razor b/src/Pages/Wallets.razor
index 608aecff..5326dbd9 100644
--- a/src/Pages/Wallets.razor
+++ b/src/Pages/Wallets.razor
@@ -24,6 +24,7 @@
@inject IPriceConversionService PriceConversionService
@inject INBXplorerService NBXplorerService
@inject IUTXOTagRepository UTXOTagRepository
+@inject IAuditService AuditService
@attribute [Authorize(Roles = "NodeManager, FinanceManager, Superadmin")]
@@ -910,11 +911,29 @@ OnSubmit="TransferFundsHotWallet"/>
if (addResult.Item1)
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Create,
+ 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();
}
else
{
ToastService.ShowError("Something went wrong");
+ await AuditService.LogAsync(
+ AuditActionType.Create,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ null,
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = arg.Item.Name, Error = addResult.Item2 });
_wallets.Remove(arg.Item);
}
}
@@ -934,10 +953,28 @@ OnSubmit="TransferFundsHotWallet"/>
{
arg.Cancel = true;
ToastService.ShowError("Something went wrong");
+ await AuditService.LogAsync(
+ AuditActionType.Delete,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ arg.Item.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = arg.Item.Name, Error = message });
}
else
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Delete,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ arg.Item.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = arg.Item.Name });
await GetData();
}
}
@@ -969,10 +1006,28 @@ OnSubmit="TransferFundsHotWallet"/>
if (updateResult.Item1)
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Update,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ arg.Item.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = arg.Item.Name, IsHotWallet = arg.Item.IsHotWallet });
}
else
{
ToastService.ShowError("Something went wrong");
+ await AuditService.LogAsync(
+ AuditActionType.Update,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ arg.Item.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = arg.Item.Name, Error = updateResult.Item2 });
}
await GetData();
@@ -1032,10 +1087,28 @@ OnSubmit="TransferFundsHotWallet"/>
if (updateResult.Item1)
{
ToastService.ShowSuccess("Key added");
+ await AuditService.LogAsync(
+ AuditActionType.AddKey,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ _selectedWallet.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { WalletName = _selectedWallet.Name, KeyId = _selectedWalletKey.Id });
}
else
{
ToastService.ShowError("Error while adding key...");
+ await AuditService.LogAsync(
+ AuditActionType.AddKey,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ _selectedWallet.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { WalletName = _selectedWallet.Name, KeyId = _selectedWalletKey.Id, Error = updateResult.Item2 });
}
}
@@ -1241,16 +1314,36 @@ OnSubmit="TransferFundsHotWallet"/>
if (_selectedWalletToFinalise == null || _selectedWalletToFinalise.IsFinalised)
return;
+ var walletId = _selectedWalletToFinalise.Id;
+ var walletName = _selectedWalletToFinalise.Name;
var result = await WalletRepository.FinaliseWallet(_selectedWalletToFinalise);
if (result.Item1)
{
ToastService.ShowSuccess("Success");
+ await AuditService.LogAsync(
+ AuditActionType.Finalise,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ walletId.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = walletName });
}
else
{
ToastService.ShowError("Error while marking wallet as finalised");
+ await AuditService.LogAsync(
+ AuditActionType.Finalise,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ walletId.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = walletName, Error = result.Item2 });
}
_selectedWalletToFinalise = null;
@@ -1330,6 +1423,15 @@ OnSubmit="TransferFundsHotWallet"/>
if (!result.Item1)
{
ToastService.ShowError("Something went wrong while importing the wallet");
+ await AuditService.LogAsync(
+ AuditActionType.Import,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ null,
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = _name, IsWatchOnly = _IsImportWalletModalWatchOnly, Error = result.Item2 });
return;
}
@@ -1338,6 +1440,15 @@ OnSubmit="TransferFundsHotWallet"/>
//Success
ToastService.ShowSuccess("Wallet imported successfully");
+ await AuditService.LogAsync(
+ AuditActionType.Import,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ null,
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { Name = _name, IsWatchOnly = _IsImportWalletModalWatchOnly });
//Close modal
await CloseAndCleanImportWalletModal();
@@ -1493,9 +1604,38 @@ OnSubmit="TransferFundsHotWallet"/>
if (transferSuccess)
{
ToastService.ShowSuccess("Funds transferred successfully");
+ await AuditService.LogAsync(
+ AuditActionType.Transfer,
+ AuditEventType.Success,
+ AuditObjectType.Wallet,
+ _sourceTransferWallet?.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new {
+ SourceWallet = _sourceWalletName,
+ TargetWallet = _targetWalletName,
+ Amount = _amountToTransfer,
+ AllFunds = _transferAllFunds,
+ WithdrawalRequestId = withdrawalRequest.Id
+ });
}
else
{
+ await AuditService.LogAsync(
+ AuditActionType.Transfer,
+ AuditEventType.Failure,
+ AuditObjectType.Wallet,
+ _sourceTransferWallet?.Id.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new {
+ SourceWallet = _sourceWalletName,
+ TargetWallet = _targetWalletName,
+ Amount = _amountToTransfer,
+ WithdrawalRequestId = withdrawalRequest.Id
+ });
ToastService.ShowError("Error while transferring funds, please contact a superadmin for troubleshooting");
}
@@ -1741,9 +1881,28 @@ OnSubmit="TransferFundsHotWallet"/>
if (!saved)
{
ToastService.ShowError("Error while updating the UTXO status");
+ await AuditService.LogAsync(
+ newValue ? AuditActionType.FreezeUTXO : AuditActionType.UnfreezeUTXO,
+ AuditEventType.Failure,
+ AuditObjectType.UTXO,
+ utxo.Outpoint.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { WalletId = _selectedWallet?.Id, Outpoint = utxo.Outpoint.ToString() });
return;
}
+ await AuditService.LogAsync(
+ newValue ? AuditActionType.FreezeUTXO : AuditActionType.UnfreezeUTXO,
+ AuditEventType.Success,
+ AuditObjectType.UTXO,
+ utxo.Outpoint.ToString(),
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new { WalletId = _selectedWallet?.Id, Outpoint = utxo.Outpoint.ToString() });
+
// Updated the UI
_detailsUTXOs = _detailsUTXOs.Select(x =>
{
diff --git a/src/Shared/NavMenu.razor b/src/Shared/NavMenu.razor
index c261bff8..ca3a0120 100644
--- a/src/Shared/NavMenu.razor
+++ b/src/Shared/NavMenu.razor
@@ -73,6 +73,14 @@
+
+
+
+
+ Audit Trail
+
+
+
diff --git a/src/Shared/NewSwapModal.razor b/src/Shared/NewSwapModal.razor
index 6f883815..bb98c50f 100644
--- a/src/Shared/NewSwapModal.razor
+++ b/src/Shared/NewSwapModal.razor
@@ -207,6 +207,7 @@
@inject IBitcoinService BitcoinService
@inject IPriceConversionService PriceConversionService
@inject ILogger Logger
+@inject IAuditService AuditService
@inherits CancellableComponent
@code {
[CascadingParameter] public required ApplicationUser LoggedUser { get; set; }
@@ -307,6 +308,28 @@
UserRequestorId = LoggedUser.Id,
});
+ // Audit successful manual swap out creation
+ await AuditService.LogAsync(
+ AuditActionType.SwapOutInitiated,
+ AuditEventType.Success,
+ AuditObjectType.SwapOut,
+ response.Id,
+ LoggedUser?.Id,
+ LoggedUser?.UserName,
+ null,
+ new
+ {
+ NodeId = _selectedNode.Id,
+ NodeName = _selectedNode.Name,
+ Provider = _selectedProvider.ToString(),
+ AmountBtc = _amountBtc,
+ AmountSats = response.Amount,
+ DestinationWalletId = _selectedWallet.Id,
+ DestinationWalletName = _selectedWallet.Name,
+ ProviderId = response.Id,
+ IsManual = true
+ });
+
ToastService.ShowSuccess("Swap created successfully");
await _confirmationModal.CloseModal();