Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api auth grpc interceptor #315

Merged
merged 31 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
a62d268
added ui for generating new tokens for NG grpc api
daliclovr Sep 11, 2023
d9b2cdb
changes proposed in review
daliclovr Sep 13, 2023
cf3b4a7
added migration files
daliclovr Sep 13, 2023
a94d45f
enabled unblock button and functionality
daliclovr Sep 21, 2023
66389cc
validation on empty name field
daliclovr Sep 21, 2023
44032e4
formatting on razor page
daliclovr Sep 21, 2023
4fd0583
removed old migration files
daliclovr Sep 22, 2023
20cb829
Add some improvements.
AleksKSoftware Sep 3, 2023
7a4bd63
added interceptor to authenticate through api token
daliclovr Sep 15, 2023
f503ca2
added unittesting for interceptor
daliclovr Sep 15, 2023
d0d88fe
added not auth if the token is blocked
daliclovr Sep 20, 2023
56fb483
removing with-context idea
daliclovr Sep 25, 2023
8e99e32
added one more test for blocked token case
daliclovr Sep 25, 2023
1ae1c81
commented different part of tests
daliclovr Sep 25, 2023
b52f42a
rename auth interceptor
daliclovr Sep 25, 2023
795e7e1
Added some fixes according to the comments.
AleksKSoftware Sep 27, 2023
5073eed
Removed unused code.
AleksKSoftware Sep 27, 2023
a793248
Added some improvements.
AleksKSoftware Sep 27, 2023
27bc855
Improvements fields.
AleksKSoftware Oct 1, 2023
0c5d866
feat(NodeChannelSubscribeJob.cs, ProcessNodeChannelAcceptorJob.cs): a…
Jossec101 Oct 4, 2023
77da1ba
Improvements
AleksKSoftware Oct 9, 2023
09ec03c
Resolving issues according to the comments
AleksKSoftware Oct 10, 2023
39b80f4
Fixed chanel capacity for regtest.
AleksKSoftware Oct 10, 2023
a59aae4
following structure of act & assert with WithMessage function
daliclovr Oct 13, 2023
9464406
added feature flag to enable authentication
daliclovr Oct 13, 2023
19ba912
Fixed amount field.
AleksKSoftware Oct 15, 2023
4a949d7
added apitoken generation for development purposes
daliclovr Oct 16, 2023
3dbc889
fixing typos
daliclovr Oct 16, 2023
89ddee3
Merge branch 'main' into fix_edit_field
RodriFS Oct 16, 2023
49c5c47
Merge pull request #306 from Elenpay/fix_edit_field
RodriFS Oct 16, 2023
cc68c06
Merge branch 'main' into api-auth-grpc-interceptor
RodriFS Oct 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 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 @@ -116,7 +117,7 @@
// Connections
POSTGRES_CONNECTIONSTRING = Environment.GetEnvironmentVariable("POSTGRES_CONNECTIONSTRING") ?? POSTGRES_CONNECTIONSTRING;

NBXPLORER_URI = GetEnvironmentalVariableOrThrowIfNotTesting("NBXPLORER_URI");

Check warning on line 120 in src/Helpers/Constants.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference assignment.

NBXPLORER_BTCRPCURL = Environment.GetEnvironmentVariable("NBXPLORER_BTCRPCURL");

Expand All @@ -143,7 +144,7 @@

if (PUSH_NOTIFICATIONS_ONESIGNAL_ENABLED)
{
PUSH_NOTIFICATIONS_ONESIGNAL_APP_ID = GetEnvironmentalVariableOrThrowIfNotTesting("PUSH_NOTIFICATIONS_ONESIGNAL_APP_ID", "if PUSH_NOTIFICATIONS_ONESIGNAL_ENABLED is set, PUSH_NOTIFICATIONS_ONESIGNAL_APP_ID");

Check warning on line 147 in src/Helpers/Constants.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference assignment.

PUSH_NOTIFICATIONS_ONESIGNAL_API_BASE_PATH = GetEnvironmentalVariableOrThrowIfNotTesting("PUSH_NOTIFICATIONS_ONESIGNAL_API_BASE_PATH", "if PUSH_NOTIFICATIONS_ONESIGNAL_ENABLED is set,PUSH_NOTIFICATIONS_ONESIGNAL_API_BASE_PATH");

Expand All @@ -165,6 +166,7 @@

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 Expand Up @@ -193,7 +195,7 @@
}

// Usage
BITCOIN_NETWORK = Environment.GetEnvironmentVariable("BITCOIN_NETWORK");

Check warning on line 198 in src/Helpers/Constants.cs

View workflow job for this annotation

GitHub Actions / build-and-test

Possible null reference assignment.

var minChannelCapacity = GetEnvironmentalVariableOrThrowIfNotTesting("MINIMUM_CHANNEL_CAPACITY_SATS");
if (minChannelCapacity != null) MINIMUM_CHANNEL_CAPACITY_SATS = long.Parse(minChannelCapacity, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture);
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 @@ -390,7 +390,7 @@
TemplatePsbtString="@_templatePSBTString"
SignedPSBT="@_psbt"/>

<CancelOrRejectPopup

Check warning on line 393 in src/Pages/ChannelRequests.razor

View workflow job for this annotation

GitHub Actions / build-and-test

Component 'CancelOrRejectPopup' expects a value for the parameter 'Reason', but a value may not have been provided.

Check warning on line 393 in src/Pages/ChannelRequests.razor

View workflow job for this annotation

GitHub Actions / build-and-test

Component 'CancelOrRejectPopup' expects a value for the parameter 'Reason', but a value may not have been provided.
@ref=@_rejectCancelModalRef
Title='@(_selectedStatusActionString + " operation: " + _selectedRequest?.Id)'
Validator="@RejectReasonValidator"
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
Loading