Skip to content

Commit

Permalink
Api auth grpc interceptor (#315)
Browse files Browse the repository at this point in the history
API Auth gRPC interceptor

---------

Co-authored-by: daliclovr <[email protected]>
  • Loading branch information
Jossec101 and daliclovr authored Oct 16, 2023
1 parent 3d3523e commit f004280
Show file tree
Hide file tree
Showing 17 changed files with 412 additions and 91 deletions.
43 changes: 43 additions & 0 deletions src/Data/DbInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,33 @@ public static void Initialize(IServiceProvider serviceProvider)
applicationDbContext.Add(testingSinglesigWallet);
applicationDbContext.Add(testingSingleSigBIP39Wallet);
}

// API Tokens generation for services
var authenticatedServices = new Dictionary<string, string>
{
{ "BTCPay", "9Hoz0PMYCsnPUzPO/JbJu8UdaKaAHJsh946xH20UzA0=" },
{ "X", "C+ktTkMGQupY9LY3IkpyqQQ2pDa7idaeSUKUnm+RawI=" },
{ "Liquidator", "8rvSsUGeyXXdDQrHctcTey/xtHdZQEn945KHwccKp9Q=" }
};

var existingTokens = applicationDbContext.ApiTokens.Where(token => authenticatedServices.Keys.Contains(token.Name)).ToList();


if (existingTokens.Count != authenticatedServices.Count && adminUser != null)
{
foreach (var service in authenticatedServices)
{
// Check if the service exists in existingTokens
if (!existingTokens.Any(token => token.Name == service.Key))
{
// The service does not exist in existingTokens, so create a new ApiToken
var newToken = CreateApiToken(service.Key, service.Value, adminUser.Id);

// Add the new token to the database
applicationDbContext.ApiTokens.Add(newToken);
}
}
}
}

applicationDbContext.SaveChanges();
Expand Down Expand Up @@ -426,6 +453,22 @@ private static void SetRoles(RoleManager<IdentityRole>? roleManager)
}
}

private static APIToken CreateApiToken(string name, string token, string userId)
{
var apiToken = new APIToken
{
Name = name,
TokenHash = token,
IsBlocked = false,
CreatorId = userId
};

apiToken.SetCreationDatetime();
apiToken.SetUpdateDatetime();

return apiToken;
}

private static NewTransactionEvent WaitNbxplorerNotification(LongPollingNotificationSession evts, DerivationStrategyBase derivationStrategy)
{
while (true)
Expand Down
6 changes: 2 additions & 4 deletions src/Data/Models/APIToken.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Security.Cryptography;
using Blazorise;

using Microsoft.AspNetCore.Cryptography.KeyDerivation;

namespace NodeGuard.Data.Models;
Expand All @@ -18,7 +17,7 @@ public class APIToken: Entity
public DateTime? ExpirationDate { get; set; }

#endregion Relationships

public void GenerateTokenHash(string password, string salt)
{

Expand All @@ -30,7 +29,6 @@ public void GenerateTokenHash(string password, string salt)
numBytesRequested: 256 / 8));

TokenHash = hashed;

}


Expand Down
24 changes: 21 additions & 3 deletions src/Data/Repositories/APITokenRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Security.Cryptography;
using Google.Protobuf.WellKnownTypes;
using Microsoft.EntityFrameworkCore;
using NodeGuard.Data.Models;
using NodeGuard.Data.Repositories.Interfaces;
Expand All @@ -21,6 +22,7 @@ public APITokenRepository(IRepository<APIToken> repository,
_dbContextFactory = dbContextFactory;
}


public async Task<(bool, string?)> AddAsync(APIToken type)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
Expand Down Expand Up @@ -55,6 +57,12 @@ public APITokenRepository(IRepository<APIToken> repository,
}
}

public async Task<APIToken?> GetByToken(string token)
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
var result = await applicationDbContext.ApiTokens.FirstOrDefaultAsync(x => x.TokenHash == token);
return result;
}
public async Task<List<APIToken>> GetAll()
{
await using var applicationDbContext = await _dbContextFactory.CreateDbContextAsync();
Expand All @@ -73,17 +81,27 @@ public async Task<List<APIToken>> GetAll()
}

public bool BlockToken(APIToken type)
{
return ChangeBlockStatus(type, true);
}

public bool UnblockToken(APIToken type)
{
return ChangeBlockStatus(type, false);
}

