mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-18 06:24:24 +01:00
Remove TokenRepository dependency from InvoiceControllerAPI
This commit is contained in:
@@ -22,19 +22,16 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
private InvoiceController _InvoiceController;
|
private InvoiceController _InvoiceController;
|
||||||
private InvoiceRepository _InvoiceRepository;
|
private InvoiceRepository _InvoiceRepository;
|
||||||
private TokenRepository _TokenRepository;
|
|
||||||
private StoreRepository _StoreRepository;
|
private StoreRepository _StoreRepository;
|
||||||
private BTCPayNetworkProvider _NetworkProvider;
|
private BTCPayNetworkProvider _NetworkProvider;
|
||||||
|
|
||||||
public InvoiceControllerAPI(InvoiceController invoiceController,
|
public InvoiceControllerAPI(InvoiceController invoiceController,
|
||||||
InvoiceRepository invoceRepository,
|
InvoiceRepository invoceRepository,
|
||||||
TokenRepository tokenRepository,
|
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
BTCPayNetworkProvider networkProvider)
|
BTCPayNetworkProvider networkProvider)
|
||||||
{
|
{
|
||||||
this._InvoiceController = invoiceController;
|
this._InvoiceController = invoiceController;
|
||||||
this._InvoiceRepository = invoceRepository;
|
this._InvoiceRepository = invoceRepository;
|
||||||
this._TokenRepository = tokenRepository;
|
|
||||||
this._StoreRepository = storeRepository;
|
this._StoreRepository = storeRepository;
|
||||||
this._NetworkProvider = networkProvider;
|
this._NetworkProvider = networkProvider;
|
||||||
}
|
}
|
||||||
@@ -44,8 +41,9 @@ namespace BTCPayServer.Controllers
|
|||||||
[MediaTypeConstraint("application/json")]
|
[MediaTypeConstraint("application/json")]
|
||||||
public async Task<DataWrapper<InvoiceResponse>> CreateInvoice([FromBody] Invoice invoice)
|
public async Task<DataWrapper<InvoiceResponse>> CreateInvoice([FromBody] Invoice invoice)
|
||||||
{
|
{
|
||||||
var bitToken = await CheckTokenPermissionAsync(Facade.Merchant, invoice.Token);
|
var store = await _StoreRepository.FindStore(this.User.GetStoreId());
|
||||||
var store = await FindStore(bitToken);
|
if (store == null)
|
||||||
|
throw new BitpayHttpException(401, "Can't access to store");
|
||||||
return await _InvoiceController.CreateInvoiceCore(invoice, store, HttpContext.Request.GetAbsoluteRoot());
|
return await _InvoiceController.CreateInvoiceCore(invoice, store, HttpContext.Request.GetAbsoluteRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,12 +51,12 @@ namespace BTCPayServer.Controllers
|
|||||||
[Route("invoices/{id}")]
|
[Route("invoices/{id}")]
|
||||||
public async Task<DataWrapper<InvoiceResponse>> GetInvoice(string id, string token)
|
public async Task<DataWrapper<InvoiceResponse>> GetInvoice(string id, string token)
|
||||||
{
|
{
|
||||||
var bitToken = await CheckTokenPermissionAsync(Facade.Merchant, token);
|
var store = await _StoreRepository.FindStore(this.User.GetStoreId());
|
||||||
var store = await FindStore(bitToken);
|
if (store == null)
|
||||||
|
throw new BitpayHttpException(401, "Can't access to store");
|
||||||
var invoice = await _InvoiceRepository.GetInvoice(store.Id, id);
|
var invoice = await _InvoiceRepository.GetInvoice(store.Id, id);
|
||||||
if (invoice == null)
|
if (invoice == null)
|
||||||
throw new BitpayHttpException(404, "Object not found");
|
throw new BitpayHttpException(404, "Object not found");
|
||||||
|
|
||||||
var resp = invoice.EntityToDTO(_NetworkProvider);
|
var resp = invoice.EntityToDTO(_NetworkProvider);
|
||||||
return new DataWrapper<InvoiceResponse>(resp);
|
return new DataWrapper<InvoiceResponse>(resp);
|
||||||
}
|
}
|
||||||
@@ -77,8 +75,10 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
if (dateEnd != null)
|
if (dateEnd != null)
|
||||||
dateEnd = dateEnd.Value + TimeSpan.FromDays(1); //Should include the end day
|
dateEnd = dateEnd.Value + TimeSpan.FromDays(1); //Should include the end day
|
||||||
var bitToken = await CheckTokenPermissionAsync(Facade.Merchant, token);
|
|
||||||
var store = await FindStore(bitToken);
|
var store = await _StoreRepository.FindStore(this.User.GetStoreId());
|
||||||
|
if (store == null)
|
||||||
|
throw new BitpayHttpException(401, "Can't access to store");
|
||||||
var query = new InvoiceQuery()
|
var query = new InvoiceQuery()
|
||||||
{
|
{
|
||||||
Count = limit,
|
Count = limit,
|
||||||
@@ -97,45 +97,5 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
return DataWrapper.Create(entities);
|
return DataWrapper.Create(entities);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<BitTokenEntity> CheckTokenPermissionAsync(Facade facade, string expectedToken)
|
|
||||||
{
|
|
||||||
if (facade == null)
|
|
||||||
throw new ArgumentNullException(nameof(facade));
|
|
||||||
|
|
||||||
var actualTokens = (await _TokenRepository.GetTokens(this.User.GetSIN())).ToArray();
|
|
||||||
actualTokens = actualTokens.SelectMany(t => GetCompatibleTokens(t)).ToArray();
|
|
||||||
|
|
||||||
var actualToken = actualTokens.FirstOrDefault(a => a.Value.Equals(expectedToken, StringComparison.Ordinal));
|
|
||||||
if (expectedToken == null || actualToken == null)
|
|
||||||
{
|
|
||||||
Logs.PayServer.LogDebug($"No token found for facade {facade} for SIN {this.User.GetSIN()}");
|
|
||||||
throw new BitpayHttpException(401, $"This endpoint does not support the `{actualTokens.Select(a => a.Facade).Concat(new[] { "user" }).FirstOrDefault()}` facade");
|
|
||||||
}
|
|
||||||
return actualToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IEnumerable<BitTokenEntity> GetCompatibleTokens(BitTokenEntity token)
|
|
||||||
{
|
|
||||||
if (token.Facade == Facade.Merchant.ToString())
|
|
||||||
{
|
|
||||||
yield return token.Clone(Facade.User);
|
|
||||||
yield return token.Clone(Facade.PointOfSale);
|
|
||||||
}
|
|
||||||
if (token.Facade == Facade.PointOfSale.ToString())
|
|
||||||
{
|
|
||||||
yield return token.Clone(Facade.User);
|
|
||||||
}
|
|
||||||
yield return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task<StoreData> FindStore(BitTokenEntity bitToken)
|
|
||||||
{
|
|
||||||
var store = await _StoreRepository.FindStore(bitToken.StoreId);
|
|
||||||
if (store == null)
|
|
||||||
throw new BitpayHttpException(401, "Unknown store");
|
|
||||||
return store;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,6 +148,11 @@ namespace BTCPayServer
|
|||||||
return principal.Claims.Where(c => c.Type == Claims.SIN).Select(c => c.Value).FirstOrDefault();
|
return principal.Claims.Where(c => c.Type == Claims.SIN).Select(c => c.Value).FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetStoreId(this ClaimsPrincipal principal)
|
||||||
|
{
|
||||||
|
return principal.Claims.Where(c => c.Type == Claims.OwnStore).Select(c => c.Value).FirstOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
|
private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() };
|
||||||
public static string ToJson(this object o)
|
public static string ToJson(this object o)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ using BTCPayServer.Controllers;
|
|||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using BTCPayServer.Services;
|
using BTCPayServer.Services;
|
||||||
|
using NBitpayClient;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -51,39 +53,12 @@ namespace BTCPayServer.Hosting
|
|||||||
var sig = values.FirstOrDefault();
|
var sig = values.FirstOrDefault();
|
||||||
httpContext.Request.Headers.TryGetValue("x-identity", out values);
|
httpContext.Request.Headers.TryGetValue("x-identity", out values);
|
||||||
var id = values.FirstOrDefault();
|
var id = values.FirstOrDefault();
|
||||||
if (!string.IsNullOrEmpty(sig) && !string.IsNullOrEmpty(id))
|
|
||||||
{
|
|
||||||
httpContext.Request.EnableRewind();
|
|
||||||
|
|
||||||
string body = string.Empty;
|
|
||||||
if (httpContext.Request.ContentLength != 0 && httpContext.Request.Body != null)
|
|
||||||
{
|
|
||||||
using (StreamReader reader = new StreamReader(httpContext.Request.Body, Encoding.UTF8, true, 1024, true))
|
|
||||||
{
|
|
||||||
body = reader.ReadToEnd();
|
|
||||||
}
|
|
||||||
httpContext.Request.Body.Position = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = httpContext.Request.GetEncodedUrl();
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var key = new PubKey(id);
|
|
||||||
if (BitIdExtensions.CheckBitIDSignature(key, sig, url, body))
|
|
||||||
{
|
|
||||||
var sin = key.GetBitIDSIN();
|
|
||||||
var identity = ((ClaimsIdentity)httpContext.User.Identity);
|
|
||||||
identity.AddClaim(new Claim(Claims.SIN, sin));
|
|
||||||
Logs.PayServer.LogDebug($"BitId signature check success for SIN {sin}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (FormatException) { }
|
|
||||||
if (!httpContext.User.HasClaim(c=> c.Type == Claims.SIN))
|
|
||||||
Logs.PayServer.LogDebug("BitId signature check failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
if (!string.IsNullOrEmpty(sig) && !string.IsNullOrEmpty(id))
|
||||||
|
{
|
||||||
|
await HandleBitId(httpContext, sig, id);
|
||||||
|
}
|
||||||
await _Next(httpContext);
|
await _Next(httpContext);
|
||||||
}
|
}
|
||||||
catch (WebSocketException)
|
catch (WebSocketException)
|
||||||
@@ -101,7 +76,7 @@ namespace BTCPayServer.Hosting
|
|||||||
Logs.PayServer.LogCritical(new EventId(), ex, "Unhandled exception in BTCPayMiddleware");
|
Logs.PayServer.LogCritical(new EventId(), ex, "Unhandled exception in BTCPayMiddleware");
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RewriteHostIfNeeded(HttpContext httpContext)
|
private void RewriteHostIfNeeded(HttpContext httpContext)
|
||||||
{
|
{
|
||||||
@@ -135,7 +110,7 @@ namespace BTCPayServer.Hosting
|
|||||||
httpContext.Request.Scheme = reverseProxyScheme;
|
httpContext.Request.Scheme = reverseProxyScheme;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
httpContext.Request.Scheme = _Options.ExternalUrl.Scheme;
|
httpContext.Request.Scheme = _Options.ExternalUrl.Scheme;
|
||||||
}
|
}
|
||||||
if (_Options.ExternalUrl.IsDefaultPort)
|
if (_Options.ExternalUrl.IsDefaultPort)
|
||||||
@@ -195,5 +170,90 @@ namespace BTCPayServer.Hosting
|
|||||||
await writer.FlushAsync();
|
await writer.FlushAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task HandleBitId(HttpContext httpContext, string sig, string id)
|
||||||
|
{
|
||||||
|
httpContext.Request.EnableRewind();
|
||||||
|
|
||||||
|
string body = string.Empty;
|
||||||
|
if (httpContext.Request.ContentLength != 0 && httpContext.Request.Body != null)
|
||||||
|
{
|
||||||
|
using (StreamReader reader = new StreamReader(httpContext.Request.Body, Encoding.UTF8, true, 1024, true))
|
||||||
|
{
|
||||||
|
body = reader.ReadToEnd();
|
||||||
|
}
|
||||||
|
httpContext.Request.Body.Position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = httpContext.Request.GetEncodedUrl();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var key = new PubKey(id);
|
||||||
|
if (BitIdExtensions.CheckBitIDSignature(key, sig, url, body))
|
||||||
|
{
|
||||||
|
var sin = key.GetBitIDSIN();
|
||||||
|
var identity = ((ClaimsIdentity)httpContext.User.Identity);
|
||||||
|
identity.AddClaim(new Claim(Claims.SIN, sin));
|
||||||
|
|
||||||
|
string token = null;
|
||||||
|
if (httpContext.Request.Query.TryGetValue("token", out var tokenValues))
|
||||||
|
{
|
||||||
|
token = tokenValues[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token == null && !String.IsNullOrEmpty(body) && httpContext.Request.Method == "POST")
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
token = JObject.Parse(body)?.Property("token")?.Value?.Value<string>();
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token != null)
|
||||||
|
{
|
||||||
|
var bitToken = await GetTokenPermissionAsync(sin, token);
|
||||||
|
if (bitToken == null)
|
||||||
|
{
|
||||||
|
throw new BitpayHttpException(401, $"This endpoint does not support this facade");
|
||||||
|
}
|
||||||
|
identity.AddClaim(new Claim(Claims.OwnStore, bitToken.StoreId));
|
||||||
|
}
|
||||||
|
Logs.PayServer.LogDebug($"BitId signature check success for SIN {sin}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (FormatException) { }
|
||||||
|
if (!httpContext.User.HasClaim(c => c.Type == Claims.SIN))
|
||||||
|
Logs.PayServer.LogDebug("BitId signature check failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<BitTokenEntity> GetTokenPermissionAsync(string sin, string expectedToken)
|
||||||
|
{
|
||||||
|
var actualTokens = (await _TokenRepository.GetTokens(sin)).ToArray();
|
||||||
|
actualTokens = actualTokens.SelectMany(t => GetCompatibleTokens(t)).ToArray();
|
||||||
|
|
||||||
|
var actualToken = actualTokens.FirstOrDefault(a => a.Value.Equals(expectedToken, StringComparison.Ordinal));
|
||||||
|
if (expectedToken == null || actualToken == null)
|
||||||
|
{
|
||||||
|
Logs.PayServer.LogDebug($"No token found for facade {Facade.Merchant} for SIN {sin}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return actualToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<BitTokenEntity> GetCompatibleTokens(BitTokenEntity token)
|
||||||
|
{
|
||||||
|
if (token.Facade == Facade.Merchant.ToString())
|
||||||
|
{
|
||||||
|
yield return token.Clone(Facade.User);
|
||||||
|
yield return token.Clone(Facade.PointOfSale);
|
||||||
|
}
|
||||||
|
if (token.Facade == Facade.PointOfSale.ToString())
|
||||||
|
{
|
||||||
|
yield return token.Clone(Facade.User);
|
||||||
|
}
|
||||||
|
yield return token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
@@ -8,5 +9,7 @@ namespace BTCPayServer.Services
|
|||||||
public class Claims
|
public class Claims
|
||||||
{
|
{
|
||||||
public const string SIN = "BITID_SIN";
|
public const string SIN = "BITID_SIN";
|
||||||
|
|
||||||
|
public const string OwnStore = "BTCPAY_OWN_STORE";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ namespace BTCPayServer.Services.Stores
|
|||||||
|
|
||||||
public async Task<StoreData> FindStore(string storeId)
|
public async Task<StoreData> FindStore(string storeId)
|
||||||
{
|
{
|
||||||
|
if (storeId == null)
|
||||||
|
return null;
|
||||||
using (var ctx = _ContextFactory.CreateContext())
|
using (var ctx = _ContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
return await ctx.FindAsync<StoreData>(storeId).ConfigureAwait(false);
|
return await ctx.FindAsync<StoreData>(storeId).ConfigureAwait(false);
|
||||||
|
|||||||
Reference in New Issue
Block a user