mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Refactor token handling, support server-initiated pairing
This commit is contained in:
@@ -51,7 +51,7 @@ namespace BTCPayServer.Tests
|
|||||||
return String.IsNullOrEmpty(var) ? defaultValue : var;
|
return String.IsNullOrEmpty(var) ? defaultValue : var;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestAccount CreateAccount()
|
public TestAccount NewAccount()
|
||||||
{
|
{
|
||||||
return new TestAccount(this);
|
return new TestAccount(this);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,10 +26,45 @@ namespace BTCPayServer.Tests
|
|||||||
{
|
{
|
||||||
GrantAccessAsync().GetAwaiter().GetResult();
|
GrantAccessAsync().GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Register()
|
||||||
|
{
|
||||||
|
RegisterAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BitcoinExtKey ExtKey
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
public async Task GrantAccessAsync()
|
public async Task GrantAccessAsync()
|
||||||
{
|
{
|
||||||
var extKey = new ExtKey().GetWif(parent.Network);
|
await RegisterAsync();
|
||||||
|
var store = await CreateStoreAsync();
|
||||||
var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant);
|
var pairingCode = BitPay.RequestClientAuthorization("test", Facade.Merchant);
|
||||||
|
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
||||||
|
await store.Pair(pairingCode.ToString(), StoreId);
|
||||||
|
}
|
||||||
|
public StoresController CreateStore()
|
||||||
|
{
|
||||||
|
return CreateStoreAsync().GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
public async Task<StoresController> CreateStoreAsync()
|
||||||
|
{
|
||||||
|
ExtKey = new ExtKey().GetWif(parent.Network);
|
||||||
|
var store = parent.PayTester.GetController<StoresController>(UserId);
|
||||||
|
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
||||||
|
StoreId = store.CreatedStoreId;
|
||||||
|
await store.UpdateStore(StoreId, new StoreViewModel()
|
||||||
|
{
|
||||||
|
DerivationScheme = ExtKey.Neuter().ToString() + "-[legacy]",
|
||||||
|
SpeedPolicy = SpeedPolicy.MediumSpeed
|
||||||
|
}, "Save");
|
||||||
|
return store;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RegisterAsync()
|
||||||
|
{
|
||||||
var account = parent.PayTester.GetController<AccountController>();
|
var account = parent.PayTester.GetController<AccountController>();
|
||||||
await account.Register(new RegisterViewModel()
|
await account.Register(new RegisterViewModel()
|
||||||
{
|
{
|
||||||
@@ -38,18 +73,6 @@ namespace BTCPayServer.Tests
|
|||||||
Password = "Kitten0@",
|
Password = "Kitten0@",
|
||||||
});
|
});
|
||||||
UserId = account.RegisteredUserId;
|
UserId = account.RegisteredUserId;
|
||||||
|
|
||||||
var store = parent.PayTester.GetController<StoresController>(account.RegisteredUserId);
|
|
||||||
await store.CreateStore(new CreateStoreViewModel() { Name = "Test Store" });
|
|
||||||
StoreId = store.CreatedStoreId;
|
|
||||||
|
|
||||||
await store.UpdateStore(StoreId, new StoreViewModel()
|
|
||||||
{
|
|
||||||
DerivationScheme = extKey.Neuter().ToString() + "-[legacy]",
|
|
||||||
SpeedPolicy = SpeedPolicy.MediumSpeed
|
|
||||||
}, "Save");
|
|
||||||
Assert.IsType<ViewResult>(await store.RequestPairing(pairingCode.ToString()));
|
|
||||||
await store.Pair(pairingCode.ToString(), StoreId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Bitpay BitPay
|
public Bitpay BitPay
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ using BTCPayServer.Servcices.Invoices;
|
|||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using BTCPayServer.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace BTCPayServer.Tests
|
namespace BTCPayServer.Tests
|
||||||
{
|
{
|
||||||
@@ -63,7 +65,7 @@ namespace BTCPayServer.Tests
|
|||||||
using(var tester = ServerTester.Create())
|
using(var tester = ServerTester.Create())
|
||||||
{
|
{
|
||||||
tester.Start();
|
tester.Start();
|
||||||
var user = tester.CreateAccount();
|
var user = tester.NewAccount();
|
||||||
user.GrantAccess();
|
user.GrantAccess();
|
||||||
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
var invoice = user.BitPay.CreateInvoice(new Invoice()
|
||||||
{
|
{
|
||||||
@@ -104,6 +106,31 @@ namespace BTCPayServer.Tests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CanUseServerInitiatedPairingCode()
|
||||||
|
{
|
||||||
|
using(var tester = ServerTester.Create())
|
||||||
|
{
|
||||||
|
tester.Start();
|
||||||
|
var acc = tester.NewAccount();
|
||||||
|
acc.Register();
|
||||||
|
acc.CreateStore();
|
||||||
|
|
||||||
|
var controller = tester.PayTester.GetController<StoresController>(acc.UserId);
|
||||||
|
var token = (RedirectToActionResult)controller.CreateToken(acc.StoreId, new Models.StoreViewModels.CreateTokenViewModel()
|
||||||
|
{
|
||||||
|
Facade = Facade.Merchant.ToString(),
|
||||||
|
Label = "bla",
|
||||||
|
PublicKey = null
|
||||||
|
}).GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
var pairingCode = (string)token.RouteValues["pairingCode"];
|
||||||
|
|
||||||
|
acc.BitPay.AuthorizeClient(new PairingCode(pairingCode)).GetAwaiter().GetResult();
|
||||||
|
Assert.True(acc.BitPay.TestAccess(Facade.Merchant));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CanSendIPN()
|
public void CanSendIPN()
|
||||||
{
|
{
|
||||||
@@ -112,7 +139,7 @@ namespace BTCPayServer.Tests
|
|||||||
using(var tester = ServerTester.Create())
|
using(var tester = ServerTester.Create())
|
||||||
{
|
{
|
||||||
tester.Start();
|
tester.Start();
|
||||||
var acc = tester.CreateAccount();
|
var acc = tester.NewAccount();
|
||||||
acc.GrantAccess();
|
acc.GrantAccess();
|
||||||
var invoice = acc.BitPay.CreateInvoice(new Invoice()
|
var invoice = acc.BitPay.CreateInvoice(new Invoice()
|
||||||
{
|
{
|
||||||
@@ -143,7 +170,7 @@ namespace BTCPayServer.Tests
|
|||||||
using(var tester = ServerTester.Create())
|
using(var tester = ServerTester.Create())
|
||||||
{
|
{
|
||||||
tester.Start();
|
tester.Start();
|
||||||
var user = tester.CreateAccount();
|
var user = tester.NewAccount();
|
||||||
Assert.False(user.BitPay.TestAccess(Facade.Merchant));
|
Assert.False(user.BitPay.TestAccess(Facade.Merchant));
|
||||||
user.GrantAccess();
|
user.GrantAccess();
|
||||||
Assert.True(user.BitPay.TestAccess(Facade.Merchant));
|
Assert.True(user.BitPay.TestAccess(Facade.Merchant));
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace BTCPayServer.Authentication
|
|||||||
{
|
{
|
||||||
public class BitTokenEntity
|
public class BitTokenEntity
|
||||||
{
|
{
|
||||||
public string Name
|
public string Facade
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
@@ -16,15 +16,7 @@ namespace BTCPayServer.Authentication
|
|||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
public DateTimeOffset DateCreated
|
public string StoreId
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
public bool Active
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
public string PairedId
|
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
@@ -46,11 +38,9 @@ namespace BTCPayServer.Authentication
|
|||||||
{
|
{
|
||||||
return new BitTokenEntity()
|
return new BitTokenEntity()
|
||||||
{
|
{
|
||||||
Active = Active,
|
|
||||||
DateCreated = DateCreated,
|
|
||||||
Label = Label,
|
Label = Label,
|
||||||
Name = Name,
|
Facade = Facade,
|
||||||
PairedId = PairedId,
|
StoreId = StoreId,
|
||||||
PairingTime = PairingTime,
|
PairingTime = PairingTime,
|
||||||
SIN = SIN,
|
SIN = SIN,
|
||||||
Value = Value
|
Value = Value
|
||||||
|
|||||||
@@ -26,17 +26,17 @@ namespace BTCPayServer.Authentication
|
|||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
public DateTimeOffset PairingTime
|
public DateTimeOffset CreatedTime
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
public DateTimeOffset PairingExpiration
|
public DateTimeOffset Expiration
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
}
|
}
|
||||||
public string Token
|
public string TokenValue
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
set;
|
set;
|
||||||
@@ -44,7 +44,7 @@ namespace BTCPayServer.Authentication
|
|||||||
|
|
||||||
public bool IsExpired()
|
public bool IsExpired()
|
||||||
{
|
{
|
||||||
return DateTimeOffset.UtcNow > PairingExpiration;
|
return DateTimeOffset.UtcNow > Expiration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using DBreeze;
|
using BTCPayServer.Data;
|
||||||
|
using DBreeze;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
using NBitcoin.DataEncoders;
|
using NBitcoin.DataEncoders;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
@@ -6,175 +7,177 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace BTCPayServer.Authentication
|
namespace BTCPayServer.Authentication
|
||||||
{
|
{
|
||||||
public class TokenRepository
|
public class TokenRepository
|
||||||
{
|
{
|
||||||
public TokenRepository(DBreezeEngine engine)
|
ApplicationDbContextFactory _Factory;
|
||||||
|
public TokenRepository(ApplicationDbContextFactory dbFactory)
|
||||||
{
|
{
|
||||||
_Engine = engine;
|
if(dbFactory == null)
|
||||||
|
throw new ArgumentNullException(nameof(dbFactory));
|
||||||
|
_Factory = dbFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<BitTokenEntity[]> GetTokens(string sin)
|
||||||
private readonly DBreezeEngine _Engine;
|
|
||||||
public DBreezeEngine Engine
|
|
||||||
{
|
{
|
||||||
get
|
using(var ctx = _Factory.CreateContext())
|
||||||
{
|
{
|
||||||
return _Engine;
|
return (await ctx.PairedSINData
|
||||||
|
.Where(p => p.SIN == sin)
|
||||||
|
.ToListAsync())
|
||||||
|
.Select(p => CreateTokenEntity(p))
|
||||||
|
.ToArray();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<BitTokenEntity[]> GetTokens(string sin)
|
private BitTokenEntity CreateTokenEntity(PairedSINData data)
|
||||||
{
|
{
|
||||||
List<BitTokenEntity> tokens = new List<BitTokenEntity>();
|
return new BitTokenEntity()
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
{
|
||||||
tx.ValuesLazyLoadingIsOn = false;
|
Label = data.Label,
|
||||||
foreach(var row in tx.SelectForward<string, byte[]>($"T_{sin}"))
|
Facade = data.Facade,
|
||||||
{
|
Value = data.Id,
|
||||||
var token = ToObject<BitTokenEntity>(row.Value);
|
SIN = data.SIN,
|
||||||
tokens.Add(token);
|
PairingTime = data.PairingTime,
|
||||||
}
|
StoreId = data.StoreDataId
|
||||||
}
|
|
||||||
return Task.FromResult(tokens.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<BitTokenEntity> CreateToken(string sin, string tokenName)
|
|
||||||
{
|
|
||||||
var token = new BitTokenEntity
|
|
||||||
{
|
|
||||||
Name = tokenName,
|
|
||||||
Value = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32)),
|
|
||||||
DateCreated = DateTimeOffset.UtcNow
|
|
||||||
};
|
};
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
|
||||||
tx.Insert<string, byte[]>($"T_{sin}", token.Name, ToBytes(token));
|
|
||||||
tx.Commit();
|
|
||||||
}
|
|
||||||
return Task.FromResult(token);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> PairWithAsync(string pairingCode, string pairedId)
|
public async Task<string> CreatePairingCodeAsync()
|
||||||
{
|
{
|
||||||
if(pairedId == null)
|
string pairingCodeId = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6));
|
||||||
throw new ArgumentNullException(nameof(pairedId));
|
using(var ctx = _Factory.CreateContext())
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
{
|
||||||
var row = tx.Select<string, byte[]>("PairingCodes", pairingCode);
|
var now = DateTime.UtcNow;
|
||||||
if(row == null || !row.Exists)
|
var expiration = DateTime.UtcNow + TimeSpan.FromMinutes(15);
|
||||||
return Task.FromResult(false);
|
await ctx.PairingCodes.AddAsync(new PairingCodeData()
|
||||||
tx.RemoveKey<string>("PairingCodes", pairingCode);
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var pairingEntity = ToObject<PairingCodeEntity>(row.Value);
|
Id = pairingCodeId,
|
||||||
if(pairingEntity.IsExpired())
|
DateCreated = now,
|
||||||
return Task.FromResult(false);
|
Expiration = expiration,
|
||||||
row = tx.Select<string, byte[]>($"T_{pairingEntity.SIN}", pairingEntity.Facade);
|
TokenValue = Encoders.Base58.EncodeData(RandomUtils.GetBytes(32))
|
||||||
if(row == null || !row.Exists)
|
});
|
||||||
return Task.FromResult(false);
|
await ctx.SaveChangesAsync();
|
||||||
var token = ToObject<BitTokenEntity>(row.Value);
|
|
||||||
if(token.Active)
|
|
||||||
return Task.FromResult(false);
|
|
||||||
token.Active = true;
|
|
||||||
token.PairedId = pairedId;
|
|
||||||
token.SIN = pairingEntity.SIN;
|
|
||||||
token.Label = pairingEntity.Label;
|
|
||||||
token.PairingTime = DateTimeOffset.UtcNow;
|
|
||||||
tx.Insert($"TbP_{pairedId}", token.Value, ToBytes(token));
|
|
||||||
tx.Insert($"T_{pairingEntity.SIN}", pairingEntity.Facade, ToBytes(token));
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
tx.Commit();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return Task.FromResult(true);
|
return pairingCodeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<BitTokenEntity[]> GetTokensByPairedIdAsync(string pairedId)
|
public async Task<PairingCodeEntity> UpdatePairingCode(PairingCodeEntity pairingCodeEntity)
|
||||||
{
|
{
|
||||||
List<BitTokenEntity> tokens = new List<BitTokenEntity>();
|
using(var ctx = _Factory.CreateContext())
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
{
|
||||||
tx.ValuesLazyLoadingIsOn = false;
|
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeEntity.Id);
|
||||||
foreach(var row in tx.SelectForward<string, byte[]>($"TbP_{pairedId}"))
|
pairingCode.Label = pairingCodeEntity.Label;
|
||||||
{
|
pairingCode.Facade = pairingCodeEntity.Facade;
|
||||||
tokens.Add(ToObject<BitTokenEntity>(row.Value));
|
await ctx.SaveChangesAsync();
|
||||||
}
|
return CreatePairingCodeEntity(pairingCode);
|
||||||
}
|
|
||||||
return Task.FromResult(tokens.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
|
|
||||||
{
|
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
|
||||||
var row = tx.Select<string, byte[]>("PairingCodes", pairingCode);
|
|
||||||
if(row == null || !row.Exists)
|
|
||||||
return Task.FromResult<PairingCodeEntity>(null);
|
|
||||||
var pairingEntity = ToObject<PairingCodeEntity>(row.Value);
|
|
||||||
if(pairingEntity.IsExpired())
|
|
||||||
return Task.FromResult<PairingCodeEntity>(null);
|
|
||||||
return Task.FromResult(pairingEntity);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task<PairingCodeEntity> AddPairingCodeAsync(PairingCodeEntity pairingCodeEntity)
|
|
||||||
|
public async Task<bool> PairWithStoreAsync(string pairingCodeId, string storeId)
|
||||||
{
|
{
|
||||||
pairingCodeEntity = Clone(pairingCodeEntity);
|
using(var ctx = _Factory.CreateContext())
|
||||||
pairingCodeEntity.Id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(6));
|
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
{
|
||||||
tx.Insert("PairingCodes", pairingCodeEntity.Id, ToBytes(pairingCodeEntity));
|
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||||
tx.Commit();
|
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||||
}
|
return false;
|
||||||
return Task.FromResult(pairingCodeEntity);
|
pairingCode.StoreDataId = storeId;
|
||||||
}
|
await ActivateIfComplete(ctx, pairingCode);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
private byte[] ToBytes<T>(T obj)
|
|
||||||
{
|
|
||||||
return ZipUtils.Zip(JsonConvert.SerializeObject(obj));
|
|
||||||
}
|
|
||||||
private T ToObject<T>(byte[] value)
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject<T>(ZipUtils.Unzip(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
private T Clone<T>(T obj)
|
|
||||||
{
|
|
||||||
return ToObject<T>(ToBytes(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> DeleteToken(string sin, string tokenName, string storeId)
|
|
||||||
{
|
|
||||||
var token = await GetToken(sin, tokenName);
|
|
||||||
if(token == null || (token.PairedId != null && token.PairedId != storeId))
|
|
||||||
return false;
|
|
||||||
using(var tx = _Engine.GetTransaction())
|
|
||||||
{
|
|
||||||
tx.RemoveKey<string>($"T_{sin}", tokenName);
|
|
||||||
if(token.PairedId != null)
|
|
||||||
tx.RemoveKey<string>($"TbP_" + token.PairedId, token.Value);
|
|
||||||
tx.Commit();
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<BitTokenEntity> GetToken(string sin, string tokenName)
|
public async Task<bool> PairWithSINAsync(string pairingCodeId, string sin)
|
||||||
{
|
{
|
||||||
using(var tx = _Engine.GetTransaction())
|
using(var ctx = _Factory.CreateContext())
|
||||||
{
|
{
|
||||||
tx.ValuesLazyLoadingIsOn = true;
|
var pairingCode = await ctx.PairingCodes.FindAsync(pairingCodeId);
|
||||||
var row = tx.Select<string, byte[]>($"T_{sin}", tokenName);
|
if(pairingCode == null || pairingCode.Expiration < DateTimeOffset.UtcNow)
|
||||||
if(row == null || !row.Exists)
|
return false;
|
||||||
return Task.FromResult<BitTokenEntity>(null);
|
pairingCode.SIN = sin;
|
||||||
var token = ToObject<BitTokenEntity>(row.Value);
|
await ActivateIfComplete(ctx, pairingCode);
|
||||||
if(!token.Active)
|
await ctx.SaveChangesAsync();
|
||||||
return Task.FromResult<BitTokenEntity>(null);
|
}
|
||||||
return Task.FromResult(token);
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private async Task ActivateIfComplete(ApplicationDbContext ctx, PairingCodeData pairingCode)
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrEmpty(pairingCode.SIN) && !string.IsNullOrEmpty(pairingCode.StoreDataId))
|
||||||
|
{
|
||||||
|
ctx.PairingCodes.Remove(pairingCode);
|
||||||
|
await ctx.PairedSINData.AddAsync(new PairedSINData()
|
||||||
|
{
|
||||||
|
Id = pairingCode.TokenValue,
|
||||||
|
PairingTime = DateTime.UtcNow,
|
||||||
|
Facade = pairingCode.Facade,
|
||||||
|
Label = pairingCode.Label,
|
||||||
|
StoreDataId = pairingCode.StoreDataId,
|
||||||
|
SIN = pairingCode.SIN
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<BitTokenEntity[]> GetTokensByStoreIdAsync(string storeId)
|
||||||
|
{
|
||||||
|
using(var ctx = _Factory.CreateContext())
|
||||||
|
{
|
||||||
|
return (await ctx.PairedSINData.Where(p => p.StoreDataId == storeId).ToListAsync())
|
||||||
|
.Select(c => CreateTokenEntity(c))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PairingCodeEntity> GetPairingAsync(string pairingCode)
|
||||||
|
{
|
||||||
|
using(var ctx = _Factory.CreateContext())
|
||||||
|
{
|
||||||
|
return CreatePairingCodeEntity(await ctx.PairingCodes.FindAsync(pairingCode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private PairingCodeEntity CreatePairingCodeEntity(PairingCodeData data)
|
||||||
|
{
|
||||||
|
return new PairingCodeEntity()
|
||||||
|
{
|
||||||
|
Facade = data.Facade,
|
||||||
|
Id = data.Id,
|
||||||
|
Label = data.Label,
|
||||||
|
Expiration = data.Expiration,
|
||||||
|
CreatedTime = data.DateCreated,
|
||||||
|
TokenValue = data.TokenValue,
|
||||||
|
SIN = data.SIN
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<bool> DeleteToken(string tokenId)
|
||||||
|
{
|
||||||
|
using(var ctx = _Factory.CreateContext())
|
||||||
|
{
|
||||||
|
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||||
|
if(token == null)
|
||||||
|
return false;
|
||||||
|
ctx.PairedSINData.Remove(token);
|
||||||
|
await ctx.SaveChangesAsync();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<BitTokenEntity> GetToken(string tokenId)
|
||||||
|
{
|
||||||
|
using(var ctx = _Factory.CreateContext())
|
||||||
|
{
|
||||||
|
var token = await ctx.PairedSINData.FindAsync(tokenId);
|
||||||
|
return CreateTokenEntity(token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||||
<Version>1.0.0.5</Version>
|
<Version>1.0.0.6</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Build\dockerfiles\**" />
|
<Compile Remove="Build\dockerfiles\**" />
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||||
<PackageReference Include="NBitcoin" Version="4.0.0.38" />
|
<PackageReference Include="NBitcoin" Version="4.0.0.38" />
|
||||||
<PackageReference Include="NBitpayClient" Version="1.0.0.9" />
|
<PackageReference Include="NBitpayClient" Version="1.0.0.10" />
|
||||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||||
<PackageReference Include="NBXplorer.Client" Version="1.0.0.16" />
|
<PackageReference Include="NBXplorer.Client" Version="1.0.0.16" />
|
||||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.1" />
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ namespace BTCPayServer.Configuration
|
|||||||
}
|
}
|
||||||
DBreezeEngine db = new DBreezeEngine(CreateDBPath(opts, "TokensDB"));
|
DBreezeEngine db = new DBreezeEngine(CreateDBPath(opts, "TokensDB"));
|
||||||
_Resources.Add(db);
|
_Resources.Add(db);
|
||||||
TokenRepository = new TokenRepository(db);
|
|
||||||
|
|
||||||
db = new DBreezeEngine(CreateDBPath(opts, "InvoiceDB"));
|
db = new DBreezeEngine(CreateDBPath(opts, "InvoiceDB"));
|
||||||
_Resources.Add(db);
|
_Resources.Add(db);
|
||||||
@@ -99,10 +98,6 @@ namespace BTCPayServer.Configuration
|
|||||||
get;
|
get;
|
||||||
private set;
|
private set;
|
||||||
}
|
}
|
||||||
public TokenRepository TokenRepository
|
|
||||||
{
|
|
||||||
get; set;
|
|
||||||
}
|
|
||||||
public InvoiceRepository InvoiceRepository
|
public InvoiceRepository InvoiceRepository
|
||||||
{
|
{
|
||||||
get;
|
get;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ namespace BTCPayServer.Controllers
|
|||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
[Route("tokens")]
|
[Route("tokens")]
|
||||||
public async Task<GetTokensResponse> GetTokens()
|
public async Task<GetTokensResponse> Tokens()
|
||||||
{
|
{
|
||||||
var tokens = await _TokenRepository.GetTokens(this.GetBitIdentity().SIN);
|
var tokens = await _TokenRepository.GetTokens(this.GetBitIdentity().SIN);
|
||||||
return new GetTokensResponse(tokens);
|
return new GetTokensResponse(tokens);
|
||||||
@@ -29,33 +29,51 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[Route("tokens")]
|
[Route("tokens")]
|
||||||
public async Task<DataWrapper<List<PairingCodeResponse>>> GetPairingCode([FromBody] PairingCodeRequest token)
|
public async Task<DataWrapper<List<PairingCodeResponse>>> Tokens([FromBody] TokenRequest request)
|
||||||
{
|
{
|
||||||
var now = DateTimeOffset.UtcNow;
|
PairingCodeEntity pairingEntity = null;
|
||||||
var pairingEntity = new PairingCodeEntity()
|
if(string.IsNullOrEmpty(request.PairingCode))
|
||||||
{
|
{
|
||||||
Facade = token.Facade,
|
if(string.IsNullOrEmpty(request.Id) || !NBitpayClient.Extensions.BitIdExtensions.ValidateSIN(request.Id))
|
||||||
Label = token.Label,
|
throw new BitpayHttpException(400, "'id' property is required");
|
||||||
SIN = token.Id,
|
if(string.IsNullOrEmpty(request.Facade))
|
||||||
PairingTime = now,
|
throw new BitpayHttpException(400, "'facade' property is required");
|
||||||
PairingExpiration = now + TimeSpan.FromMinutes(15)
|
|
||||||
};
|
var pairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
||||||
var grantedToken = await _TokenRepository.CreateToken(token.Id, token.Facade);
|
await _TokenRepository.PairWithSINAsync(pairingCode, request.Id);
|
||||||
pairingEntity.Token = grantedToken.Name;
|
pairingEntity = await _TokenRepository.UpdatePairingCode(new PairingCodeEntity()
|
||||||
pairingEntity = await _TokenRepository.AddPairingCodeAsync(pairingEntity);
|
{
|
||||||
|
Id = pairingCode,
|
||||||
|
Facade = request.Facade,
|
||||||
|
Label = request.Label
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var sin = this.GetBitIdentity(false)?.SIN ?? request.Id;
|
||||||
|
if(string.IsNullOrEmpty(request.Id) || !NBitpayClient.Extensions.BitIdExtensions.ValidateSIN(request.Id))
|
||||||
|
throw new BitpayHttpException(400, "'id' property is required, alternatively, use BitId");
|
||||||
|
|
||||||
|
pairingEntity = await _TokenRepository.GetPairingAsync(request.PairingCode);
|
||||||
|
pairingEntity.SIN = sin;
|
||||||
|
if(!await _TokenRepository.PairWithSINAsync(request.PairingCode, sin))
|
||||||
|
throw new BitpayHttpException(400, "Unknown pairing code");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
var pairingCodes = new List<PairingCodeResponse>
|
var pairingCodes = new List<PairingCodeResponse>
|
||||||
{
|
|
||||||
new PairingCodeResponse()
|
|
||||||
{
|
{
|
||||||
PairingCode = pairingEntity.Id,
|
new PairingCodeResponse()
|
||||||
PairingExpiration = pairingEntity.PairingExpiration,
|
{
|
||||||
DateCreated = pairingEntity.PairingTime,
|
PairingCode = pairingEntity.Id,
|
||||||
Facade = grantedToken.Name,
|
PairingExpiration = pairingEntity.Expiration,
|
||||||
Token = grantedToken.Value,
|
DateCreated = pairingEntity.CreatedTime,
|
||||||
Label = pairingEntity.Label
|
Facade = pairingEntity.Facade,
|
||||||
}
|
Token = pairingEntity.TokenValue,
|
||||||
};
|
Label = pairingEntity.Label
|
||||||
|
}
|
||||||
|
};
|
||||||
return DataWrapper.Create(pairingCodes);
|
return DataWrapper.Create(pairingCodes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,26 +83,26 @@ namespace BTCPayServer.Controllers
|
|||||||
if(facade == null)
|
if(facade == null)
|
||||||
throw new ArgumentNullException(nameof(facade));
|
throw new ArgumentNullException(nameof(facade));
|
||||||
|
|
||||||
var actualTokens = (await _TokenRepository.GetTokens(this.GetBitIdentity().SIN)).Where(t => t.Active).ToArray();
|
var actualTokens = (await _TokenRepository.GetTokens(this.GetBitIdentity().SIN)).ToArray();
|
||||||
actualTokens = actualTokens.SelectMany(t => GetCompatibleTokens(t)).ToArray();
|
actualTokens = actualTokens.SelectMany(t => GetCompatibleTokens(t)).ToArray();
|
||||||
|
|
||||||
var actualToken = actualTokens.FirstOrDefault(a => a.Value.Equals(expectedToken, StringComparison.Ordinal));
|
var actualToken = actualTokens.FirstOrDefault(a => a.Value.Equals(expectedToken, StringComparison.Ordinal));
|
||||||
if(expectedToken == null || actualToken == null)
|
if(expectedToken == null || actualToken == null)
|
||||||
{
|
{
|
||||||
Logs.PayServer.LogDebug($"No token found for facade {facade} for SIN {this.GetBitIdentity().SIN}");
|
Logs.PayServer.LogDebug($"No token found for facade {facade} for SIN {this.GetBitIdentity().SIN}");
|
||||||
throw new BitpayHttpException(401, $"This endpoint does not support the `{actualTokens.Select(a => a.Name).Concat(new[] { "user" }).FirstOrDefault()}` facade");
|
throw new BitpayHttpException(401, $"This endpoint does not support the `{actualTokens.Select(a => a.Facade).Concat(new[] { "user" }).FirstOrDefault()}` facade");
|
||||||
}
|
}
|
||||||
return actualToken;
|
return actualToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<BitTokenEntity> GetCompatibleTokens(BitTokenEntity token)
|
private IEnumerable<BitTokenEntity> GetCompatibleTokens(BitTokenEntity token)
|
||||||
{
|
{
|
||||||
if(token.Name == Facade.Merchant.ToString())
|
if(token.Facade == Facade.Merchant.ToString())
|
||||||
{
|
{
|
||||||
yield return token.Clone(Facade.User);
|
yield return token.Clone(Facade.User);
|
||||||
yield return token.Clone(Facade.PointOfSale);
|
yield return token.Clone(Facade.PointOfSale);
|
||||||
}
|
}
|
||||||
if(token.Name == Facade.PointOfSale.ToString())
|
if(token.Facade == Facade.PointOfSale.ToString())
|
||||||
{
|
{
|
||||||
yield return token.Clone(Facade.User);
|
yield return token.Clone(Facade.User);
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ namespace BTCPayServer.Controllers
|
|||||||
|
|
||||||
private async Task<StoreData> FindStore(BitTokenEntity bitToken)
|
private async Task<StoreData> FindStore(BitTokenEntity bitToken)
|
||||||
{
|
{
|
||||||
var store = await _StoreRepository.FindStore(bitToken.PairedId);
|
var store = await _StoreRepository.FindStore(bitToken.StoreId);
|
||||||
if(store == null)
|
if(store == null)
|
||||||
throw new BitpayHttpException(401, "Unknown store");
|
throw new BitpayHttpException(401, "Unknown store");
|
||||||
return store;
|
return store;
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ using BTCPayServer.Servcices.Invoices;
|
|||||||
using BTCPayServer.Services.Rates;
|
using BTCPayServer.Services.Rates;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
using BTCPayServer.Validations;
|
using BTCPayServer.Validations;
|
||||||
|
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.AspNetCore.Mvc.Routing;
|
using Microsoft.AspNetCore.Mvc.Routing;
|
||||||
|
|
||||||
namespace BTCPayServer.Controllers
|
namespace BTCPayServer.Controllers
|
||||||
|
|||||||
@@ -197,11 +197,11 @@ namespace BTCPayServer.Controllers
|
|||||||
public async Task<IActionResult> ListTokens(string storeId)
|
public async Task<IActionResult> ListTokens(string storeId)
|
||||||
{
|
{
|
||||||
var model = new TokensViewModel();
|
var model = new TokensViewModel();
|
||||||
var tokens = await _TokenRepository.GetTokensByPairedIdAsync(storeId);
|
var tokens = await _TokenRepository.GetTokensByStoreIdAsync(storeId);
|
||||||
model.StatusMessage = StatusMessage;
|
model.StatusMessage = StatusMessage;
|
||||||
model.Tokens = tokens.Select(t => new TokenViewModel()
|
model.Tokens = tokens.Select(t => new TokenViewModel()
|
||||||
{
|
{
|
||||||
Facade = t.Name,
|
Facade = t.Facade,
|
||||||
Label = t.Label,
|
Label = t.Label,
|
||||||
SIN = t.SIN,
|
SIN = t.SIN,
|
||||||
Id = t.Value
|
Id = t.Value
|
||||||
@@ -219,16 +219,34 @@ namespace BTCPayServer.Controllers
|
|||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
var pairingCode = await _TokenController.GetPairingCode(new PairingCodeRequest()
|
var tokenRequest = new TokenRequest()
|
||||||
{
|
{
|
||||||
Facade = model.Facade,
|
Facade = model.Facade,
|
||||||
Label = model.Label,
|
Label = model.Label,
|
||||||
Id = NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey))
|
Id = model.PublicKey == null ? null : NBitpayClient.Extensions.BitIdExtensions.GetBitIDSIN(new PubKey(model.PublicKey))
|
||||||
});
|
};
|
||||||
|
|
||||||
|
string pairingCode = null;
|
||||||
|
if(model.PublicKey == null)
|
||||||
|
{
|
||||||
|
tokenRequest.PairingCode = await _TokenRepository.CreatePairingCodeAsync();
|
||||||
|
await _TokenRepository.UpdatePairingCode(new PairingCodeEntity()
|
||||||
|
{
|
||||||
|
Id = tokenRequest.PairingCode,
|
||||||
|
Facade = model.Facade,
|
||||||
|
Label = model.Label,
|
||||||
|
});
|
||||||
|
await _TokenRepository.PairWithStoreAsync(tokenRequest.PairingCode, storeId);
|
||||||
|
pairingCode = tokenRequest.PairingCode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pairingCode = ((DataWrapper<List<PairingCodeResponse>>)await _TokenController.Tokens(tokenRequest)).Data[0].PairingCode;
|
||||||
|
}
|
||||||
|
|
||||||
return RedirectToAction(nameof(RequestPairing), new
|
return RedirectToAction(nameof(RequestPairing), new
|
||||||
{
|
{
|
||||||
pairingCode = pairingCode.Data[0].PairingCode,
|
pairingCode = pairingCode,
|
||||||
selectedStore = storeId
|
selectedStore = storeId
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -239,22 +257,21 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
var model = new CreateTokenViewModel();
|
var model = new CreateTokenViewModel();
|
||||||
model.Facade = "merchant";
|
model.Facade = "merchant";
|
||||||
if(_Env.IsDevelopment())
|
|
||||||
{
|
|
||||||
model.PublicKey = new Key().PubKey.ToHex();
|
|
||||||
}
|
|
||||||
return View(model);
|
return View(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ValidateAntiForgeryToken]
|
[ValidateAntiForgeryToken]
|
||||||
[Route("{storeId}/Tokens/Delete")]
|
[Route("{storeId}/Tokens/Delete")]
|
||||||
public async Task<IActionResult> DeleteToken(string storeId, string name, string sin)
|
public async Task<IActionResult> DeleteToken(string storeId, string tokenId)
|
||||||
{
|
{
|
||||||
if(await _TokenRepository.DeleteToken(sin, name, storeId))
|
var token = await _TokenRepository.GetToken(tokenId);
|
||||||
StatusMessage = "Token revoked";
|
if(token == null ||
|
||||||
else
|
token.StoreId != storeId ||
|
||||||
|
!await _TokenRepository.DeleteToken(tokenId))
|
||||||
StatusMessage = "Failure to revoke this token";
|
StatusMessage = "Failure to revoke this token";
|
||||||
|
else
|
||||||
|
StatusMessage = "Token revoked";
|
||||||
return RedirectToAction(nameof(ListTokens));
|
return RedirectToAction(nameof(ListTokens));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,7 +294,7 @@ namespace BTCPayServer.Controllers
|
|||||||
Id = pairing.Id,
|
Id = pairing.Id,
|
||||||
Facade = pairing.Facade,
|
Facade = pairing.Facade,
|
||||||
Label = pairing.Label,
|
Label = pairing.Label,
|
||||||
SIN = pairing.SIN,
|
SIN = pairing.SIN ?? "Server-Initiated Pairing",
|
||||||
SelectedStore = selectedStore ?? stores.FirstOrDefault()?.Id,
|
SelectedStore = selectedStore ?? stores.FirstOrDefault()?.Id,
|
||||||
Stores = stores.Select(s => new PairingModel.StoreViewModel()
|
Stores = stores.Select(s => new PairingModel.StoreViewModel()
|
||||||
{
|
{
|
||||||
@@ -294,11 +311,14 @@ namespace BTCPayServer.Controllers
|
|||||||
public async Task<IActionResult> Pair(string pairingCode, string selectedStore)
|
public async Task<IActionResult> Pair(string pairingCode, string selectedStore)
|
||||||
{
|
{
|
||||||
var store = await _Repo.FindStore(selectedStore, GetUserId());
|
var store = await _Repo.FindStore(selectedStore, GetUserId());
|
||||||
if(store == null)
|
var pairing = await _TokenRepository.GetPairingAsync(pairingCode);
|
||||||
|
if(store == null || pairing == null)
|
||||||
return NotFound();
|
return NotFound();
|
||||||
if(pairingCode != null && await _TokenRepository.PairWithAsync(pairingCode, store.Id))
|
if(pairingCode != null && await _TokenRepository.PairWithStoreAsync(pairingCode, store.Id))
|
||||||
{
|
{
|
||||||
StatusMessage = "Pairing is successfull";
|
StatusMessage = "Pairing is successfull";
|
||||||
|
if(pairing.SIN == null)
|
||||||
|
StatusMessage = "Server initiated pairing code: " + pairingCode;
|
||||||
return RedirectToAction(nameof(ListTokens), new
|
return RedirectToAction(nameof(ListTokens), new
|
||||||
{
|
{
|
||||||
storeId = store.Id
|
storeId = store.Id
|
||||||
|
|||||||
@@ -56,6 +56,17 @@ namespace BTCPayServer.Data
|
|||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public DbSet<PairingCodeData> PairingCodes
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DbSet<PairedSINData> PairedSINData
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
var isConfigured = optionsBuilder.Options.Extensions.OfType<RelationalOptionsExtension>().Any();
|
var isConfigured = optionsBuilder.Options.Extensions.OfType<RelationalOptionsExtension>().Any();
|
||||||
@@ -94,6 +105,15 @@ namespace BTCPayServer.Data
|
|||||||
|
|
||||||
builder.Entity<AddressInvoiceData>()
|
builder.Entity<AddressInvoiceData>()
|
||||||
.HasKey(o => o.Address);
|
.HasKey(o => o.Address);
|
||||||
|
|
||||||
|
builder.Entity<PairingCodeData>()
|
||||||
|
.HasKey(o => o.Id);
|
||||||
|
|
||||||
|
builder.Entity<PairedSINData>(b =>
|
||||||
|
{
|
||||||
|
b.HasIndex(o => o.SIN);
|
||||||
|
b.HasIndex(o => o.StoreDataId);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
39
BTCPayServer/Data/PairedSINData.cs
Normal file
39
BTCPayServer/Data/PairedSINData.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Data
|
||||||
|
{
|
||||||
|
public class PairedSINData
|
||||||
|
{
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Facade
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string StoreDataId
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public string Label
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public DateTimeOffset PairingTime
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public string SIN
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
BTCPayServer/Data/PairingCodeData.cs
Normal file
50
BTCPayServer/Data/PairingCodeData.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Data
|
||||||
|
{
|
||||||
|
public class PairingCodeData
|
||||||
|
{
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Facade
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public string StoreDataId
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
|
public DateTimeOffset Expiration
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Label
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public string SIN
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public DateTime DateCreated
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
public string TokenValue
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,10 +38,10 @@ namespace BTCPayServer
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static BitIdentity GetBitIdentity(this Controller controller)
|
public static BitIdentity GetBitIdentity(this Controller controller, bool throws = true)
|
||||||
{
|
{
|
||||||
if(!(controller.User.Identity is BitIdentity))
|
if(!(controller.User.Identity is BitIdentity))
|
||||||
throw new UnauthorizedAccessException("no-bitid");
|
return throws ? throw new UnauthorizedAccessException("no-bitid") : (BitIdentity)null;
|
||||||
return (BitIdentity)controller.User.Identity;
|
return (BitIdentity)controller.User.Identity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ using BTCPayServer.Models;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using BTCPayServer.Services.Wallets;
|
using BTCPayServer.Services.Wallets;
|
||||||
|
using BTCPayServer.Authentication;
|
||||||
|
|
||||||
namespace BTCPayServer.Hosting
|
namespace BTCPayServer.Hosting
|
||||||
{
|
{
|
||||||
@@ -107,7 +108,7 @@ namespace BTCPayServer.Hosting
|
|||||||
runtime.Configure(o.GetRequiredService<BTCPayServerOptions>());
|
runtime.Configure(o.GetRequiredService<BTCPayServerOptions>());
|
||||||
return runtime;
|
return runtime;
|
||||||
});
|
});
|
||||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().TokenRepository);
|
services.TryAddSingleton<TokenRepository>();
|
||||||
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().InvoiceRepository);
|
services.TryAddSingleton(o => o.GetRequiredService<BTCPayServerRuntime>().InvoiceRepository);
|
||||||
services.TryAddSingleton<Network>(o => o.GetRequiredService<BTCPayServerOptions>().Network);
|
services.TryAddSingleton<Network>(o => o.GetRequiredService<BTCPayServerOptions>().Network);
|
||||||
services.TryAddSingleton<ApplicationDbContextFactory>(o => o.GetRequiredService<BTCPayServerRuntime>().DBFactory);
|
services.TryAddSingleton<ApplicationDbContextFactory>(o => o.GetRequiredService<BTCPayServerRuntime>().DBFactory);
|
||||||
|
|||||||
444
BTCPayServer/Migrations/20171010082424_Tokens.Designer.cs
generated
Normal file
444
BTCPayServer/Migrations/20171010082424_Tokens.Designer.cs
generated
Normal file
@@ -0,0 +1,444 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using BTCPayServer.Data;
|
||||||
|
using BTCPayServer.Servcices.Invoices;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.Internal;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Migrations
|
||||||
|
{
|
||||||
|
[DbContext(typeof(ApplicationDbContext))]
|
||||||
|
[Migration("20171010082424_Tokens")]
|
||||||
|
partial class Tokens
|
||||||
|
{
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "2.0.0-rtm-26452");
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Address")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("InvoiceDataId");
|
||||||
|
|
||||||
|
b.HasKey("Address");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceDataId");
|
||||||
|
|
||||||
|
b.ToTable("AddressInvoices");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<byte[]>("Blob");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Created");
|
||||||
|
|
||||||
|
b.Property<string>("CustomerEmail");
|
||||||
|
|
||||||
|
b.Property<string>("ExceptionStatus");
|
||||||
|
|
||||||
|
b.Property<string>("ItemCode");
|
||||||
|
|
||||||
|
b.Property<string>("OrderId");
|
||||||
|
|
||||||
|
b.Property<string>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("StoreDataId");
|
||||||
|
|
||||||
|
b.ToTable("Invoices");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Facade");
|
||||||
|
|
||||||
|
b.Property<string>("Label");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("PairingTime");
|
||||||
|
|
||||||
|
b.Property<string>("SIN");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SIN");
|
||||||
|
|
||||||
|
b.HasIndex("StoreDataId");
|
||||||
|
|
||||||
|
b.ToTable("PairedSINData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreated");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Facade");
|
||||||
|
|
||||||
|
b.Property<string>("Label");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("SIN");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.Property<string>("TokenValue");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PairingCodes");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<byte[]>("Blob");
|
||||||
|
|
||||||
|
b.Property<string>("InvoiceDataId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceDataId");
|
||||||
|
|
||||||
|
b.ToTable("Payments");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<byte[]>("Blob");
|
||||||
|
|
||||||
|
b.Property<string>("InvoiceDataId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("InvoiceDataId");
|
||||||
|
|
||||||
|
b.ToTable("RefundAddresses");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.SettingData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.StoreData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("DerivationStrategy");
|
||||||
|
|
||||||
|
b.Property<int>("SpeedPolicy");
|
||||||
|
|
||||||
|
b.Property<byte[]>("StoreCertificate");
|
||||||
|
|
||||||
|
b.Property<string>("StoreName");
|
||||||
|
|
||||||
|
b.Property<string>("StoreWebsite");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("Stores");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("ApplicationUserId");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.Property<string>("Role");
|
||||||
|
|
||||||
|
b.HasKey("ApplicationUserId", "StoreDataId");
|
||||||
|
|
||||||
|
b.HasIndex("StoreDataId");
|
||||||
|
|
||||||
|
b.ToTable("UserStore");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Models.ApplicationUser", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AccessFailedCount");
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<bool>("EmailConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("LockoutEnabled");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset?>("LockoutEnd");
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedEmail")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedUserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber");
|
||||||
|
|
||||||
|
b.Property<bool>("PhoneNumberConfirmed");
|
||||||
|
|
||||||
|
b.Property<bool>("RequiresEmailConfirmation");
|
||||||
|
|
||||||
|
b.Property<string>("SecurityStamp");
|
||||||
|
|
||||||
|
b.Property<bool>("TwoFactorEnabled");
|
||||||
|
|
||||||
|
b.Property<string>("UserName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedEmail")
|
||||||
|
.HasName("EmailIndex");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedUserName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("UserNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUsers");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ConcurrencyStamp")
|
||||||
|
.IsConcurrencyToken();
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.Property<string>("NormalizedName")
|
||||||
|
.HasMaxLength(256);
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("NormalizedName")
|
||||||
|
.IsUnique()
|
||||||
|
.HasName("RoleNameIndex");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetRoleClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("ClaimType");
|
||||||
|
|
||||||
|
b.Property<string>("ClaimValue");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserClaims");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderKey");
|
||||||
|
|
||||||
|
b.Property<string>("ProviderDisplayName");
|
||||||
|
|
||||||
|
b.Property<string>("UserId")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("LoginProvider", "ProviderKey");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserLogins");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("RoleId");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "RoleId");
|
||||||
|
|
||||||
|
b.HasIndex("RoleId");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserRoles");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("UserId");
|
||||||
|
|
||||||
|
b.Property<string>("LoginProvider");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("Value");
|
||||||
|
|
||||||
|
b.HasKey("UserId", "LoginProvider", "Name");
|
||||||
|
|
||||||
|
b.ToTable("AspNetUserTokens");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("InvoiceDataId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("StoreDataId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||||
|
.WithMany("Payments")
|
||||||
|
.HasForeignKey("InvoiceDataId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||||
|
.WithMany("RefundAddresses")
|
||||||
|
.HasForeignKey("InvoiceDataId");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Models.ApplicationUser", "ApplicationUser")
|
||||||
|
.WithMany("UserStores")
|
||||||
|
.HasForeignKey("ApplicationUserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||||
|
.WithMany("UserStores")
|
||||||
|
.HasForeignKey("StoreDataId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("RoleId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
|
||||||
|
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("BTCPayServer.Models.ApplicationUser")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("UserId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
67
BTCPayServer/Migrations/20171010082424_Tokens.cs
Normal file
67
BTCPayServer/Migrations/20171010082424_Tokens.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Migrations
|
||||||
|
{
|
||||||
|
public partial class Tokens : Migration
|
||||||
|
{
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PairedSINData",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
PairingTime = table.Column<DateTimeOffset>(nullable: false),
|
||||||
|
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
StoreDataId = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PairedSINData", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "PairingCodes",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<string>(type: "TEXT", nullable: false),
|
||||||
|
DateCreated = table.Column<DateTime>(nullable: false),
|
||||||
|
Expiration = table.Column<DateTimeOffset>(nullable: false),
|
||||||
|
Facade = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Label = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
Name = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
SIN = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
StoreDataId = table.Column<string>(type: "TEXT", nullable: true),
|
||||||
|
TokenValue = table.Column<string>(type: "TEXT", nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_PairingCodes", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PairedSINData_SIN",
|
||||||
|
table: "PairedSINData",
|
||||||
|
column: "SIN");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_PairedSINData_StoreDataId",
|
||||||
|
table: "PairedSINData",
|
||||||
|
column: "StoreDataId");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PairedSINData");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "PairingCodes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -62,6 +62,58 @@ namespace BTCPayServer.Migrations
|
|||||||
b.ToTable("Invoices");
|
b.ToTable("Invoices");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Facade");
|
||||||
|
|
||||||
|
b.Property<string>("Label");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("PairingTime");
|
||||||
|
|
||||||
|
b.Property<string>("SIN");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("SIN");
|
||||||
|
|
||||||
|
b.HasIndex("StoreDataId");
|
||||||
|
|
||||||
|
b.ToTable("PairedSINData");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("BTCPayServer.Data.PairingCodeData", b =>
|
||||||
|
{
|
||||||
|
b.Property<string>("Id")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<DateTime>("DateCreated");
|
||||||
|
|
||||||
|
b.Property<DateTimeOffset>("Expiration");
|
||||||
|
|
||||||
|
b.Property<string>("Facade");
|
||||||
|
|
||||||
|
b.Property<string>("Label");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("SIN");
|
||||||
|
|
||||||
|
b.Property<string>("StoreDataId");
|
||||||
|
|
||||||
|
b.Property<string>("TokenValue");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("PairingCodes");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
|
||||||
{
|
{
|
||||||
b.Property<string>("Id")
|
b.Property<string>("Id")
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace BTCPayServer.Models
|
|||||||
{
|
{
|
||||||
JObject item = new JObject();
|
JObject item = new JObject();
|
||||||
jarray.Add(item);
|
jarray.Add(item);
|
||||||
JProperty jProp = new JProperty(token.Name);
|
JProperty jProp = new JProperty(token.Facade);
|
||||||
item.Add(jProp);
|
item.Add(jProp);
|
||||||
jProp.Value = token.Value;
|
jProp.Value = token.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using NBitcoin;
|
|||||||
|
|
||||||
namespace BTCPayServer.Models
|
namespace BTCPayServer.Models
|
||||||
{
|
{
|
||||||
public class PairingCodeRequest
|
public class TokenRequest
|
||||||
{
|
{
|
||||||
[JsonProperty(PropertyName = "id")]
|
[JsonProperty(PropertyName = "id")]
|
||||||
public string Id
|
public string Id
|
||||||
@@ -34,6 +34,12 @@ namespace BTCPayServer.Models
|
|||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonProperty(PropertyName = "pairingCode")]
|
||||||
|
public string PairingCode
|
||||||
|
{
|
||||||
|
get; set;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PairingCodeResponse
|
public class PairingCodeResponse
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label asp-for="PublicKey"></label>
|
<label asp-for="PublicKey"></label>
|
||||||
|
<small class="text-muted">Keep empty for server-initiated pairing</small>
|
||||||
<input asp-for="PublicKey" class="form-control" />
|
<input asp-for="PublicKey" class="form-control" />
|
||||||
<span asp-validation-for="PublicKey" class="text-danger"></span>
|
<span asp-validation-for="PublicKey" class="text-danger"></span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,8 +27,7 @@
|
|||||||
<td>@token.Facade</td>
|
<td>@token.Facade</td>
|
||||||
<td>
|
<td>
|
||||||
<form asp-action="DeleteToken" method="post">
|
<form asp-action="DeleteToken" method="post">
|
||||||
<input type="hidden" name="name" value="@token.Facade">
|
<input type="hidden" name="tokenId" value="@token.Id">
|
||||||
<input type="hidden" name="sin" value="@token.SIN">
|
|
||||||
<button type="submit" class="btn btn-danger" role="button">Revoke</button>
|
<button type="submit" class="btn btn-danger" role="button">Revoke</button>
|
||||||
</form>
|
</form>
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
Reference in New Issue
Block a user