private bool ChangeBlockStatus(APIToken type, bool status)
{
try
{
type.IsBlocked = true;
type.IsBlocked = status;
Update(type);
return true;
}
catch (Exception e)
{
const string errorWhileBlockingToken = "Error while blocking token";
_logger.LogError(e, errorWhileBlockingToken);
var errorWhileChangingBlockStatus = status ? "Error while blocking token" : "Error while unblocking token";
_logger.LogError(e, errorWhileChangingBlockStatus);
return false;
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Data/Repositories/Interfaces/IAPITokenRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ namespace NodeGuard.Data.Repositories.Interfaces;
public interface IAPITokenRepository
{
Task<(bool, string?)> AddAsync(APIToken type);
Task<APIToken?> GetByToken(string token);
Task<List<APIToken>> GetAll();
(bool, string?) Update(APIToken type);
bool BlockToken(APIToken type);
bool UnblockToken(APIToken type);

}
2 changes: 2 additions & 0 deletions src/Helpers/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class Constants
public static readonly string? AWS_ACCESS_KEY_ID;
public static readonly string? AWS_SECRET_ACCESS_KEY;
public static readonly string API_TOKEN_SALT;
public static readonly bool GRPC_AUTH_FEATURE_FLAG;

// Crons & Jobs
public static readonly string MONITOR_WITHDRAWALS_CRON = "10 0/5 * * * ?";
Expand Down Expand Up @@ -165,6 +166,7 @@ static Constants()

API_TOKEN_SALT = Environment.GetEnvironmentVariable("API_TOKEN_SALT") ?? "H/fCx1+maAFMcdi6idIYEg==";

GRPC_AUTH_FEATURE_FLAG = Environment.GetEnvironmentVariable("GRPC_AUTH_FEATURE_FLAG") == "true";

// Crons & Jobs
MONITOR_WITHDRAWALS_CRON = Environment.GetEnvironmentVariable("MONITOR_WITHDRAWALS_CRON") ?? MONITOR_WITHDRAWALS_CRON;
Expand Down
6 changes: 3 additions & 3 deletions src/Helpers/ValidationHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public static void ValidateChannelCapacity(ValidatorEventArgs obj)
}
}

public static void ValidateWithdrawalAmount(ValidatorEventArgs obj, Boolean isAmountDisabled)
public static void ValidateWithdrawalAmount(ValidatorEventArgs obj)
{
var amount = (decimal)obj.Value;

Expand All @@ -87,13 +87,13 @@ public static void ValidateWithdrawalAmount(ValidatorEventArgs obj, Boolean isAm
decimal minimum = Constants.MINIMUM_WITHDRAWAL_BTC_AMOUNT;
decimal maximum = Constants.MAXIMUM_WITHDRAWAL_BTC_AMOUNT;

if (amount < minimum && !isAmountDisabled)
if (amount < minimum)
{
obj.Status = ValidationStatus.Error;
obj.ErrorText = $"Error, the minimum amount to withdraw is at least {minimum:f8} BTC";
}

if (amount > maximum && !isAmountDisabled)
if (amount > maximum)
{
obj.Status = ValidationStatus.Error;
obj.ErrorText = $"Error, the maximum amount to withdraw is {maximum:f8} BTC";
Expand Down
7 changes: 7 additions & 0 deletions src/Jobs/NodeChannelSubscribeJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,18 @@ public async Task Execute(IJobExecutionContext context)
_logger.LogError(e, "Error reading and update event of node {NodeId}", nodeId);
throw new JobExecutionException(e, true);
}


}


}
catch (Exception e)
{
_logger.LogError(e, "Error while subscribing for the channel updates of node {NodeId}", nodeId);
//Sleep to avoid massive requests
await Task.Delay(1000);

throw new JobExecutionException(e, true);
}

Expand Down
5 changes: 5 additions & 0 deletions src/Jobs/ProcessNodeChannelAcceptorJob.cs
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,12 @@ await AcceptChannelOpeningRequestWithUpfrontShutdown(_nBXplorerService,
catch (Exception e)
{
_logger.LogError(e, "Error on {JobName}", nameof(ProcessNodeChannelAcceptorJob));

//Sleep to avoid massive requests
await Task.Delay(1000);

throw new JobExecutionException(e, true);

}
}
}
21 changes: 17 additions & 4 deletions src/Pages/Apis.razor
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@
<Button Color="Color.Primary" Clicked="@context.Clicked">Edit</Button>
@if (!context.Item.IsBlocked)
{
<Button Color="Color.Danger" Clicked="@(()=>OnBlockTokenClicked(context.Item))">Block</Button>
<Button Color="Color.Danger" Clicked="@(()=>OnBlockStatusTokenClicked(context.Item))">Block</Button>
}
else
{
<Button Color="Color.Success" Clicked="@(()=>OnBlockStatusTokenClicked(context.Item, false))">Unblock</Button>
}
</Buttons>
</EditCommandTemplate>
Expand All @@ -48,6 +52,15 @@
</DeleteCommandTemplate>
</DataGridCommandColumn>
<DataGridColumn TItem="APIToken" Editable="true" Field="@nameof(APIToken.Name)" Caption="Name" Sortable="false" Displayable="@IsColumnVisible(APITokenColumnName.Name)" Filterable="true">
<EditTemplate>
<Validation Validator="@ValidationHelper.ValidateName">
<TextEdit Text="@((string) context.CellValue)" TextChanged="(text) => { context.CellValue = text; }">
<Feedback>
<ValidationError/>
</Feedback>
</TextEdit>
</Validation>
</EditTemplate>
</DataGridColumn>
<DataGridColumn TItem="APIToken" Field="@nameof(APIToken.CreationDatetime)" Caption="Creation time" Sortable="true" SortDirection="SortDirection.Descending" Displayable="@IsColumnVisible(APITokenColumnName.CreationDatetime)" Filterable="false">
<DisplayTemplate>
Expand Down Expand Up @@ -193,15 +206,15 @@
return APITokenColumnLayout.IsColumnVisible(column);
}

