mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-03-03 17:36:59 +01:00
Can't pair same SIN to different store
This commit is contained in:
parent
15e73e1cad
commit
a7e10c0fb9
4 changed files with 55 additions and 14 deletions
|
@ -15,6 +15,7 @@ using System.IO;
|
|||
using Newtonsoft.Json.Linq;
|
||||
using BTCPayServer.Controllers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using BTCPayServer.Authentication;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -168,6 +169,25 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CantPairTwiceWithSamePubkey()
|
||||
{
|
||||
using(var tester = ServerTester.Create())
|
||||
{
|
||||
tester.Start();
|
||||
var acc = tester.NewAccount();
|
||||
acc.Register();
|
||||
var store = acc.CreateStore();
|
||||
var pairingCode = acc.BitPay.RequestClientAuthorization("test", Facade.Merchant);
|
||||
Assert.IsType<RedirectToActionResult>(store.Pair(pairingCode.ToString(), acc.StoreId).GetAwaiter().GetResult());
|
||||
|
||||
pairingCode = acc.BitPay.RequestClientAuthorization("test1", Facade.Merchant);
|
||||
var store2 = acc.CreateStore();
|
||||
store2.Pair(pairingCode.ToString(), store2.CreatedStoreId).GetAwaiter().GetResult();
|
||||
Assert.Contains(nameof(PairingResult.ReusedKey), store2.StatusMessage);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void InvoiceFlowThroughDifferentStatesCorrectly()
|
||||
{
|
||||
|
|
|
@ -13,6 +13,14 @@ using System.Linq;
|
|||
|
||||
namespace BTCPayServer.Authentication
|
||||
{
|
||||
public enum PairingResult
|
||||
{
|
||||
Partial,
|
||||
Complete,
|
||||
ReusedKey,
|
||||
Expired
|
||||
}
|
||||
|
||||
public class TokenRepository
|
||||
{
|
||||
ApplicationDbContextFactory _Factory;
|
||||
|
@ -79,40 +87,45 @@ namespace BTCPayServer.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<bool> PairWithStoreAsync(string pairingCodeId, string storeId)
|
||||
public async Task<PairingResult> PairWithStoreAsync(string pairingCodeId, string storeId)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return false;
|
||||
return PairingResult.Expired;
|
||||
pairingCode.StoreDataId = storeId;
|
||||
await ActivateIfComplete(ctx, pairingCode);
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<bool> PairWithSINAsync(string pairingCodeId, string sin)
|
||||
public async Task<PairingResult> PairWithSINAsync(string pairingCodeId, string sin)
|
||||
{
|
||||
using(var ctx = _Factory.CreateContext())
|
||||
{
|
||||
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||
return false;
|
||||
return PairingResult.Expired;
|
||||
pairingCode.SIN = sin;
|
||||
await ActivateIfComplete(ctx, pairingCode);
|
||||
var result = await ActivateIfComplete(ctx, pairingCode);
|
||||
await ctx.SaveChangesAsync();
|
||||
return result;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private async Task ActivateIfComplete(ApplicationDbContext ctx, PairingCodeData pairingCode)
|
||||
private async Task<PairingResult> ActivateIfComplete(ApplicationDbContext ctx, PairingCodeData pairingCode)
|
||||
{
|
||||
if(!string.IsNullOrEmpty(pairingCode.SIN) && !string.IsNullOrEmpty(pairingCode.StoreDataId))
|
||||
{
|
||||
ctx.PairingCodes.Remove(pairingCode);
|
||||
|
||||
// Can have concurrency issues... but no harm can be done
|
||||
var alreadyUsed = await ctx.PairedSINData.Where(p => p.SIN == pairingCode.SIN && p.StoreDataId != pairingCode.StoreDataId).AnyAsync();
|
||||
if(alreadyUsed)
|
||||
return PairingResult.ReusedKey;
|
||||
await ctx.PairedSINData.AddAsync(new PairedSINData()
|
||||
{
|
||||
Id = pairingCode.TokenValue,
|
||||
|
@ -122,7 +135,9 @@ namespace BTCPayServer.Authentication
|
|||
StoreDataId = pairingCode.StoreDataId,
|
||||
SIN = pairingCode.SIN
|
||||
});
|
||||
return PairingResult.Complete;
|
||||
}
|
||||
return PairingResult.Partial;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -57,8 +57,10 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
pairingEntity = await _TokenRepository.GetPairingAsync(request.PairingCode);
|
||||
pairingEntity.SIN = sin;
|
||||
if(!await _TokenRepository.PairWithSINAsync(request.PairingCode, sin))
|
||||
throw new BitpayHttpException(400, "Unknown pairing code");
|
||||
|
||||
var result = await _TokenRepository.PairWithSINAsync(request.PairingCode, sin);
|
||||
if(result != PairingResult.Complete && result != PairingResult.Partial)
|
||||
throw new BitpayHttpException(400, $"Error while pairing ({result})");
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -320,14 +320,18 @@ namespace BTCPayServer.Controllers
|
|||
[Route("api-access-request")]
|
||||
public async Task<IActionResult> Pair(string pairingCode, string selectedStore)
|
||||
{
|
||||
if(pairingCode == null)
|
||||
return NotFound();
|
||||
var store = await _Repo.FindStore(selectedStore, GetUserId());
|
||||
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
|
||||
if(store == null || pairing == null)
|
||||
return NotFound();
|
||||
if(pairingCode != null && await _TokenRepository.PairWithStoreAsync(pairingCode, store.Id))
|
||||
|
||||
var pairingResult = await _TokenRepository.PairWithStoreAsync(pairingCode, store.Id);
|
||||
if(pairingResult == PairingResult.Complete || pairingResult == PairingResult.Partial)
|
||||
{
|
||||
StatusMessage = "Pairing is successfull";
|
||||
if(pairing.SIN == null)
|
||||
if(pairingResult == PairingResult.Partial)
|
||||
StatusMessage = "Server initiated pairing code: " + pairingCode;
|
||||
return RedirectToAction(nameof(ListTokens), new
|
||||
{
|
||||
|
@ -336,7 +340,7 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
else
|
||||
{
|
||||
StatusMessage = "Pairing failed";
|
||||
StatusMessage = $"Pairing failed ({pairingResult})";
|
||||
return RedirectToAction(nameof(ListTokens), new
|
||||
{
|
||||
storeId = store.Id
|
||||
|
|
Loading…
Add table
Reference in a new issue