2021-06-14 11:19:52 +02:00
#nullable enable
2021-02-26 03:58:51 +01:00
using System.Collections.Generic ;
2021-07-29 13:29:34 +02:00
using System.Diagnostics.CodeAnalysis ;
2021-02-26 03:58:51 +01:00
using System.Linq ;
using System.Threading.Tasks ;
using BTCPayServer.Abstractions.Constants ;
2021-07-27 14:08:54 +02:00
using BTCPayServer.Abstractions.Contracts ;
2022-02-24 09:00:44 +01:00
using BTCPayServer.Abstractions.Extensions ;
2021-02-26 03:58:51 +01:00
using BTCPayServer.Client ;
using BTCPayServer.Client.Models ;
using BTCPayServer.Configuration ;
using BTCPayServer.Data ;
using BTCPayServer.HostedServices ;
using BTCPayServer.Lightning ;
using BTCPayServer.Payments ;
using BTCPayServer.Payments.Lightning ;
using BTCPayServer.Security ;
using BTCPayServer.Services.Stores ;
using Microsoft.AspNetCore.Authorization ;
using Microsoft.AspNetCore.Mvc ;
using Microsoft.Extensions.Options ;
using StoreData = BTCPayServer . Data . StoreData ;
2022-01-14 05:05:23 +01:00
namespace BTCPayServer.Controllers.Greenfield
2021-02-26 03:58:51 +01:00
{
[ApiController]
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
2022-01-07 04:17:59 +01:00
public class GreenfieldStoreLightningNetworkPaymentMethodsController : ControllerBase
2021-02-26 03:58:51 +01:00
{
private StoreData Store = > HttpContext . GetStoreData ( ) ;
private readonly StoreRepository _storeRepository ;
private readonly BTCPayNetworkProvider _btcPayNetworkProvider ;
private readonly IAuthorizationService _authorizationService ;
2021-07-27 14:08:54 +02:00
private readonly ISettingsRepository _settingsRepository ;
2021-02-26 03:58:51 +01:00
2022-01-07 04:17:59 +01:00
public GreenfieldStoreLightningNetworkPaymentMethodsController (
2021-02-26 03:58:51 +01:00
StoreRepository storeRepository ,
BTCPayNetworkProvider btcPayNetworkProvider ,
IAuthorizationService authorizationService ,
2021-07-27 14:08:54 +02:00
ISettingsRepository settingsRepository )
2021-02-26 03:58:51 +01:00
{
_storeRepository = storeRepository ;
_btcPayNetworkProvider = btcPayNetworkProvider ;
_authorizationService = authorizationService ;
2021-07-27 14:08:54 +02:00
_settingsRepository = settingsRepository ;
2021-02-26 03:58:51 +01:00
}
2021-07-26 11:12:44 +02:00
public static IEnumerable < LightningNetworkPaymentMethodData > GetLightningPaymentMethods ( StoreData store ,
BTCPayNetworkProvider networkProvider , bool? enabled )
2021-02-26 03:58:51 +01:00
{
2021-06-02 05:49:58 +02:00
var blob = store . GetStoreBlob ( ) ;
2021-02-26 03:58:51 +01:00
var excludedPaymentMethods = blob . GetExcludedPaymentMethods ( ) ;
2021-06-02 05:49:58 +02:00
return store . GetSupportedPaymentMethods ( networkProvider )
2021-02-26 03:58:51 +01:00
. Where ( ( method ) = > method . PaymentId . PaymentType = = PaymentTypes . LightningLike )
. OfType < LightningSupportedPaymentMethod > ( )
. Select ( paymentMethod = >
2021-06-14 11:19:52 +02:00
new LightningNetworkPaymentMethodData (
paymentMethod . PaymentId . CryptoCode ,
2021-07-26 11:12:44 +02:00
paymentMethod . GetExternalLightningUrl ( ) ? . ToString ( ) ? ?
paymentMethod . GetDisplayableConnectionString ( ) ,
2021-09-25 07:04:34 +02:00
! excludedPaymentMethods . Match ( paymentMethod . PaymentId ) ,
2021-08-16 10:07:49 +02:00
paymentMethod . PaymentId . ToStringNormalized ( ) ,
paymentMethod . DisableBOLT11PaymentOption
2021-06-14 11:19:52 +02:00
)
)
2021-07-26 11:12:44 +02:00
. Where ( ( result ) = > enabled is null | | enabled = = result . Enabled )
2021-06-02 05:49:58 +02:00
. ToList ( ) ;
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork")]
public ActionResult < IEnumerable < LightningNetworkPaymentMethodData > > GetLightningPaymentMethods (
2021-07-27 14:11:47 +02:00
string storeId ,
2021-07-26 11:12:44 +02:00
[FromQuery] bool? enabled )
2021-06-02 05:49:58 +02:00
{
2021-07-26 11:12:44 +02:00
return Ok ( GetLightningPaymentMethods ( Store , _btcPayNetworkProvider , enabled ) ) ;
2021-02-26 03:58:51 +01:00
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpGet("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
2021-07-27 14:11:47 +02:00
public ActionResult < LightningNetworkPaymentMethodData > GetLightningNetworkPaymentMethod ( string storeId , string cryptoCode )
2021-02-26 03:58:51 +01:00
{
2021-12-16 15:04:06 +01:00
AssertSupportLightning ( cryptoCode ) ;
2021-02-26 03:58:51 +01:00
2021-10-25 08:18:02 +02:00
var method = GetExistingLightningLikePaymentMethod ( _btcPayNetworkProvider , cryptoCode , Store ) ;
2021-02-26 03:58:51 +01:00
if ( method is null )
{
2021-12-16 15:04:06 +01:00
throw ErrorPaymentMethodNotConfigured ( ) ;
2021-02-26 03:58:51 +01:00
}
2021-07-26 11:12:44 +02:00
2021-02-26 03:58:51 +01:00
return Ok ( method ) ;
}
2021-07-26 11:12:44 +02:00
2021-12-16 15:04:06 +01:00
protected JsonHttpException ErrorPaymentMethodNotConfigured ( )
{
return new JsonHttpException ( this . CreateAPIError ( 404 , "paymentmethod-not-configured" , "The lightning payment method is not set up" ) ) ;
}
2021-02-26 03:58:51 +01:00
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpDelete("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
public async Task < IActionResult > RemoveLightningNetworkPaymentMethod (
2021-07-27 14:11:47 +02:00
string storeId ,
2021-10-25 08:18:02 +02:00
string cryptoCode )
2021-02-26 03:58:51 +01:00
{
2021-12-16 15:04:06 +01:00
AssertSupportLightning ( cryptoCode ) ;
2021-07-26 11:12:44 +02:00
2021-02-26 03:58:51 +01:00
var id = new PaymentMethodId ( cryptoCode , PaymentTypes . LightningLike ) ;
var store = Store ;
store . SetSupportedPaymentMethod ( id , null ) ;
await _storeRepository . UpdateStore ( store ) ;
return Ok ( ) ;
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
[HttpPut("~/api/v1/stores/{storeId}/payment-methods/LightningNetwork/{cryptoCode}")]
2021-07-27 14:11:47 +02:00
public async Task < IActionResult > UpdateLightningNetworkPaymentMethod ( string storeId , string cryptoCode ,
2021-08-30 06:38:57 +02:00
[FromBody] UpdateLightningNetworkPaymentMethodRequest request )
2021-02-26 03:58:51 +01:00
{
var paymentMethodId = new PaymentMethodId ( cryptoCode , PaymentTypes . LightningLike ) ;
2021-12-16 15:04:06 +01:00
AssertSupportLightning ( cryptoCode ) ;
2021-02-26 03:58:51 +01:00
2021-08-30 06:38:57 +02:00
if ( string . IsNullOrEmpty ( request . ConnectionString ) )
2021-02-26 03:58:51 +01:00
{
ModelState . AddModelError ( nameof ( LightningNetworkPaymentMethodData . ConnectionString ) ,
"Missing connectionString" ) ;
}
if ( ! ModelState . IsValid )
return this . CreateValidationError ( ModelState ) ;
2021-07-29 13:29:34 +02:00
LightningSupportedPaymentMethod ? paymentMethod = null ;
2022-01-11 15:38:05 +01:00
var store = Store ;
var storeBlob = store . GetStoreBlob ( ) ;
var existing = GetExistingLightningLikePaymentMethod ( _btcPayNetworkProvider , cryptoCode , store ) ;
if ( existing = = null | | existing . ConnectionString ! = request . ConnectionString )
2021-02-26 03:58:51 +01:00
{
2021-08-30 06:38:57 +02:00
if ( request . ConnectionString = = LightningSupportedPaymentMethod . InternalNode )
2021-02-26 03:58:51 +01:00
{
2021-03-02 03:11:58 +01:00
if ( ! await CanUseInternalLightning ( ) )
2021-02-26 03:58:51 +01:00
{
2022-01-11 15:38:05 +01:00
return this . CreateAPIPermissionError ( Policies . CanUseInternalLightningNode , $"You are not authorized to use the internal lightning node. Either add '{Policies.CanUseInternalLightningNode}' to an API Key, or allow non-admin users to use the internal lightning node in the server settings." ) ;
2021-02-26 03:58:51 +01:00
}
2021-07-26 11:12:44 +02:00
2021-03-02 03:11:58 +01:00
paymentMethod = new Payments . Lightning . LightningSupportedPaymentMethod ( )
{
CryptoCode = paymentMethodId . CryptoCode
} ;
paymentMethod . SetInternalNode ( ) ;
2021-02-26 03:58:51 +01:00
}
2021-03-02 03:11:58 +01:00
else
2021-02-26 03:58:51 +01:00
{
2021-08-30 06:38:57 +02:00
if ( ! LightningConnectionString . TryParse ( request . ConnectionString , false ,
2021-03-02 03:11:58 +01:00
out var connectionString , out var error ) )
2021-02-26 03:58:51 +01:00
{
2021-08-30 06:38:57 +02:00
ModelState . AddModelError ( nameof ( request . ConnectionString ) , $"Invalid URL ({error})" ) ;
2021-02-26 03:58:51 +01:00
return this . CreateValidationError ( ModelState ) ;
}
2021-07-26 11:12:44 +02:00
2021-03-02 03:11:58 +01:00
if ( connectionString . ConnectionType = = LightningConnectionType . LndGRPC )
2021-02-26 03:58:51 +01:00
{
2021-08-30 06:38:57 +02:00
ModelState . AddModelError ( nameof ( request . ConnectionString ) ,
2021-03-02 03:11:58 +01:00
$"BTCPay does not support gRPC connections" ) ;
2021-02-26 03:58:51 +01:00
return this . CreateValidationError ( ModelState ) ;
}
2021-07-26 11:12:44 +02:00
2021-03-02 03:11:58 +01:00
if ( ! await CanManageServer ( ) & & ! connectionString . IsSafe ( ) )
2021-02-26 03:58:51 +01:00
{
2021-08-30 06:38:57 +02:00
ModelState . AddModelError ( nameof ( request . ConnectionString ) ,
2021-07-26 11:12:44 +02:00
$"You do not have 'btcpay.server.canmodifyserversettings' rights, so the connection string should not contain 'cookiefilepath', 'macaroondirectorypath', 'macaroonfilepath', and should not point to a local ip or to a dns name ending with '.internal', '.local', '.lan' or '.'." ) ;
2021-02-26 03:58:51 +01:00
return this . CreateValidationError ( ModelState ) ;
}
2021-07-26 11:12:44 +02:00
2021-03-02 03:11:58 +01:00
paymentMethod = new Payments . Lightning . LightningSupportedPaymentMethod ( )
{
CryptoCode = paymentMethodId . CryptoCode
} ;
paymentMethod . SetLightningUrl ( connectionString ) ;
2021-02-26 03:58:51 +01:00
}
}
store . SetSupportedPaymentMethod ( paymentMethodId , paymentMethod ) ;
2021-08-30 06:38:57 +02:00
storeBlob . SetExcluded ( paymentMethodId , ! request . Enabled ) ;
2021-02-26 03:58:51 +01:00
store . SetStoreBlob ( storeBlob ) ;
await _storeRepository . UpdateStore ( store ) ;
2021-10-25 08:18:02 +02:00
return Ok ( GetExistingLightningLikePaymentMethod ( _btcPayNetworkProvider , cryptoCode , store ) ) ;
2021-02-26 03:58:51 +01:00
}
2021-10-25 08:18:02 +02:00
public static LightningNetworkPaymentMethodData ? GetExistingLightningLikePaymentMethod ( BTCPayNetworkProvider btcPayNetworkProvider , string cryptoCode ,
StoreData store )
2021-02-26 03:58:51 +01:00
{
2021-10-25 08:18:02 +02:00
2021-02-26 03:58:51 +01:00
var storeBlob = store . GetStoreBlob ( ) ;
var id = new PaymentMethodId ( cryptoCode , PaymentTypes . LightningLike ) ;
var paymentMethod = store
2021-10-25 08:18:02 +02:00
. GetSupportedPaymentMethods ( btcPayNetworkProvider )
2021-02-26 03:58:51 +01:00
. OfType < LightningSupportedPaymentMethod > ( )
. FirstOrDefault ( method = > method . PaymentId = = id ) ;
var excluded = storeBlob . IsExcluded ( id ) ;
2021-07-29 13:29:34 +02:00
return paymentMethod is null
2021-02-26 03:58:51 +01:00
? null
: new LightningNetworkPaymentMethodData ( paymentMethod . PaymentId . CryptoCode ,
2021-12-31 08:59:02 +01:00
paymentMethod . GetDisplayableConnectionString ( ) , ! excluded ,
2021-08-16 10:07:49 +02:00
paymentMethod . PaymentId . ToStringNormalized ( ) , paymentMethod . DisableBOLT11PaymentOption ) ;
2021-02-26 03:58:51 +01:00
}
2021-12-16 15:04:06 +01:00
private BTCPayNetwork AssertSupportLightning ( string cryptoCode )
2021-02-26 03:58:51 +01:00
{
2021-12-16 15:04:06 +01:00
var network = _btcPayNetworkProvider . GetNetwork < BTCPayNetwork > ( cryptoCode ) ;
if ( network is null )
throw new JsonHttpException ( this . CreateAPIError ( 404 , "unknown-cryptocode" , "This crypto code isn't set up in this BTCPay Server instance" ) ) ;
if ( ! ( network . SupportLightning is true ) )
throw new JsonHttpException ( this . CreateAPIError ( 404 , "unknown-cryptocode" , "This crypto code doesn't support lightning" ) ) ;
return network ;
2021-02-26 03:58:51 +01:00
}
2021-07-26 11:12:44 +02:00
2021-02-26 03:58:51 +01:00
private async Task < bool > CanUseInternalLightning ( )
{
2021-07-27 14:08:54 +02:00
return ( await _settingsRepository . GetPolicies ( ) ) . AllowLightningInternalNodeForAll | |
2021-07-26 11:12:44 +02:00
( await _authorizationService . AuthorizeAsync ( User , null ,
new PolicyRequirement ( Policies . CanUseInternalLightningNode ) ) ) . Succeeded ;
2021-02-26 03:58:51 +01:00
}
2021-07-26 11:12:44 +02:00
2021-03-02 03:11:58 +01:00
private async Task < bool > CanManageServer ( )
{
2021-07-26 11:12:44 +02:00
return
2021-03-02 03:11:58 +01:00
( await _authorizationService . AuthorizeAsync ( User , null ,
new PolicyRequirement ( Policies . CanModifyServerSettings ) ) ) . Succeeded ;
}
2021-02-26 03:58:51 +01:00
}
}