private async Task OnBlockTokenClicked(APIToken contextItem)
private async Task OnBlockStatusTokenClicked(APIToken contextItem, bool blockIt = true)
{
if (contextItem != null)
{
var result= APITokenRepository.BlockToken(contextItem);
var result= blockIt ? APITokenRepository.BlockToken(contextItem) : APITokenRepository.UnblockToken(contextItem);

if (result)
{
ToastService.ShowSuccess("Token blocked");
ToastService.ShowSuccess(blockIt ? "Token blocked" : "Token unblocked");
}
else
{
Expand Down
21 changes: 11 additions & 10 deletions src/Pages/ChannelRequests.razor
Original file line number Diff line number Diff line change
Expand Up @@ -141,16 +141,16 @@
</DisplayTemplate>
<EditTemplate>
<Validation Validator="@ValidationHelper.ValidateChannelCapacity" @ref="_capacityValidation">
<NumericPicker TValue="decimal" @bind-Value="@_amount" Min="0" Decimals="8" Step="0.00001m" CurrencySymbol=" BTC" CurrencySymbolPlacement="CurrencySymbolPlacement.Suffix" Disabled="@(SelectedUTXOs.Count > 0)">
<Feedback>
<ValidationError/>
</Feedback>
</NumericPicker>
<NumericPicker TValue="decimal" @bind-Value="@_amount" Min="@_minimumChannelCapacity" Max="@_maxChannelCapacity" Decimals="8" Step="0.00001m" CurrencySymbol=" BTC" CurrencySymbolPlacement="CurrencySymbolPlacement.Suffix" Disabled="@(SelectedUTXOs.Count > 0)"/>
<FieldHelp>

@($"Amount in Satoshis. Minimum {_minimumChannelCapacity:f8}. Current amount: {Math.Round(PriceConversionService.SatToUsdConversion(new Money(_amount, MoneyUnit.BTC).Satoshi, _btcPrice), 2)} USD")

</FieldHelp>
@{
decimal amountToShow = _amount < _maxChannelCapacity
? _amount
: _maxChannelCapacity;
decimal convertedAmount = Math.Round(PriceConversionService.SatToUsdConversion(new Money(amountToShow, MoneyUnit.BTC).Satoshi, _btcPrice), 2);
}
@($"Amount in Satoshis. Minimum {_minimumChannelCapacity:f8}. Current amount: {convertedAmount} USD")
</FieldHelp>
</Validation>
<div class="mb-3">
<Button Color="Color.Primary" Disabled="@(!_selectedWalletId.HasValue)" Clicked="@OpenCoinSelectionModal">Select Coins</Button> or use
Expand Down Expand Up @@ -459,8 +459,9 @@
private decimal _selectedWalletBalance;
private string? _destNodeName;
private static readonly decimal _minimumChannelCapacity = new Money(Constants.MINIMUM_CHANNEL_CAPACITY_SATS).ToUnit(MoneyUnit.BTC);
private static readonly decimal _maxChannelCapacity = CurrentNetworkHelper.GetCurrentNetwork() == Network.RegTest ? new Money(Constants.MAXIMUM_CHANNEL_CAPACITY_SATS_REGTEST).ToUnit(MoneyUnit.BTC) : Constants.MAXIMUM_WITHDRAWAL_BTC_AMOUNT;
private decimal _amount { get; set; } = _minimumChannelCapacity;
private bool _selectedPrivate = false;
private bool _selectedPrivate;

//Validation
private Validation? _walletValidation;
Expand Down
4 changes: 2 additions & 2 deletions src/Pages/Wallets.razor
Original file line number Diff line number Diff line change
Expand Up @@ -469,9 +469,9 @@
</Field>
</Column>
<Column ColumnSize="ColumnSize.Is6">
<Validation Validator="args => ValidationHelper.ValidateWithdrawalAmount(args, _transferAllFunds)">
<Validation Validator="args => ValidationHelper.ValidateWithdrawalAmount(args)">
<Field>
<FieldLabel>Amount</FieldLabel>
<FieldLabel>Amount</FieldLabel>
<FieldBody>
<NumericPicker TValue="decimal" Disabled="_transferAllFunds" @bind-Value="@_amountToTransfer" CurrencySymbol="₿ " Max="@_maxWithdrawal" Min="@_minWithdrawal" Decimals="8">
<Feedback>
Expand Down
Loading

0 comments on commit f004280

Please sign in to comment.