mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-01-18 13:26:47 +01:00
Merge pull request #6469 from NicolasDorier/validatinglightningclient
Plugins: Add a way for LightningClient to be more customizable (Fix #6467)
This commit is contained in:
commit
cc915df10e
@ -217,17 +217,25 @@ namespace BTCPayServer
|
||||
return endpoint != null;
|
||||
}
|
||||
|
||||
public static Uri GetServerUri(this ILightningClient client)
|
||||
[Obsolete("Use GetServerUri(this ILightningClient client, string connectionString) instead")]
|
||||
public static Uri GetServerUri(this ILightningClient client) => GetServerUri(client, client.ToString());
|
||||
public static Uri GetServerUri(this ILightningClient client, string connectionString)
|
||||
{
|
||||
var kv = LightningConnectionStringHelper.ExtractValues(client.ToString(), out _);
|
||||
|
||||
if (client is IExtendedLightningClient { ServerUri: { } uri })
|
||||
return uri;
|
||||
var kv = client.ExtractValues(connectionString);
|
||||
return !kv.TryGetValue("server", out var server) ? null : new Uri(server, UriKind.Absolute);
|
||||
}
|
||||
|
||||
public static string GetDisplayName(this ILightningClient client)
|
||||
[Obsolete("Use GetDisplayName(this ILightningClient client, string connectionString) instead")]
|
||||
public static string GetDisplayName(this ILightningClient client) => GetDisplayName(client, client.ToString());
|
||||
public static string GetDisplayName(this ILightningClient client, string connectionString)
|
||||
{
|
||||
LightningConnectionStringHelper.ExtractValues(client.ToString(), out var type);
|
||||
|
||||
if (client is IExtendedLightningClient { DisplayName: { } displayName })
|
||||
return displayName;
|
||||
var kv = client.ExtractValues(connectionString);
|
||||
if (!kv.TryGetValue("type", out var type))
|
||||
return "???";
|
||||
var lncType = typeof(LightningConnectionType);
|
||||
var fields = lncType.GetFields(BindingFlags.Public | BindingFlags.Static);
|
||||
var field = fields.FirstOrDefault(f => f.GetValue(lncType)?.ToString() == type);
|
||||
@ -236,9 +244,96 @@ namespace BTCPayServer
|
||||
return attr?.Name ?? type;
|
||||
}
|
||||
|
||||
public static bool IsSafe(this ILightningClient client)
|
||||
private static bool TryParseLegacy(string str, out Dictionary<string, string> connectionString)
|
||||
{
|
||||
var kv = LightningConnectionStringHelper.ExtractValues(client.ToString(), out _);
|
||||
if (str.StartsWith("/"))
|
||||
{
|
||||
str = "unix:" + str;
|
||||
}
|
||||
|
||||
Dictionary<string, string> dictionary = new Dictionary<string, string>();
|
||||
connectionString = null;
|
||||
if (!Uri.TryCreate(str, UriKind.Absolute, out Uri result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!new string[4] { "unix", "tcp", "http", "https" }.Contains(result.Scheme))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.Scheme == "unix")
|
||||
{
|
||||
str = result.AbsoluteUri.Substring("unix:".Length);
|
||||
while (str.Length >= 1 && str[0] == '/')
|
||||
{
|
||||
str = str.Substring(1);
|
||||
}
|
||||
|
||||
result = new Uri("unix://" + str, UriKind.Absolute);
|
||||
dictionary.Add("type", "clightning");
|
||||
}
|
||||
|
||||
if (result.Scheme == "tcp")
|
||||
{
|
||||
dictionary.Add("type", "clightning");
|
||||
}
|
||||
|
||||
if (result.Scheme == "http" || result.Scheme == "https")
|
||||
{
|
||||
string[] array = result.UserInfo.Split(':');
|
||||
if (string.IsNullOrEmpty(result.UserInfo) || array.Length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dictionary.Add("type", "charge");
|
||||
dictionary.Add("username", array[0]);
|
||||
dictionary.Add("password", array[1]);
|
||||
if (result.Scheme == "http")
|
||||
{
|
||||
dictionary.Add("allowinsecure", "true");
|
||||
}
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(result.UserInfo))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
dictionary.Add("server", new UriBuilder(result)
|
||||
{
|
||||
UserName = "",
|
||||
Password = ""
|
||||
}.Uri.ToString());
|
||||
connectionString = dictionary;
|
||||
return true;
|
||||
}
|
||||
|
||||
static Dictionary<string, string> ExtractValues(this ILightningClient client, string connectionString)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(connectionString);
|
||||
if (TryParseLegacy(connectionString, out var legacy))
|
||||
return legacy;
|
||||
string[] source = connectionString.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var kv = new Dictionary<string, string>();
|
||||
foreach (string item in source.Select((string p) => p.Trim()))
|
||||
{
|
||||
int num = item.IndexOf('=');
|
||||
if (num == -1)
|
||||
continue;
|
||||
string text = item.Substring(0, num).Trim().ToLowerInvariant();
|
||||
string value = item.Substring(num + 1).Trim();
|
||||
kv.TryAdd(text, value);
|
||||
}
|
||||
return kv;
|
||||
}
|
||||
|
||||
[Obsolete("Use IsSafe(this ILightningClient client, string connectionString) instead")]
|
||||
public static bool IsSafe(this ILightningClient client) => IsSafe(client, client.ToString());
|
||||
public static bool IsSafe(this ILightningClient client, string connectionString)
|
||||
{
|
||||
var kv = client.ExtractValues(connectionString);
|
||||
if (kv.TryGetValue("cookiefilepath", out _) ||
|
||||
kv.TryGetValue("macaroondirectorypath", out _) ||
|
||||
kv.TryGetValue("macaroonfilepath", out _) )
|
||||
@ -662,6 +757,9 @@ namespace BTCPayServer
|
||||
return controller.View("PostRedirect", redirectVm);
|
||||
}
|
||||
|
||||
public static string RemoveUserInfo(this Uri uri)
|
||||
=> string.IsNullOrEmpty(uri.UserInfo) ? uri.ToString() : uri.ToString().Replace(uri.UserInfo, "***");
|
||||
|
||||
public static DataDirectories Configure(this DataDirectories dataDirectories, IConfiguration configuration)
|
||||
{
|
||||
var networkType = DefaultConfiguration.GetNetworkType(configuration);
|
||||
|
25
BTCPayServer/Payments/Lightning/IExtendedLightningClient.cs
Normal file
25
BTCPayServer/Payments/Lightning/IExtendedLightningClient.cs
Normal file
@ -0,0 +1,25 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Lightning;
|
||||
|
||||
namespace BTCPayServer.Payments.Lightning
|
||||
{
|
||||
public interface IExtendedLightningClient : ILightningClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to validate the client configuration
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<ValidationResult> Validate();
|
||||
/// <summary>
|
||||
/// The display name of this client (ie. LND (REST), Eclair, LNDhub)
|
||||
/// </summary>
|
||||
public string? DisplayName { get; }
|
||||
/// <summary>
|
||||
/// The server URI of this client (ie. http://localhost:8080)
|
||||
/// </summary>
|
||||
public Uri? ServerUri { get; }
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -263,7 +264,16 @@ namespace BTCPayServer.Payments.Lightning
|
||||
try
|
||||
{
|
||||
var client = _lightningClientFactory.Create(config.ConnectionString, _Network);
|
||||
if (!client.IsSafe())
|
||||
if (client is IExtendedLightningClient vlc)
|
||||
{
|
||||
var result = await vlc.Validate();
|
||||
if (result != ValidationResult.Success)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(config.ConnectionString), result?.ErrorMessage ?? "Invalid connection string");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!client.IsSafe(config.ConnectionString))
|
||||
{
|
||||
var canManage = (await validationContext.AuthorizationService.AuthorizeAsync(validationContext.User, null,
|
||||
new PolicyRequirement(Policies.CanModifyServerSettings))).Succeeded;
|
||||
@ -274,6 +284,11 @@ namespace BTCPayServer.Payments.Lightning
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (FormatException ex)
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(config.ConnectionString), ex.Message);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
validationContext.ModelState.AddModelError(nameof(config.ConnectionString), "Invalid connection string");
|
||||
|
@ -500,27 +500,20 @@ retry:
|
||||
public CancellationTokenSource? StopListeningCancellationTokenSource;
|
||||
async Task Listen(CancellationToken cancellation)
|
||||
{
|
||||
Uri? uri = null;
|
||||
string? logUrl = null;
|
||||
string? uri = null;
|
||||
try
|
||||
{
|
||||
var lightningClient = _lightningClientFactory.Create(ConnectionString, _network);
|
||||
if(lightningClient is null)
|
||||
return;
|
||||
uri = lightningClient.GetServerUri();
|
||||
logUrl = uri switch
|
||||
{
|
||||
null when LightningConnectionStringHelper.ExtractValues(ConnectionString, out var type) is not null => type,
|
||||
null => string.Empty,
|
||||
_ => string.IsNullOrEmpty(uri.UserInfo) ? uri.ToString() : uri.ToString().Replace(uri.UserInfo, "***")
|
||||
};
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Start listening {Uri}", _network.CryptoCode, logUrl);
|
||||
uri = lightningClient.GetServerUri(ConnectionString)?.RemoveUserInfo() ?? "";
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Start listening {Uri}", _network.CryptoCode, uri);
|
||||
using var session = await lightningClient.Listen(cancellation);
|
||||
// Just in case the payment arrived after our last poll but before we listened.
|
||||
await PollAllListenedInvoices(cancellation);
|
||||
if (_ErrorAlreadyLogged)
|
||||
{
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Could reconnect successfully to {Uri}", _network.CryptoCode, logUrl);
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Could reconnect successfully to {Uri}", _network.CryptoCode, uri);
|
||||
}
|
||||
_ErrorAlreadyLogged = false;
|
||||
while (!_ListenedInvoices.IsEmpty)
|
||||
@ -552,12 +545,12 @@ retry:
|
||||
catch (Exception ex) when (!cancellation.IsCancellationRequested && !_ErrorAlreadyLogged)
|
||||
{
|
||||
_ErrorAlreadyLogged = true;
|
||||
Logs.PayServer.LogError(ex, "{CryptoCode} (Lightning): Error while contacting {Uri}", _network.CryptoCode, logUrl);
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Stop listening {Uri}", _network.CryptoCode, logUrl);
|
||||
Logs.PayServer.LogError(ex, "{CryptoCode} (Lightning): Error while contacting {Uri}", _network.CryptoCode, uri);
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): Stop listening {Uri}", _network.CryptoCode, uri);
|
||||
}
|
||||
catch (OperationCanceledException) when (cancellation.IsCancellationRequested) { }
|
||||
if (_ListenedInvoices.IsEmpty)
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): No more invoice to listen on {Uri}, releasing the connection", _network.CryptoCode, logUrl);
|
||||
Logs.PayServer.LogInformation("{CryptoCode} (Lightning): No more invoice to listen on {Uri}, releasing the connection", _network.CryptoCode, uri);
|
||||
}
|
||||
|
||||
private uint256? GetPaymentHash(ListenedInvoice listenedInvoice)
|
||||
|
@ -19,8 +19,8 @@
|
||||
@try
|
||||
{
|
||||
var client = LightningClientFactoryService.Create(Model.ConnectionString, NetworkProvider.GetNetwork<BTCPayNetwork>(Model.CryptoCode));
|
||||
<span>@client.GetDisplayName()</span>
|
||||
var uri = client.GetServerUri();
|
||||
<span>@client.GetDisplayName(Model.ConnectionString)</span>
|
||||
var uri = client.GetServerUri(Model.ConnectionString);
|
||||
if (uri is not null)
|
||||
{
|
||||
<span>(@uri.Host)</span>
|
||||
|
@ -23,8 +23,8 @@
|
||||
@try
|
||||
{
|
||||
var client = LightningClientFactoryService.Create(Model.ConnectionString, NetworkProvider.GetNetwork<BTCPayNetwork>(Model.CryptoCode));
|
||||
<span>@client.GetDisplayName()</span>
|
||||
var uri = client.GetServerUri();
|
||||
<span>@client.GetDisplayName(Model.ConnectionString)</span>
|
||||
var uri = client.GetServerUri(Model.ConnectionString);
|
||||
if (uri is not null)
|
||||
{
|
||||
<span>(@uri.Host)</span>
|
||||
|
Loading…
Reference in New Issue
Block a user