2020-06-28 21:44:35 -05:00
|
|
|
using System;
|
2021-05-06 20:28:02 +09:00
|
|
|
using System.Globalization;
|
2017-09-13 15:47:34 +09:00
|
|
|
using System.Linq;
|
2020-06-28 17:55:27 +09:00
|
|
|
using System.Net.WebSockets;
|
2017-09-13 15:47:34 +09:00
|
|
|
using System.Threading.Tasks;
|
2022-03-02 18:28:12 +01:00
|
|
|
using BTCPayServer.Abstractions.Extensions;
|
2020-06-28 17:55:27 +09:00
|
|
|
using BTCPayServer.Configuration;
|
2017-09-13 15:47:34 +09:00
|
|
|
using BTCPayServer.Logging;
|
|
|
|
using BTCPayServer.Models;
|
2020-06-04 08:53:55 +02:00
|
|
|
using BTCPayServer.Services;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.AspNetCore.Http;
|
2020-10-07 10:21:18 +02:00
|
|
|
using Microsoft.AspNetCore.Http.Extensions;
|
2020-06-28 17:55:27 +09:00
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Microsoft.Extensions.Primitives;
|
|
|
|
using Newtonsoft.Json;
|
2017-09-13 15:47:34 +09:00
|
|
|
|
|
|
|
namespace BTCPayServer.Hosting
|
|
|
|
{
|
2017-10-27 17:53:04 +09:00
|
|
|
public class BTCPayMiddleware
|
|
|
|
{
|
2020-06-28 22:07:48 -05:00
|
|
|
readonly RequestDelegate _Next;
|
|
|
|
readonly BTCPayServerOptions _Options;
|
2021-11-22 17:16:08 +09:00
|
|
|
|
|
|
|
public Logs Logs { get; }
|
|
|
|
|
2020-06-28 22:07:48 -05:00
|
|
|
readonly BTCPayServerEnvironment _Env;
|
2017-12-17 01:04:20 +09:00
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
public BTCPayMiddleware(RequestDelegate next,
|
2020-06-04 08:53:55 +02:00
|
|
|
BTCPayServerOptions options,
|
2021-11-22 17:16:08 +09:00
|
|
|
BTCPayServerEnvironment env,
|
|
|
|
Logs logs)
|
2017-10-27 17:53:04 +09:00
|
|
|
{
|
2020-06-04 08:53:55 +02:00
|
|
|
_Env = env ?? throw new ArgumentNullException(nameof(env));
|
2017-10-27 17:53:04 +09:00
|
|
|
_Next = next ?? throw new ArgumentNullException(nameof(next));
|
2017-12-02 23:22:23 +09:00
|
|
|
_Options = options ?? throw new ArgumentNullException(nameof(options));
|
2021-11-22 17:16:08 +09:00
|
|
|
Logs = logs;
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
2017-10-12 16:33:53 +09:00
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
public async Task Invoke(HttpContext httpContext)
|
|
|
|
{
|
2021-05-06 20:28:02 +09:00
|
|
|
CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
|
|
|
|
CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture;
|
2018-04-28 02:51:20 +09:00
|
|
|
try
|
2017-10-27 17:53:04 +09:00
|
|
|
{
|
2018-04-29 20:32:43 +09:00
|
|
|
var bitpayAuth = GetBitpayAuth(httpContext, out bool isBitpayAuth);
|
|
|
|
var isBitpayAPI = IsBitpayAPI(httpContext, isBitpayAuth);
|
2019-02-02 15:19:22 +09:00
|
|
|
if (isBitpayAPI && httpContext.Request.Method == "OPTIONS")
|
|
|
|
{
|
|
|
|
httpContext.Response.StatusCode = 200;
|
2019-02-02 15:22:00 +09:00
|
|
|
httpContext.Response.SetHeader("Access-Control-Allow-Origin", "*");
|
2019-02-02 15:51:38 +09:00
|
|
|
if (httpContext.Request.Headers.ContainsKey("Access-Control-Request-Headers"))
|
|
|
|
{
|
|
|
|
httpContext.Response.SetHeader("Access-Control-Allow-Headers", httpContext.Request.Headers["Access-Control-Request-Headers"].FirstOrDefault());
|
|
|
|
}
|
2019-02-02 15:19:22 +09:00
|
|
|
return; // We bypass MVC completely
|
|
|
|
}
|
2018-04-29 20:32:43 +09:00
|
|
|
httpContext.SetIsBitpayAPI(isBitpayAPI);
|
|
|
|
if (isBitpayAPI)
|
2018-04-29 18:28:04 +09:00
|
|
|
{
|
2019-02-02 16:12:51 +09:00
|
|
|
httpContext.Response.SetHeader("Access-Control-Allow-Origin", "*");
|
2018-04-30 22:28:00 +09:00
|
|
|
httpContext.SetBitpayAuth(bitpayAuth);
|
2019-10-07 12:43:17 +09:00
|
|
|
await _Next(httpContext);
|
|
|
|
return;
|
|
|
|
}
|
2020-06-04 08:53:55 +02:00
|
|
|
|
2021-01-05 13:44:08 +09:00
|
|
|
var isHtml = httpContext.Request.Headers.TryGetValue("Accept", out var accept)
|
|
|
|
&& accept.ToString().StartsWith("text/html", StringComparison.OrdinalIgnoreCase);
|
|
|
|
var isModal = httpContext.Request.Query.TryGetValue("view", out var view)
|
|
|
|
&& view.ToString().Equals("modal", StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (!string.IsNullOrEmpty(_Env.OnionUrl) &&
|
2021-12-31 16:59:02 +09:00
|
|
|
!httpContext.Request.IsOnion() &&
|
2021-01-05 13:44:08 +09:00
|
|
|
isHtml &&
|
|
|
|
!isModal)
|
2020-06-04 08:53:55 +02:00
|
|
|
{
|
2020-10-07 10:21:18 +02:00
|
|
|
var onionLocation = _Env.OnionUrl + httpContext.Request.GetEncodedPathAndQuery();
|
2020-06-04 08:53:55 +02:00
|
|
|
httpContext.Response.SetHeader("Onion-Location", onionLocation);
|
|
|
|
}
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
2018-02-15 16:17:27 +09:00
|
|
|
catch (WebSocketException)
|
|
|
|
{ }
|
2017-10-27 17:53:04 +09:00
|
|
|
catch (UnauthorizedAccessException ex)
|
|
|
|
{
|
|
|
|
await HandleBitpayHttpException(httpContext, new BitpayHttpException(401, ex.Message));
|
2020-02-03 02:18:36 -06:00
|
|
|
return;
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
|
|
|
catch (BitpayHttpException ex)
|
|
|
|
{
|
|
|
|
await HandleBitpayHttpException(httpContext, ex);
|
2020-02-03 02:18:36 -06:00
|
|
|
return;
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
|
|
|
catch (Exception ex)
|
|
|
|
{
|
|
|
|
Logs.PayServer.LogCritical(new EventId(), ex, "Unhandled exception in BTCPayMiddleware");
|
|
|
|
throw;
|
|
|
|
}
|
2019-10-07 12:43:17 +09:00
|
|
|
await _Next(httpContext);
|
2018-04-29 18:28:04 +09:00
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
|
2018-04-29 20:32:43 +09:00
|
|
|
private static (string Signature, String Id, String Authorization) GetBitpayAuth(HttpContext httpContext, out bool hasBitpayAuth)
|
|
|
|
{
|
|
|
|
httpContext.Request.Headers.TryGetValue("x-signature", out StringValues values);
|
|
|
|
var sig = values.FirstOrDefault();
|
|
|
|
httpContext.Request.Headers.TryGetValue("x-identity", out values);
|
|
|
|
var id = values.FirstOrDefault();
|
|
|
|
httpContext.Request.Headers.TryGetValue("Authorization", out values);
|
|
|
|
var auth = values.FirstOrDefault();
|
|
|
|
hasBitpayAuth = auth != null || (sig != null && id != null);
|
|
|
|
return (sig, id, auth);
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool IsBitpayAPI(HttpContext httpContext, bool bitpayAuth)
|
|
|
|
{
|
|
|
|
if (!httpContext.Request.Path.HasValue)
|
|
|
|
return false;
|
|
|
|
|
2024-03-30 17:20:24 +08:00
|
|
|
// In case of anyone can create invoice, the storeId can be set explicitly
|
2019-03-25 12:59:42 +09:00
|
|
|
bitpayAuth |= httpContext.Request.Query.ContainsKey("storeid");
|
|
|
|
|
2018-05-11 22:38:31 +09:00
|
|
|
var isJson = (httpContext.Request.ContentType ?? string.Empty).StartsWith("application/json", StringComparison.OrdinalIgnoreCase);
|
2018-04-29 20:32:43 +09:00
|
|
|
var path = httpContext.Request.Path.Value;
|
2019-01-30 14:36:26 +09:00
|
|
|
var method = httpContext.Request.Method;
|
2019-02-02 13:57:17 +09:00
|
|
|
var isCors = method == "OPTIONS";
|
2019-01-30 14:57:10 +09:00
|
|
|
|
2018-04-29 20:32:43 +09:00
|
|
|
if (
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || bitpayAuth) &&
|
2019-03-25 12:59:42 +09:00
|
|
|
(path == "/invoices" || path == "/invoices/") &&
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || (method == "POST" && isJson)))
|
2018-04-29 20:32:43 +09:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || bitpayAuth) &&
|
2018-10-11 22:50:28 +08:00
|
|
|
(path == "/invoices" || path == "/invoices/") &&
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || method == "GET"))
|
2018-04-29 20:32:43 +09:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (
|
2019-02-02 13:57:17 +09:00
|
|
|
path.StartsWith("/invoices/", StringComparison.OrdinalIgnoreCase) &&
|
|
|
|
(isCors || method == "GET") &&
|
|
|
|
(isCors || isJson || httpContext.Request.Query.ContainsKey("token")))
|
2018-04-29 20:32:43 +09:00
|
|
|
return true;
|
|
|
|
|
2018-07-27 07:55:42 +02:00
|
|
|
if (path.StartsWith("/rates", StringComparison.OrdinalIgnoreCase) &&
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || method == "GET"))
|
2018-04-29 20:32:43 +09:00
|
|
|
return true;
|
|
|
|
|
|
|
|
if (
|
2018-10-06 23:20:01 +09:00
|
|
|
path.Equals("/tokens", StringComparison.Ordinal) &&
|
2019-02-02 13:57:17 +09:00
|
|
|
(isCors || method == "GET" || method == "POST"))
|
2018-04-29 20:32:43 +09:00
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-10-27 17:53:04 +09:00
|
|
|
private static async Task HandleBitpayHttpException(HttpContext httpContext, BitpayHttpException ex)
|
|
|
|
{
|
|
|
|
httpContext.Response.StatusCode = ex.StatusCode;
|
2020-02-03 02:18:36 -06:00
|
|
|
httpContext.Response.ContentType = "application/json";
|
|
|
|
var result = JsonConvert.SerializeObject(new BitpayErrorsModel(ex));
|
|
|
|
await httpContext.Response.WriteAsync(result);
|
2017-10-27 17:53:04 +09:00
|
|
|
}
|
|
|
|
}
|
2017-09-13 15:47:34 +09:00
|
|
|
}
|