Compare commits
17 Commits
v1.2.3
...
v1.2.4-wip
Author | SHA1 | Date | |
---|---|---|---|
858da35539 | |||
ed52a1012a | |||
8ee0a97e9c | |||
02195ed096 | |||
562b96cf1f | |||
f44b54bc59 | |||
1889dec567 | |||
aa953ad6a6 | |||
6a7d434c89 | |||
cc812f96c9 | |||
6a4cb0e95c | |||
4c7149de95 | |||
532e847cdd | |||
3e3d4aea03 | |||
55bfecd4ed | |||
2e4d1f6d37 | |||
a3338b6f80 |
@ -27,7 +27,7 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NBitcoin" Version="6.0.8" />
|
||||
<PackageReference Include="NBitcoin" Version="6.0.12" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
</ItemGroup>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.1.0" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.1.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Compile Remove="Altcoins\**\*.cs"></Compile>
|
||||
|
@ -505,9 +505,11 @@ namespace BTCPayServer.Controllers
|
||||
var paymentMethodDetails = paymentMethod.GetPaymentMethodDetails();
|
||||
if (!paymentMethodDetails.Activated)
|
||||
{
|
||||
await _InvoiceRepository.ActivateInvoicePaymentMethod(_EventAggregator, _NetworkProvider,
|
||||
_paymentMethodHandlerDictionary, store, invoice, paymentMethod.GetId());
|
||||
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
|
||||
if (await _InvoiceRepository.ActivateInvoicePaymentMethod(_EventAggregator, _NetworkProvider,
|
||||
_paymentMethodHandlerDictionary, store, invoice, paymentMethod.GetId()))
|
||||
{
|
||||
return await GetInvoiceModel(invoiceId, paymentMethodId, lang);
|
||||
}
|
||||
}
|
||||
var dto = invoice.EntityToDTO();
|
||||
var storeBlob = store.GetStoreBlob();
|
||||
|
@ -195,7 +195,25 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private void AdjustVMForAuthorization(AuthorizeApiKeysViewModel vm)
|
||||
{
|
||||
var parsedPermissions = Permission.ToPermissions(vm.Permissions?.Split(';')??Array.Empty<string>()).GroupBy(permission => permission.Policy);
|
||||
var permissions = vm.Permissions?.Split(';') ?? Array.Empty<string>();
|
||||
var permissionsWithStoreIDs = new List<string>();
|
||||
/**
|
||||
* Go over each permission and associated store IDs and
|
||||
* join them so that permission for a specific store is parsed correctly
|
||||
*/
|
||||
for (var i = 0; i < permissions.Length; i++) {
|
||||
var currPerm = permissions[i];
|
||||
var storeIds = vm.PermissionValues[i].SpecificStores.ToArray();
|
||||
if (storeIds.Length > 0) {
|
||||
for (var x = 0; x < storeIds.Length; x++) {
|
||||
permissionsWithStoreIDs.Add($"{currPerm}:{storeIds[x]}");
|
||||
}
|
||||
} else {
|
||||
permissionsWithStoreIDs.Add(currPerm);
|
||||
}
|
||||
}
|
||||
|
||||
var parsedPermissions = Permission.ToPermissions(permissionsWithStoreIDs.ToArray()).GroupBy(permission => permission.Policy);
|
||||
|
||||
for (var index = vm.PermissionValues.Count - 1; index >= 0; index--)
|
||||
{
|
||||
@ -210,6 +228,14 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
else if (wanted?.Any() ?? false)
|
||||
{
|
||||
var commandParts = vm.Command?.Split(':', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||||
var command = commandParts.Length > 1 ? commandParts[1] : null;
|
||||
var isPerformingAnAction = command == "change-store-mode" || command == "add-store";
|
||||
// Don't want to accidentally change mode for the user if they are explicitly performing some action
|
||||
if (isPerformingAnAction) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (vm.SelectiveStores && Policies.IsStorePolicy(permissionValue.Permission) &&
|
||||
wanted.Any(permission => !string.IsNullOrEmpty(permission.Scope)))
|
||||
{
|
||||
@ -358,6 +384,12 @@ namespace BTCPayServer.Controllers
|
||||
permissionValueItem.StoreMode = permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific
|
||||
? AddApiKeyViewModel.ApiKeyStoreMode.AllStores
|
||||
: AddApiKeyViewModel.ApiKeyStoreMode.Specific;
|
||||
// Reset values for "all stores" option to their original values
|
||||
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||
{
|
||||
permissionValueItem.SpecificStores = new List<string>();
|
||||
permissionValueItem.Value = true;
|
||||
}
|
||||
|
||||
if (permissionValueItem.StoreMode == AddApiKeyViewModel.ApiKeyStoreMode.Specific &&
|
||||
!permissionValueItem.SpecificStores.Any() && viewModel.Stores.Any())
|
||||
|
@ -1154,20 +1154,19 @@ namespace BTCPayServer.Controllers
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Wallet settings updated";
|
||||
return RedirectToAction(nameof(WalletSettings));
|
||||
}
|
||||
else if (command == "prune")
|
||||
else if (command == "clear")
|
||||
{
|
||||
var result = await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
|
||||
.PruneAsync(derivationScheme.AccountDerivation, new PruneRequest(), cancellationToken);
|
||||
if (result.TotalPruned == 0)
|
||||
if (Version.TryParse(_dashboard.Get(walletId.CryptoCode)?.Status?.Version ?? "0.0.0.0", out var v) &&
|
||||
v < new Version(2, 2, 4))
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The wallet is already pruned";
|
||||
TempData[WellKnownTempData.ErrorMessage] = $"This version of NBXplorer doesn't support this operation, please upgrade to 2.2.4 or above";
|
||||
}
|
||||
else
|
||||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] =
|
||||
$"The wallet has been successfully pruned ({result.TotalPruned} transactions have been removed from the history)";
|
||||
await ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode)
|
||||
.WipeAsync(derivationScheme.AccountDerivation, cancellationToken);
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"The transactions have been wiped out, to restore your balance, rescan the wallet.";
|
||||
}
|
||||
|
||||
return RedirectToAction(nameof(WalletSettings));
|
||||
}
|
||||
else if (command == "view-seed" && await CanUseHotWallet())
|
||||
|
@ -52,7 +52,7 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
||||
|
||||
public bool CanHandle(PaymentMethodId paymentMethod)
|
||||
{
|
||||
return paymentMethod.PaymentType == BitcoinPaymentType.Instance &&
|
||||
return paymentMethod?.PaymentType == BitcoinPaymentType.Instance &&
|
||||
_btcPayNetworkProvider.GetNetwork<BTCPayNetwork>(paymentMethod.CryptoCode)?.ReadonlyWallet is false;
|
||||
}
|
||||
|
||||
@ -89,6 +89,10 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
||||
if (payout?.Proof is null)
|
||||
return null;
|
||||
var paymentMethodId = payout.GetPaymentMethodId();
|
||||
if (paymentMethodId is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var raw = JObject.Parse(Encoding.UTF8.GetString(payout.Proof));
|
||||
if (raw.TryGetValue("proofType", StringComparison.InvariantCultureIgnoreCase, out var proofType) &&
|
||||
proofType.Value<string>() == ManualPayoutProof.Type)
|
||||
@ -161,7 +165,9 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
||||
.Include(p => p.PullPaymentData.StoreData)
|
||||
.Where(p => payoutIds.Contains(p.Id))
|
||||
.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingPayment)
|
||||
.ToListAsync()).Where(data => CanHandle(PaymentMethodId.Parse(data.PaymentMethodId)))
|
||||
.ToListAsync()).Where(data =>
|
||||
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
||||
CanHandle(paymentMethodId))
|
||||
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple=> tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == false);
|
||||
foreach (var valueTuple in payouts)
|
||||
{
|
||||
@ -185,7 +191,9 @@ public class BitcoinLikePayoutHandler : IPayoutHandler
|
||||
.Include(p => p.PullPaymentData.StoreData)
|
||||
.Where(p => payoutIds.Contains(p.Id))
|
||||
.Where(p => p.PullPaymentData.StoreId == storeId && !p.PullPaymentData.Archived && p.State == PayoutState.AwaitingPayment)
|
||||
.ToListAsync()).Where(data => CanHandle(PaymentMethodId.Parse(data.PaymentMethodId)))
|
||||
.ToListAsync()).Where(data =>
|
||||
PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId) &&
|
||||
CanHandle(paymentMethodId))
|
||||
.Select(data => (data, ParseProof(data) as PayoutTransactionOnChainBlob)).Where(tuple=> tuple.Item2 != null && tuple.Item2.TransactionId != null && tuple.Item2.Accounted == true);
|
||||
foreach (var valueTuple in payouts)
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ namespace BTCPayServer.Data
|
||||
|
||||
public static PaymentMethodId GetPaymentMethodId(this PayoutData data)
|
||||
{
|
||||
return PaymentMethodId.Parse(data.PaymentMethodId);
|
||||
return PaymentMethodId.TryParse(data.PaymentMethodId, out var paymentMethodId)? paymentMethodId : null;
|
||||
}
|
||||
public static PayoutBlob GetBlob(this PayoutData data, BTCPayNetworkJsonSerializerSettings serializers)
|
||||
{
|
||||
|
@ -171,7 +171,8 @@ namespace BTCPayServer.Data
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
if (ExcludedPaymentMethods == null || ExcludedPaymentMethods.Length == 0)
|
||||
return PaymentFilter.Never();
|
||||
return PaymentFilter.Any(ExcludedPaymentMethods.Select(p => PaymentFilter.WhereIs(PaymentMethodId.Parse(p))).ToArray());
|
||||
|
||||
return PaymentFilter.Any(ExcludedPaymentMethods.ParsePaymentMethodIds().Select(PaymentFilter.WhereIs).ToArray());
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,8 @@ namespace BTCPayServer.Data
|
||||
public static PaymentMethodId[] GetEnabledPaymentIds(this StoreData storeData, BTCPayNetworkProvider networks)
|
||||
{
|
||||
var excludeFilter = storeData.GetStoreBlob().GetExcludedPaymentMethods();
|
||||
var paymentMethodIds = storeData.GetSupportedPaymentMethods(networks).Select(p => p.PaymentId)
|
||||
var paymentMethodIds = storeData.GetSupportedPaymentMethods(networks)
|
||||
.Select(p => p.PaymentId)
|
||||
.Where(a => !excludeFilter.Match(a))
|
||||
.OrderByDescending(a => a.CryptoCode == "BTC")
|
||||
.ThenBy(a => a.CryptoCode)
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
@ -482,6 +483,15 @@ namespace BTCPayServer
|
||||
"Supported chains: " + String.Join(',', supportedChains.ToArray()));
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PaymentMethodId[] ParsePaymentMethodIds(this string[] paymentMethods)
|
||||
{
|
||||
return paymentMethods.Select(s =>
|
||||
{
|
||||
PaymentMethodId.TryParse(s, out var parsed);
|
||||
return parsed;
|
||||
}).Where(id => id != null).ToArray();
|
||||
}
|
||||
|
||||
public static DataDirectories Configure(this DataDirectories dataDirectories, IConfiguration configuration)
|
||||
{
|
||||
|
@ -286,8 +286,13 @@ namespace BTCPayServer.HostedServices
|
||||
req.Completion.SetResult(PayoutApproval.Result.OldRevision);
|
||||
return;
|
||||
}
|
||||
if (!PaymentMethodId.TryParse(payout.PaymentMethodId, out var paymentMethod))
|
||||
{
|
||||
req.Completion.SetResult(PayoutApproval.Result.NotFound);
|
||||
return;
|
||||
}
|
||||
payout.State = PayoutState.AwaitingPayment;
|
||||
var paymentMethod = PaymentMethodId.Parse(payout.PaymentMethodId);
|
||||
|
||||
if (paymentMethod.CryptoCode == payout.PullPaymentData.GetBlob().Currency)
|
||||
req.Rate = 1.0m;
|
||||
var cryptoAmount = payoutBlob.Amount / req.Rate;
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NBitcoin.Crypto;
|
||||
|
||||
namespace BTCPayServer.Security
|
||||
{
|
||||
@ -69,6 +70,35 @@ namespace BTCPayServer.Security
|
||||
}
|
||||
|
||||
readonly HashSet<ConsentSecurityPolicy> _Policies = new HashSet<ConsentSecurityPolicy>();
|
||||
|
||||
/// <summary>
|
||||
/// Allow a specific script as event handler
|
||||
/// </summary>
|
||||
/// <param name="script"></param>
|
||||
public void AllowUnsafeHashes(string script)
|
||||
{
|
||||
if (script is null)
|
||||
throw new ArgumentNullException(nameof(script));
|
||||
var sha = GetSha256(script);
|
||||
Add("script-src", $"'unsafe-hashes'");
|
||||
Add("script-src", $"'sha256-{sha}'");
|
||||
}
|
||||
/// <summary>
|
||||
/// Allow the injection of script tag with the following script
|
||||
/// </summary>
|
||||
/// <param name="script"></param>
|
||||
public void AllowInline(string script)
|
||||
{
|
||||
if (script is null)
|
||||
throw new ArgumentNullException(nameof(script));
|
||||
var sha = GetSha256(script);
|
||||
Add("script-src", $"'sha256-{sha}'");
|
||||
}
|
||||
static string GetSha256(string script)
|
||||
{
|
||||
return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal))));
|
||||
}
|
||||
|
||||
public void Add(string name, string value)
|
||||
{
|
||||
Add(new ConsentSecurityPolicy(name, value));
|
||||
|
@ -289,7 +289,10 @@ namespace BTCPayServer.Services.Invoices
|
||||
JObject strategies = JObject.Parse(DerivationStrategies);
|
||||
foreach (var strat in strategies.Properties())
|
||||
{
|
||||
var paymentMethodId = PaymentMethodId.Parse(strat.Name);
|
||||
if (!PaymentMethodId.TryParse(strat.Name, out var paymentMethodId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
var network = Networks.GetNetwork<BTCPayNetworkBase>(paymentMethodId.CryptoCode);
|
||||
if (network != null)
|
||||
{
|
||||
|
@ -10,11 +10,13 @@ namespace BTCPayServer.Services.Invoices
|
||||
{
|
||||
public static class InvoiceExtensions
|
||||
{
|
||||
|
||||
public static async Task ActivateInvoicePaymentMethod(this InvoiceRepository invoiceRepository,
|
||||
public static async Task<bool> ActivateInvoicePaymentMethod(this InvoiceRepository invoiceRepository,
|
||||
EventAggregator eventAggregator, BTCPayNetworkProvider btcPayNetworkProvider, PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
|
||||
StoreData store,InvoiceEntity invoice, PaymentMethodId paymentMethodId)
|
||||
{
|
||||
if (invoice.GetInvoiceState().Status != InvoiceStatusLegacy.New)
|
||||
return false;
|
||||
bool success = false;
|
||||
var eligibleMethodToActivate = invoice.GetPaymentMethod(paymentMethodId);
|
||||
if (!eligibleMethodToActivate.GetPaymentMethodDetails().Activated)
|
||||
{
|
||||
@ -34,6 +36,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
eligibleMethodToActivate.SetPaymentMethodDetails(newDetails);
|
||||
await invoiceRepository.UpdateInvoicePaymentMethod(invoice.Id, eligibleMethodToActivate);
|
||||
eventAggregator.Publish(new InvoicePaymentMethodActivated(paymentMethodId, invoice));
|
||||
eventAggregator.Publish(new InvoiceNeedUpdateEvent(invoice.Id));
|
||||
success = true;
|
||||
}
|
||||
catch (PaymentMethodUnavailableException ex)
|
||||
{
|
||||
@ -45,8 +49,8 @@ namespace BTCPayServer.Services.Invoices
|
||||
}
|
||||
|
||||
await invoiceRepository.AddInvoiceLogs(invoice.Id, logs);
|
||||
eventAggregator.Publish(new InvoiceNeedUpdateEvent(invoice.Id));
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,6 @@ namespace BTCPayServer.TagHelpers
|
||||
[HtmlTargetElement(Attributes = "onkeypress")]
|
||||
[HtmlTargetElement(Attributes = "onchange")]
|
||||
[HtmlTargetElement(Attributes = "onsubmit")]
|
||||
[HtmlTargetElement(Attributes = "href")]
|
||||
public class CSPEventTagHelper : TagHelper
|
||||
{
|
||||
public const string EventNames = "onclick,onkeypress,onchange,onsubmit";
|
||||
@ -91,29 +90,55 @@ namespace BTCPayServer.TagHelpers
|
||||
var n = attr.Name.ToLowerInvariant();
|
||||
if (EventSet.Contains(n))
|
||||
{
|
||||
Allow(attr.Value.ToString());
|
||||
}
|
||||
else if (n == "href")
|
||||
{
|
||||
var v = attr.Value.ToString();
|
||||
if (v.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Allow(v);
|
||||
}
|
||||
_csp.AllowUnsafeHashes(attr.Value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Allow(string v)
|
||||
|
||||
/// <summary>
|
||||
/// Add sha256- to allow inline event handlers in CSP
|
||||
/// </summary>
|
||||
[HtmlTargetElement("template", Attributes = "csp-allow")]
|
||||
public class CSPTemplate : TagHelper
|
||||
{
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
public CSPTemplate(ContentSecurityPolicies csp)
|
||||
{
|
||||
var sha = GetSha256(v);
|
||||
_csp.Add("script-src", $"'unsafe-hashes'");
|
||||
_csp.Add("script-src", $"'sha256-{sha}'");
|
||||
_csp = csp;
|
||||
}
|
||||
|
||||
public static string GetSha256(string script)
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
return Convert.ToBase64String(Hashes.SHA256(Encoding.UTF8.GetBytes(script.Replace("\r\n", "\n", StringComparison.Ordinal))));
|
||||
output.Attributes.RemoveAll("csp-allow");
|
||||
var childContent = await output.GetChildContentAsync();
|
||||
var content = childContent.GetContent();
|
||||
_csp.AllowInline(content);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add sha256- to allow inline event handlers in a:href=javascript:
|
||||
/// </summary>
|
||||
[HtmlTargetElement("a", Attributes = "csp-allow")]
|
||||
public class CSPA : TagHelper
|
||||
{
|
||||
private readonly ContentSecurityPolicies _csp;
|
||||
public CSPA(ContentSecurityPolicies csp)
|
||||
{
|
||||
_csp = csp;
|
||||
}
|
||||
public override void Process(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
output.Attributes.RemoveAll("csp-allow");
|
||||
if (output.Attributes.TryGetAttribute("href", out var attr))
|
||||
{
|
||||
var v = attr.Value.ToString();
|
||||
if (v.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_csp.AllowInline(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
@inject BTCPayServer.Security.ContentSecurityPolicies csp
|
||||
@{
|
||||
Layout = null;
|
||||
csp.Add("script-src", "https://cdn.jsdelivr.net");
|
||||
csp.Add("worker-src", "blob:");
|
||||
}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -25,6 +28,6 @@
|
||||
@*Ignore this, this is for making the test ClickOnAllSideMenus happy*@
|
||||
<div class="navbar-brand" style="visibility:collapse;"></div>
|
||||
<redoc spec-url="@Url.ActionLink("Swagger")"></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.45/bundles/redoc.standalone.js" integrity="sha384-RC31+q3tyqdcilXYaU++ii/FAByqeZ+sjKUHMJ8hMzIY5k4kzNqi4Ett88EZ/4lq" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@2.0.0-rc.54/bundles/redoc.standalone.js" integrity="sha384-pxWFJkxrlfignEDb+sJ8XrdnJQ+V2bsiRqgPnfmOk1i3KKSubbydbolVZJeKisNY" crossorigin="anonymous"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -64,7 +64,7 @@
|
||||
@if (Model.PermissionValues[i].StoreMode == ManageController.AddApiKeyViewModel.ApiKeyStoreMode.AllStores)
|
||||
{
|
||||
<div class="list-group-item form-group">
|
||||
<div class="form-check">
|
||||
<div class="form-check d-flex">
|
||||
@if (Model.Strict || Model.PermissionValues[i].Forbidden)
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="hidden" asp-for="PermissionValues[i].Value"/>
|
||||
@ -74,10 +74,10 @@
|
||||
{
|
||||
<input id="@Model.PermissionValues[i].Permission" type="checkbox" asp-for="PermissionValues[i].Value" class="form-check-input"/>
|
||||
}
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label">@Model.PermissionValues[i].Title</label>
|
||||
<label for="@Model.PermissionValues[i].Permission" class="h5 form-check-label m-0 me-4 ms-2">@Model.PermissionValues[i].Title</label>
|
||||
@if (Model.SelectiveStores)
|
||||
{
|
||||
<button type="submit" class="btn btn-link" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
|
||||
<button type="submit" class="btn btn-link p-0 me-4" name="command" value="@($"{Model.PermissionValues[i].Permission}:change-store-mode")">select specific stores...</button>
|
||||
}
|
||||
|
||||
@if (Model.PermissionValues[i].Forbidden)
|
||||
|
@ -1,7 +1,9 @@
|
||||
@inject BTCPayServer.Security.ContentSecurityPolicies csp
|
||||
@model PayButtonViewModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.PayButton, "Pay Button", Context.GetStoreData().StoreName);
|
||||
csp.AllowUnsafeHashes("onBTCPayFormSubmit(event);return false");
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
@ -14,6 +16,24 @@
|
||||
<script src="~/vendor/vuejs-vee-validate/vee-validate.js" asp-append-version="true"></script>
|
||||
<script src="~/vendor/clipboard.js/clipboard.js" asp-append-version="true"></script>
|
||||
<script src="~/paybutton/paybutton.js" asp-append-version="true"></script>
|
||||
<template id="template-get-scripts" csp-allow>
|
||||
if (!window.btcpay) {
|
||||
var script = document.createElement('script');
|
||||
script.src=@(Safe.Json(Model.UrlRoot + "modal/btcpay.js"));
|
||||
document.getElementsByTagName('head')[0].append(script);
|
||||
}
|
||||
function onBTCPayFormSubmit(event) {
|
||||
event.preventDefault();
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function() {
|
||||
if (this.readyState == 4 && this.status == 200 && this.responseText) {
|
||||
window.btcpay.showInvoice(JSON.parse(this.responseText).invoiceId);
|
||||
}
|
||||
};
|
||||
xhttp.open('POST', event.target.getAttribute('action'), true);
|
||||
xhttp.send(new FormData(event.target));
|
||||
}
|
||||
</template>
|
||||
<script>
|
||||
var srvModel = @Safe.Json(Model);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
@using Newtonsoft.Json
|
||||
@using Newtonsoft.Json
|
||||
@using System.Text
|
||||
@using NBitcoin.DataEncoders
|
||||
@model WalletSettingsViewModel
|
||||
@ -70,7 +70,7 @@
|
||||
Other actions...
|
||||
</button>
|
||||
<div class="dropdown-menu" aria-labelledby="OtherActionsDropdownToggle">
|
||||
<button name="command" type="submit" class="dropdown-item" value="prune">Prune old transactions from history</button>
|
||||
<button name="command" type="submit" class="dropdown-item" value="clear">Clear all transactions from history</button>
|
||||
@if (Model.NBXSeedAvailable)
|
||||
{
|
||||
<button name="command" type="submit" class="dropdown-item" value="view-seed">View seed</button>
|
||||
|
@ -42,33 +42,11 @@ function getStyles (styles) {
|
||||
}
|
||||
|
||||
function getScripts(srvModel) {
|
||||
return ""+
|
||||
"<script>" +
|
||||
"if(!window.btcpay){ " +
|
||||
" var head = document.getElementsByTagName('head')[0];" +
|
||||
" var script = document.createElement('script');" +
|
||||
" script.src='"+esc(srvModel.urlRoot)+"modal/btcpay.js';" +
|
||||
" script.type = 'text/javascript';" +
|
||||
" head.append(script);" +
|
||||
"}" +
|
||||
"function onBTCPayFormSubmit(event){" +
|
||||
" var xhttp = new XMLHttpRequest();" +
|
||||
" xhttp.onreadystatechange = function() {" +
|
||||
" if (this.readyState == 4 && this.status == 200) {" +
|
||||
" if(this.status == 200 && this.responseText){" +
|
||||
" var response = JSON.parse(this.responseText);" +
|
||||
" window.btcpay.showInvoice(response.invoiceId);" +
|
||||
" }" +
|
||||
" }" +
|
||||
" };" +
|
||||
" xhttp.open(\"POST\", event.target.getAttribute('action'), true);" +
|
||||
" xhttp.send(new FormData( event.target ));" +
|
||||
"}" +
|
||||
"</script>";
|
||||
if (!srvModel.useModal) return ''
|
||||
const template = document.getElementById('template-get-scripts')
|
||||
return template.innerHTML.replace(/&/g, '&')
|
||||
}
|
||||
|
||||
|
||||
|
||||
function inputChanges(event, buttonSize) {
|
||||
if (buttonSize !== null && buttonSize !== undefined) {
|
||||
srvModel.buttonSize = buttonSize;
|
||||
@ -115,12 +93,10 @@ function inputChanges(event, buttonSize) {
|
||||
}
|
||||
|
||||
var html =
|
||||
//Scripts
|
||||
(srvModel.useModal? getScripts(srvModel) :"") +
|
||||
// Styles
|
||||
getStyles('template-paybutton-styles') + (isSlider ? getStyles('template-slider-styles') : '') +
|
||||
// Form
|
||||
'<form method="POST" '+ ( srvModel.useModal? ' onsubmit="onBTCPayFormSubmit(event);return false" ' : '' )+' action="' + esc(srvModel.urlRoot) + actionUrl + '" class="btcpay-form btcpay-form--' + (srvModel.fitButtonInline ? 'inline' : 'block') +'">\n' +
|
||||
'<form method="POST"' + (srvModel.useModal ? ' onsubmit="onBTCPayFormSubmit(event);return false"' : '') + ' action="' + esc(srvModel.urlRoot) + actionUrl + '" class="btcpay-form btcpay-form--' + (srvModel.fitButtonInline ? 'inline' : 'block') +'">\n' +
|
||||
addInput("storeId", srvModel.storeId);
|
||||
|
||||
if(app){
|
||||
@ -144,7 +120,6 @@ function inputChanges(event, buttonSize) {
|
||||
|
||||
if (srvModel.checkoutQueryString) html += addInput("checkoutQueryString", srvModel.checkoutQueryString);
|
||||
}
|
||||
|
||||
|
||||
// Fixed amount: Add price and currency as hidden inputs
|
||||
if (isFixedAmount) {
|
||||
@ -192,10 +167,21 @@ function inputChanges(event, buttonSize) {
|
||||
'</button>'
|
||||
}
|
||||
html += '</form>';
|
||||
|
||||
// Scripts
|
||||
var scripts = getScripts(srvModel);
|
||||
var code = html + (scripts ? `\n<script>\n ${scripts.trim()}\n</script>` : '')
|
||||
|
||||
$("#mainCode").text(html).html();
|
||||
$("#preview").html(html);
|
||||
var form = document.querySelector("#preview form");
|
||||
$("#mainCode").text(code).html();
|
||||
var preview = document.getElementById('preview');
|
||||
preview.innerHTML = html;
|
||||
if (scripts) {
|
||||
// script needs to be inserted as node, otherwise it won't get executed
|
||||
var script = document.createElement('script');
|
||||
script.innerHTML = scripts
|
||||
preview.appendChild(script)
|
||||
}
|
||||
var form = preview.querySelector("form");
|
||||
var url = new URL(form.getAttribute("action"));
|
||||
var formData = new FormData(form);
|
||||
formData.forEach((value, key) => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<Project>
|
||||
<PropertyGroup>
|
||||
<Version>1.2.3</Version>
|
||||
<Version>1.2.4</Version>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
13
Changelog.md
13
Changelog.md
@ -1,12 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## 1.2.4
|
||||
|
||||
Minor bug fixes release, update recommended for shared hosting.
|
||||
|
||||
### Bug fixes
|
||||
|
||||
* If `Only enable the payment method after user explicitly chooses it` is enabled for a store and a payment method is unavailable, the server could become unresponsive. @NicolasDorier
|
||||
* Authorize API key page was broken when trying to select specific stores (#2858) @ubolator
|
||||
* The /docs page was broken in 1.2.3 due to CSP @NicolasDorier
|
||||
* Fixing crashes happening when someone migrate from BTCPay Server altcoins edition back to bitcoin only @Kukks
|
||||
|
||||
## 1.2.3
|
||||
|
||||
This release fixes three XSS vulnerabilities. Those vulnerabilities only impacts shared BTCPay instances.
|
||||
Special thanks to Ajmal "@b3ef" Aboobacker and Abdul "@b1nslashsh" muhaimin for finding them who contacted us through @huntrdev.
|
||||
See [1](https://huntr.dev/bounties/ffabdac8-7280-4806-b70c-9b0d1aafbb6e/), [2](https://www.huntr.dev/bounties/32e30ecf-31fa-45f6-8552-47250ef0e613/) and [3](https://huntr.dev/bounties/0fcdee5f-1f07-47ce-b650-ea8b4a7d35d8/).
|
||||
|
||||
# Bug fixes:
|
||||
### Bug fixes:
|
||||
|
||||
* Use CSP to prevent future XSS vulnerabilities. (#2856, #2863) @NicolasDorier
|
||||
* Fix XSS vulnerabilities in summernote, the rich text editor (#2859) @dennisreimann
|
||||
|
Reference in New Issue
Block a user