Compare commits

...

68 Commits

Author SHA1 Message Date
9ca9b5a5d2 Remove lightning sats denomination (Fix #1012) 2019-09-05 14:58:01 +09:00
3a87dc2223 Fix payment method switching stopped working #1012 2019-09-05 12:48:00 +09:00
67d3875c98 Fix error on this.fetchData 2019-09-05 12:13:36 +09:00
77d0f3d85c Fix tests 2019-09-05 11:55:31 +09:00
0798b95c6b fix build 2019-09-05 11:41:51 +09:00
c247e275f6 Make sure NotificationUrl and RedirectUrl are of type Uri 2019-09-04 18:20:36 +09:00
f17a359893 Fix NotificationUrl and RedirectUrl templating 2019-09-04 18:01:26 +09:00
8e15707dc7 fix dockerfiles 2019-09-04 17:37:40 +09:00
d890753ee2 bump 2019-09-04 17:30:58 +09:00
00b82ad07a Update translations 2019-09-04 17:30:24 +09:00
a21948cf16 Merge pull request #1004 from bolatovumar/fix-606
Tackle additional issues in #606
2019-09-04 17:26:58 +09:00
eb583ba628 Revert "Start off cart hidden on desktop breakpoint"
This reverts commit a169179061c27670f14196cc21cdf64988fb941c.
2019-09-04 09:29:46 +02:00
a4b61f8aab [POS app] Allow empty string for product description (#1010)
fix #1008
2019-09-04 13:07:09 +09:00
7208e63155 Excerpts from #971 (manual PR) (part1 ) (#1006)
* Excerpts from #971

Relating to selenium tests + small renamings

* fix build
2019-09-03 20:11:36 +09:00
8f464b0838 Fix build 2019-09-03 14:10:13 +09:00
233b799a46 Fix inventory string (#1005) 2019-09-03 14:08:55 +09:00
d99beb9811 New feature: Apps limited inventory (#961) 2019-09-02 22:37:52 +09:00
fefc45854e Remove anchor where querying URL 2019-09-02 18:05:40 +09:00
0047a5388d Add test for dead links in Views 2019-09-02 16:32:38 +09:00
66064bd2eb Add link to wallets doc 2019-09-02 16:10:52 +09:00
6bd601137a Allow Placeholders in redirectURL (Fix #169) 2019-09-02 16:04:41 +09:00
eae913f809 fix(pos-app): hide "clear search" button when input field is manually cleared 2019-09-01 11:18:46 -07:00
bc8e7ce888 Add background color to cart tip button when it's focused 2019-08-30 20:27:43 -07:00
1ec342da1e Apply consistent border radius to POS app search bar 2019-08-30 20:06:08 -07:00
a169179061 Start off cart hidden on desktop breakpoint 2019-08-30 20:04:21 -07:00
57b436417c Revert "Skip BundleMinify if in Debug mode"
This reverts commit 0229b560e7b3fd1e1df42c4a7ade1ae5d6d16388.
2019-08-30 19:36:57 +09:00
0229b560e7 Skip BundleMinify if in Debug mode 2019-08-30 19:18:56 +09:00
3c51bd3b23 Merge pull request #1000 from btcpayserver/refactor/move-data
[Refactor] Move SQL classes in their own assembly
2019-08-30 18:45:22 +09:00
b7ba97d86f Fix namespace in the db snapshot 2019-08-30 18:22:19 +09:00
0eb58e9a91 Remove designer files 2019-08-30 18:22:18 +09:00
f257f9f91d [Refactor] Move SQL classes in their own assembly 2019-08-30 18:22:13 +09:00
8971dbc2f9 Ensure raised amount is showing with correct precision in crowdfound app tooltip (#987)
fix #926
2019-08-30 16:17:17 +09:00
5b4e78f8d1 Add bitpay as direct provider, remove reliance on NBitpayClient in the Rate Assembly 2019-08-29 18:13:01 +09:00
27f20386df Add confirmation button to recovery codes view (#984)
fix #806
2019-08-29 17:16:02 +09:00
9154e4264d Address issues in #606 (#988)
* Increase POS cart button and input size elements as per #606

* Increase confirmation modal input and button size as per #606

* Add loading indicator to cart confirmation pay button as per #606

* Ensure POS app tip amount input shows decimal amount with correct divisibility

* Center POS app cart quantity input field

address task in #606

* Ensure search bar and content are horizontally aligned

address task in #606
2019-08-29 17:15:00 +09:00
7457e99451 Change button style 2019-08-29 16:26:34 +09:00
c5227d9996 Request consent from user before giving application access to the user's data & services. 2019-08-29 16:25:16 +09:00
1447b5e8be Add top margin to 2FA screen heading (#986)
fix #985
2019-08-29 16:16:16 +09:00
efdb131c33 checkout fixes (#997) 2019-08-29 15:37:02 +09:00
d9a0db3efc bump nbx 2019-08-29 00:03:14 +09:00
cb8c077c1e better error message for bad ssh config 2019-08-27 23:39:17 +09:00
9688798a4a Asyncify SSH access, do not show SSH service if ssh is not well configured 2019-08-27 23:30:25 +09:00
9a9e31c759 Split Bitcoin/LN to partial defined in payment handler and convert checkout to Vue (#996) 2019-08-27 11:31:15 +09:00
55c0c0ea6f fix #994 tab slider not always working on checkout (#995) 2019-08-26 20:43:40 +09:00
43ee22f965 Allow overriding UI of checkout in payment handler (#992) 2019-08-25 22:50:11 +09:00
wiz
989a7b863e Revert "Add bisq as supporting P2P service" (#993)
This reverts commit 0c5c6233c7d8f1577ce20fc933a071b775142032.
2019-08-25 16:53:29 +09:00
709ee54ac2 Do not prevent btcpay from starting if unsupported sshkey 2019-08-25 12:27:29 +09:00
664b920a39 Make invoice payments view modular per payment handler (#991) 2019-08-24 23:10:13 +09:00
7ea3312534 Add display attributes. Fix #98 (#990) 2019-08-24 23:06:34 +09:00
3e9bee2d44 simplify settings changed listening (#976)
* simplify settings changed listening

Might as well use the Event aggregator to listen in on changes instead of the current complicated magic in the settings repo. This also reduces db calls( it sends the new settings through the event instead of fetching them again)

* add settings extension

* switch to old style but using event aggregator
2019-08-22 20:44:06 +09:00
a571f77a40 Ensure POS/Crowdfund price input fields bring up numpad on mobile (#982)
fix #680
2019-08-21 23:05:52 +09:00
13f2be7811 Add bigger than zero validation for payment request amount (#981)
fix #844
2019-08-21 23:05:06 +09:00
3d00611ddf Remove stray semicolon from payment request view (#980)
fix #979
2019-08-21 23:04:25 +09:00
576734b5cb downgrade clightning 2019-08-21 15:12:09 +09:00
6cd60732b5 Properly render replaced tx in invoice view 2019-08-21 01:25:03 +09:00
1635e1e3fb missing file commit (#977) 2019-08-21 00:00:50 +09:00
b29b46bbc7 Add policy to allow internal lightning node for non-admins (#975)
Partially related to #204 (but no actual management and accounting of funds in different stores occurs)
2019-08-20 17:38:15 +09:00
e45f1afd51 Small rewrite of condition 2019-08-20 16:12:18 +09:00
288dc9b626 Do not loop infinitely waiting for btcpay to start 2019-08-20 16:10:44 +09:00
81c6a76ea2 make sure tests run on latest images 2019-08-20 14:17:52 +09:00
182f9b3cf6 bump clightning 2019-08-20 13:38:42 +09:00
e71fd4950f Add dev env videos to readme (#964)
- Add full node wallet as a feature
- Formatting
- Add windows dev env video
- Add ubuntu dev env video
2019-08-19 14:15:22 +09:00
72d519bb45 Add description and embedded css to POS (#970)
* Add description and embedded css to POS

* wrap embedded css props in <style> before Safe.raw
2019-08-19 14:13:42 +09:00
e743b2e457 Downgrade clightning 2019-08-17 15:57:15 +09:00
f932a34581 Fix docker-compose test 2019-08-17 15:37:57 +09:00
3543d9bd60 Fix docker-compose test 2019-08-17 15:35:41 +09:00
d387834c6c Fix docker-compose test 2019-08-17 15:25:00 +09:00
6ea15411b6 Update c-ligthning and some packages 2019-08-17 15:14:31 +09:00
222 changed files with 4254 additions and 15661 deletions

View File

@ -4,5 +4,6 @@ set -e
cd ../BTCPayServer.Tests
docker-compose -v
docker-compose down --v
docker-compose pull
docker-compose build
docker-compose run -e "TEST_FILTERS=$1" tests

View File

@ -56,7 +56,6 @@ namespace BTCPayServer
InitFeathercoin();
InitGroestlcoin();
InitViacoin();
// Assume that electrum mappings are same as BTC if not specified
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
{

View File

@ -4,7 +4,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
<PackageReference Include="NBitcoin" Version="4.1.2.38" />
<PackageReference Include="NBXplorer.Client" Version="2.0.0.18" />
<PackageReference Include="NBitcoin" Version="4.2.4" />
<PackageReference Include="NBXplorer.Client" Version="2.0.0.19" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="../Build/Common.csproj" />
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="2.0.0" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Data
{
public class AddressInvoiceData
{
/// <summary>
/// Some crypto currencies share same address prefix
/// For not having exceptions thrown by two address on different network, we suffix by "#CRYPTOCODE"
/// </summary>
[Obsolete("Use GetHash instead")]
public string Address
{
get; set;
}
public InvoiceData InvoiceData
{
get; set;
}
public string InvoiceDataId
{
get; set;
}
public DateTimeOffset? CreatedTime
{
get; set;
}
}
}

View File

@ -1,11 +1,7 @@
using System.Linq;
using BTCPayServer.Authentication.OpenId.Models;
using System;
using System.Linq;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using BTCPayServer.Models;
using BTCPayServer.Services.PaymentRequests;
using BTCPayServer.Services.U2F.Models;
using BTCPayServer.Storage.Models;
using Microsoft.EntityFrameworkCore.Infrastructure;
using OpenIddict.EntityFrameworkCore.Models;
@ -229,7 +225,7 @@ namespace BTCPayServer.Data
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<PaymentRequestData>()
.Property(e => e.Created)
.HasDefaultValue(NBitcoin.Utils.UnixTimeToDateTime(0));
.HasDefaultValue(new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero));
builder.Entity<PaymentRequestData>()
.HasIndex(o => o.Status);

View File

@ -88,13 +88,13 @@ namespace BTCPayServer.Data
public void ConfigureBuilder(DbContextOptionsBuilder builder)
{
if (_Type == DatabaseType.Sqlite)
builder.UseSqlite(_ConnectionString);
builder.UseSqlite(_ConnectionString, o => o.MigrationsAssembly("BTCPayServer.Data"));
else if (_Type == DatabaseType.Postgres)
builder
.UseNpgsql(_ConnectionString)
.UseNpgsql(_ConnectionString, o => o.MigrationsAssembly("BTCPayServer.Data"))
.ReplaceService<IMigrationsSqlGenerator, CustomNpgsqlMigrationsSqlGenerator>();
else if (_Type == DatabaseType.MySQL)
builder.UseMySql(_ConnectionString);
builder.UseMySql(_ConnectionString, o => o.MigrationsAssembly("BTCPayServer.Data"));
}
}
}

View File

@ -2,13 +2,10 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Authentication.OpenId.Models;
using Microsoft.AspNetCore.Identity;
using BTCPayServer.Data;
using BTCPayServer.Services.U2F.Models;
using BTCPayServer.Storage.Models;
namespace BTCPayServer.Models
namespace BTCPayServer.Data
{
// Add profile data for application users by adding properties to the ApplicationUser class
public class ApplicationUser : IdentityUser

View File

@ -1,6 +1,6 @@
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Authentication.OpenId.Models
namespace BTCPayServer.Data
{
public class BTCPayOpenIdAuthorization : OpenIddictAuthorization<string, BTCPayOpenIdClient, BTCPayOpenIdToken> { }
}
}

View File

@ -1,7 +1,6 @@
using BTCPayServer.Models;
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Authentication.OpenId.Models
namespace BTCPayServer.Data
{
public class BTCPayOpenIdClient: OpenIddictApplication<string, BTCPayOpenIdAuthorization, BTCPayOpenIdToken>
{

View File

@ -1,6 +1,6 @@
using OpenIddict.EntityFrameworkCore.Models;
namespace BTCPayServer.Authentication.OpenId.Models
namespace BTCPayServer.Data
{
public class BTCPayOpenIdToken : OpenIddictToken<string, BTCPayOpenIdClient, BTCPayOpenIdAuthorization> { }
}
}

View File

@ -31,29 +31,6 @@ namespace BTCPayServer.Data
[Obsolete("Use GetCryptoCode instead")]
public string CryptoCode { get; set; }
#pragma warning disable CS0618
public Payments.PaymentMethodId GetPaymentMethodId()
{
return string.IsNullOrEmpty(CryptoCode) ? new Payments.PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike)
: Payments.PaymentMethodId.Parse(CryptoCode);
}
public string GetAddress()
{
if (Address == null)
return null;
var index = Address.IndexOf("#", StringComparison.InvariantCulture);
if (index == -1)
return Address;
return Address.Substring(0, index);
}
public HistoricalAddressInvoiceData SetAddress(string depositAddress, string cryptoCode)
{
Address = depositAddress + "#" + cryptoCode;
CryptoCode = cryptoCode;
return this;
}
#pragma warning restore CS0618
public DateTimeOffset Assigned
{
get; set;

View File

@ -1,5 +1,4 @@
using BTCPayServer.Models;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -81,10 +80,5 @@ namespace BTCPayServer.Data
get; set;
}
public List<PendingInvoiceData> PendingInvoices { get; set; }
public Services.Invoices.InvoiceState GetInvoiceState()
{
return new Services.Invoices.InvoiceState(Status, ExceptionStatus);
}
}
}

View File

@ -15,7 +15,7 @@ namespace BTCPayServer.Data
{
get; set;
}
public string UniqueId { get; internal set; }
public string UniqueId { get; set; }
public DateTimeOffset Timestamp
{
get; set;

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace BTCPayServer.Data
{
public class PaymentRequestData
{
public string Id { get; set; }
public DateTimeOffset Created
{
get; set;
}
public string StoreDataId { get; set; }
public StoreData StoreData { get; set; }
public PaymentRequestStatus Status { get; set; }
public byte[] Blob { get; set; }
public class PaymentRequestBlob
{
public decimal Amount { get; set; }
public string Currency { get; set; }
public DateTime? ExpiryDate { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Email { get; set; }
public string EmbeddedCSS { get; set; }
public string CustomCSSLink { get; set; }
public bool AllowCustomPaymentAmounts { get; set; }
}
public enum PaymentRequestStatus
{
Pending = 0,
Completed = 1,
Expired = 2
}
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using Newtonsoft.Json.Linq;
using System.Security.Claims;
namespace BTCPayServer.Data
{
public enum SpeedPolicy
{
HighSpeed = 0,
MediumSpeed = 1,
LowSpeed = 2,
LowMediumSpeed = 3
}
public class StoreData
{
public string Id
{
get;
set;
}
public List<UserStore> UserStores
{
get; set;
}
public List<AppData> Apps
{
get; set;
}
public List<PaymentRequestData> PaymentRequests
{
get; set;
}
public List<InvoiceData> Invoices { get; set; }
[Obsolete("Use GetDerivationStrategies instead")]
public string DerivationStrategy
{
get; set;
}
[Obsolete("Use GetDerivationStrategies instead")]
public string DerivationStrategies
{
get;
set;
}
public string StoreName
{
get; set;
}
public SpeedPolicy SpeedPolicy
{
get; set;
}
public string StoreWebsite
{
get; set;
}
public byte[] StoreCertificate
{
get; set;
}
[NotMapped]
[Obsolete]
public string Role
{
get; set;
}
public byte[] StoreBlob
{
get;
set;
}
[Obsolete("Use GetDefaultPaymentId instead")]
public string DefaultCrypto { get; set; }
public List<PairedSINData> PairedSINs { get; set; }
public IEnumerable<APIKeyData> APIKeys { get; set; }
[NotMapped]
public List<Claim> AdditionalClaims { get; set; } = new List<Claim>();
}
public enum NetworkFeeMode
{
MultiplePaymentsOnly,
Always,
Never
}
}

View File

@ -1,8 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations.Schema;
using BTCPayServer.Models;
namespace BTCPayServer.Storage.Models
namespace BTCPayServer.Data
{
public class StoredFile
{

View File

@ -1,8 +1,7 @@
using System;
using System.ComponentModel.DataAnnotations;
using BTCPayServer.Models;
namespace BTCPayServer.Services.U2F.Models
namespace BTCPayServer.Data
{
public class U2FDevice
{

View File

@ -1,5 +1,4 @@
using BTCPayServer.Models;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

View File

@ -14,25 +14,6 @@ namespace BTCPayServer.Data
public List<WalletTransactionData> WalletTransactions { get; set; }
public byte[] Blob { get; set; }
public WalletBlobInfo GetBlobInfo()
{
if (Blob == null || Blob.Length == 0)
{
return new WalletBlobInfo();
}
var blobInfo = JsonConvert.DeserializeObject<WalletBlobInfo>(ZipUtils.Unzip(Blob));
return blobInfo;
}
public void SetBlobInfo(WalletBlobInfo blobInfo)
{
if (blobInfo == null)
{
Blob = Array.Empty<byte>();
return;
}
Blob = ZipUtils.Zip(JsonConvert.SerializeObject(blobInfo));
}
}
public class Label

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
namespace BTCPayServer.Data
{
public class WalletTransactionData
{
public string WalletDataId { get; set; }
public WalletData WalletData { get; set; }
public string TransactionId { get; set; }
public string Labels { get; set; }
public byte[] Blob { get; set; }
}
public class WalletTransactionInfo
{
public string Comment { get; set; } = string.Empty;
[JsonIgnore]
public HashSet<string> Labels { get; set; } = new HashSet<string>();
}
}

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20170913143004_Init")]
public partial class Init : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20170926073744_Settings")]
public partial class Settings : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20170926084408_RequiresEmailConfirmation")]
public partial class RequiresEmailConfirmation : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171006013443_AddressMapping")]
public partial class AddressMapping : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171010082424_Tokens")]
public partial class Tokens : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171012020112_PendingInvoices")]
public partial class PendingInvoices : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171023101754_StoreBlob")]
public partial class StoreBlob : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171024163354_RenewUsedAddresses")]
public partial class RenewUsedAddresses : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171105235734_PaymentAccounted")]
public partial class PaymentAccounted : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20171221054550_AltcoinSupport")]
public partial class AltcoinSupport : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180106095215_DerivationStrategies")]
public partial class DerivationStrategies : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180109021122_defaultcrypto")]
public partial class defaultcrypto : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180114123253_events")]
public partial class events : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180402095640_appdata")]
public partial class appdata : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,9 +1,13 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using System;
using System.Collections.Generic;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180429083930_legacyapikey")]
public partial class legacyapikey : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,7 +1,11 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20180719095626_CanDeleteStores")]
public partial class CanDeleteStores : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190121133309_AddPaymentRequests")]
public partial class AddPaymentRequests : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,7 +1,11 @@
using Microsoft.EntityFrameworkCore.Migrations;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190219032533_AppsTagging")]
public partial class AppsTagging : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190225091644_AddOpenIddict")]
public partial class AddOpenIddict : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190324141717_AddFiles")]
public partial class AddFiles : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190425081749_AddU2fDevices")]
public partial class AddU2fDevices : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190701082105_sort_paymentrequests")]
public partial class sort_paymentrequests : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -1,8 +1,12 @@
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20190802142637_WalletData")]
public partial class WalletData : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)

View File

@ -0,0 +1,883 @@
// <auto-generated />
using System;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
namespace BTCPayServer.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.11-servicing-32099");
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.Property<string>("Address")
.ValueGeneratedOnAdd();
b.Property<DateTimeOffset?>("CreatedTime");
b.Property<string>("InvoiceDataId");
b.HasKey("Address");
b.HasIndex("InvoiceDataId");
b.ToTable("AddressInvoices");
});
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd()
.HasMaxLength(50);
b.Property<string>("StoreId")
.HasMaxLength(50);
b.HasKey("Id");
b.HasIndex("StoreId");
b.ToTable("ApiKeys");
});
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("AppType");
b.Property<DateTimeOffset>("Created");
b.Property<string>("Name");
b.Property<string>("Settings");
b.Property<string>("StoreDataId");
b.Property<bool>("TagAllInvoices");
b.HasKey("Id");
b.HasIndex("StoreDataId");
b.ToTable("Apps");
});
modelBuilder.Entity("BTCPayServer.Data.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("BTCPayServer.Data.BTCPayOpenIdAuthorization", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ApplicationId");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50);
b.Property<string>("Properties");
b.Property<string>("Scopes");
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(25);
b.Property<string>("Subject")
.IsRequired()
.HasMaxLength(450);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(25);
b.HasKey("Id");
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictAuthorizations");
});
modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdClient", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ApplicationUserId");
b.Property<string>("ClientId")
.IsRequired()
.HasMaxLength(100);
b.Property<string>("ClientSecret");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50);
b.Property<string>("ConsentType");
b.Property<string>("DisplayName");
b.Property<string>("Permissions");
b.Property<string>("PostLogoutRedirectUris");
b.Property<string>("Properties");
b.Property<string>("RedirectUris");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(25);
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.HasIndex("ClientId")
.IsUnique();
b.ToTable("OpenIddictApplications");
});
modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdToken", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ApplicationId");
b.Property<string>("AuthorizationId");
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50);
b.Property<DateTimeOffset?>("CreationDate");
b.Property<DateTimeOffset?>("ExpirationDate");
b.Property<string>("Payload");
b.Property<string>("Properties");
b.Property<string>("ReferenceId")
.HasMaxLength(100);
b.Property<string>("Status")
.IsRequired()
.HasMaxLength(25);
b.Property<string>("Subject")
.IsRequired()
.HasMaxLength(450);
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(25);
b.HasKey("Id");
b.HasIndex("AuthorizationId");
b.HasIndex("ReferenceId")
.IsUnique();
b.HasIndex("ApplicationId", "Status", "Subject", "Type");
b.ToTable("OpenIddictTokens");
});
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
{
b.Property<string>("InvoiceDataId");
b.Property<string>("Address");
b.Property<DateTimeOffset>("Assigned");
b.Property<string>("CryptoCode");
b.Property<DateTimeOffset?>("UnAssigned");
b.HasKey("InvoiceDataId", "Address");
b.ToTable("HistoricalAddressInvoices");
});
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.InvoiceEventData", b =>
{
b.Property<string>("InvoiceDataId");
b.Property<string>("UniqueId");
b.Property<string>("Message");
b.Property<DateTimeOffset>("Timestamp");
b.HasKey("InvoiceDataId", "UniqueId");
b.ToTable("InvoiceEvents");
});
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Label");
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>("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<bool>("Accounted");
b.Property<byte[]>("Blob");
b.Property<string>("InvoiceDataId");
b.HasKey("Id");
b.HasIndex("InvoiceDataId");
b.ToTable("Payments");
});
modelBuilder.Entity("BTCPayServer.Data.PaymentRequestData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.Property<DateTimeOffset>("Created")
.ValueGeneratedOnAdd()
.HasDefaultValue(new DateTimeOffset(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
b.Property<int>("Status");
b.Property<string>("StoreDataId");
b.HasKey("Id");
b.HasIndex("Status");
b.HasIndex("StoreDataId");
b.ToTable("PaymentRequests");
});
modelBuilder.Entity("BTCPayServer.Data.PendingInvoiceData", b =>
{
b.Property<string>("Id");
b.HasKey("Id");
b.ToTable("PendingInvoices");
});
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>("DefaultCrypto");
b.Property<string>("DerivationStrategies");
b.Property<string>("DerivationStrategy");
b.Property<int>("SpeedPolicy");
b.Property<byte[]>("StoreBlob");
b.Property<byte[]>("StoreCertificate");
b.Property<string>("StoreName");
b.Property<string>("StoreWebsite");
b.HasKey("Id");
b.ToTable("Stores");
});
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ApplicationUserId");
b.Property<string>("FileName");
b.Property<string>("StorageFileName");
b.Property<DateTime>("Timestamp");
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("Files");
});
modelBuilder.Entity("BTCPayServer.Data.U2FDevice", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ApplicationUserId");
b.Property<byte[]>("AttestationCert")
.IsRequired();
b.Property<int>("Counter");
b.Property<byte[]>("KeyHandle")
.IsRequired();
b.Property<string>("Name");
b.Property<byte[]>("PublicKey")
.IsRequired();
b.HasKey("Id");
b.HasIndex("ApplicationUserId");
b.ToTable("U2FDevices");
});
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.Data.WalletData", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<byte[]>("Blob");
b.HasKey("Id");
b.ToTable("Wallets");
});
modelBuilder.Entity("BTCPayServer.Data.WalletTransactionData", b =>
{
b.Property<string>("WalletDataId");
b.Property<string>("TransactionId");
b.Property<byte[]>("Blob");
b.Property<string>("Labels");
b.HasKey("WalletDataId", "TransactionId");
b.ToTable("WalletTransactions");
});
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("OpenIddict.EntityFrameworkCore.Models.OpenIddictScope<string>", b =>
{
b.Property<string>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("ConcurrencyToken")
.IsConcurrencyToken()
.HasMaxLength(50);
b.Property<string>("Description");
b.Property<string>("DisplayName");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(200);
b.Property<string>("Properties");
b.Property<string>("Resources");
b.HasKey("Id");
b.HasIndex("Name")
.IsUnique();
b.ToTable("OpenIddictScopes");
});
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("AddressInvoices")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("APIKeys")
.HasForeignKey("StoreId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("Apps")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdAuthorization", b =>
{
b.HasOne("BTCPayServer.Data.BTCPayOpenIdClient", "Application")
.WithMany("Authorizations")
.HasForeignKey("ApplicationId");
});
modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdClient", b =>
{
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
.WithMany("OpenIdClients")
.HasForeignKey("ApplicationUserId");
});
modelBuilder.Entity("BTCPayServer.Data.BTCPayOpenIdToken", b =>
{
b.HasOne("BTCPayServer.Data.BTCPayOpenIdClient", "Application")
.WithMany("Tokens")
.HasForeignKey("ApplicationId");
b.HasOne("BTCPayServer.Data.BTCPayOpenIdAuthorization", "Authorization")
.WithMany("Tokens")
.HasForeignKey("AuthorizationId");
});
modelBuilder.Entity("BTCPayServer.Data.HistoricalAddressInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("HistoricalAddressInvoices")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("Invoices")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.InvoiceEventData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("Events")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.PairedSINData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("PairedSINs")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.PaymentData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("Payments")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.PaymentRequestData", b =>
{
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("PaymentRequests")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.PendingInvoiceData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("PendingInvoices")
.HasForeignKey("Id")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.RefundAddressesData", b =>
{
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
.WithMany("RefundAddresses")
.HasForeignKey("InvoiceDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
{
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
.WithMany("StoredFiles")
.HasForeignKey("ApplicationUserId");
});
modelBuilder.Entity("BTCPayServer.Data.U2FDevice", b =>
{
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
.WithMany("U2FDevices")
.HasForeignKey("ApplicationUserId");
});
modelBuilder.Entity("BTCPayServer.Data.UserStore", b =>
{
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
.WithMany("UserStores")
.HasForeignKey("ApplicationUserId")
.OnDelete(DeleteBehavior.Cascade);
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
.WithMany("UserStores")
.HasForeignKey("StoreDataId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("BTCPayServer.Data.WalletTransactionData", b =>
{
b.HasOne("BTCPayServer.Data.WalletData", "WalletData")
.WithMany("WalletTransactions")
.HasForeignKey("WalletDataId")
.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.Data.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("BTCPayServer.Data.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.Data.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("BTCPayServer.Data.ApplicationUser")
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade);
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.EntityFrameworkCore.Infrastructure;
namespace BTCPayServer.Migrations
{
public static class MigrationsExtensions
{
public static bool SupportDropColumn(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportDropForeignKey(this Microsoft.EntityFrameworkCore.Migrations.Migration migration, string activeProvider)
{
return activeProvider != "Microsoft.EntityFrameworkCore.Sqlite";
}
public static bool SupportDropForeignKey(this DatabaseFacade facade)
{
return facade.ProviderName != "Microsoft.EntityFrameworkCore.Sqlite";
}
}
}

View File

@ -14,7 +14,6 @@
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
<PackageReference Include="NBitpayClient" Version="1.0.0.34" />
</ItemGroup>
<ItemGroup>

View File

@ -1,33 +1,35 @@
using NBitpayClient;
using System.Linq;
using System.Linq;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Rating;
using System.Threading;
using System.Net.Http;
using Newtonsoft.Json.Linq;
namespace BTCPayServer.Services.Rates
{
public class BitpayRateProvider : IRateProvider, IHasExchangeName
{
public const string BitpayName = "bitpay";
Bitpay _Bitpay;
public BitpayRateProvider(Bitpay bitpay)
private readonly HttpClient _httpClient;
public BitpayRateProvider(HttpClient httpClient)
{
if (bitpay == null)
throw new ArgumentNullException(nameof(bitpay));
_Bitpay = bitpay;
_httpClient = httpClient ?? new HttpClient();
}
public string ExchangeName => BitpayName;
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
{
return new ExchangeRates((await _Bitpay.GetRatesAsync().ConfigureAwait(false))
.AllRates
.Select(r => new ExchangeRate() { Exchange = BitpayName, CurrencyPair = new CurrencyPair("BTC", r.Code), BidAsk = new BidAsk(r.Value) })
.ToList());
var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
return new ExchangeRates(jarray
.Children<JObject>()
.Select(jobj => new ExchangeRate(ExchangeName, new CurrencyPair("BTC", jobj["code"].Value<string>()), new BidAsk(jobj["rate"].Value<decimal>())))
.Where(o => o.CurrencyPair.Right != "BTC")
.ToArray());
}
}
}

View File

@ -114,6 +114,7 @@ namespace BTCPayServer.Services.Rates
Providers.Add("kraken", new KrakenExchangeRateProvider() { HttpClient = _httpClientFactory?.CreateClient("EXCHANGE_KRAKEN") });
Providers.Add("bylls", new ByllsRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BYLLS")));
Providers.Add("bitbank", new BitbankRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITBANK")));
Providers.Add("bitpay", new BitpayRateProvider(_httpClientFactory?.CreateClient("EXCHANGE_BITPAY")));
// Those exchanges make multiple requests when calling GetTickers so we remove them
//DirectProviders.Add("gemini", new ExchangeSharpRateProvider("gemini", new ExchangeGeminiAPI()));

View File

@ -114,20 +114,27 @@ namespace BTCPayServer.Tests
$"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&nonce={Guid.NewGuid().ToString()}");
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
s.Driver.FindElement(By.Id("consent-yes")).Click();
var url = s.Driver.Url;
var results = url.Split("#").Last().Split("&")
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
await TestApiAgainstAccessToken(results["access_token"], tester, user);
//in Implicit mode, you renew your token by hitting the same endpoint but adding prompt=none. If you are still logged in on the site, you will receive a fresh token.
var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none");
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrlSilentModel);
url = s.Driver.Url;
results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
await TestApiAgainstAccessToken(results["access_token"], tester, user);
LogoutFlow(tester, id, s);
s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl);
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
Assert.Throws<NoSuchElementException>(() => s.Driver.FindElement(By.Id("consent-yes")));
results = url.Split("#").Last().Split("&")
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
await TestApiAgainstAccessToken(results["access_token"], tester, user);
}
}
@ -171,6 +178,7 @@ namespace BTCPayServer.Tests
$"connect/authorize?response_type=code&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid offline_access&state={Guid.NewGuid().ToString()}");
s.Driver.Navigate().GoToUrl(authorizeUrl);
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
s.Driver.FindElement(By.Id("consent-yes")).Click();
var url = s.Driver.Url;
var results = url.Split("?").Last().Split("&")
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
@ -204,6 +212,15 @@ namespace BTCPayServer.Tests
var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret);
await TestApiAgainstAccessToken(refreshedAccessToken, tester, user);
LogoutFlow(tester, id, s);
s.Driver.Navigate().GoToUrl(authorizeUrl);
s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password);
Assert.Throws<NoSuchElementException>(() => s.Driver.FindElement(By.Id("consent-yes")));
results = url.Split("?").Last().Split("&")
.ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]);
Assert.True(results.ContainsKey("code"));
}
}

View File

@ -10,9 +10,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="Selenium.WebDriver" Version="3.141.0" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="74.0.3729.6" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="76.0.3809.6801" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>

View File

@ -228,26 +228,31 @@ namespace BTCPayServer.Tests
private async Task WaitSiteIsOperational()
{
var synching = WaitIsFullySynched();
var accessingHomepage = WaitCanAccessHomepage();
await Task.WhenAll(synching, accessingHomepage).ConfigureAwait(false);
}
private async Task WaitCanAccessHomepage()
{
var resp = await HttpClient.GetAsync("/").ConfigureAwait(false);
while (resp.StatusCode != HttpStatusCode.OK)
using (var cts = new CancellationTokenSource(10_000))
{
await Task.Delay(10).ConfigureAwait(false);
var synching = WaitIsFullySynched(cts.Token);
var accessingHomepage = WaitCanAccessHomepage(cts.Token);
await Task.WhenAll(synching, accessingHomepage).ConfigureAwait(false);
}
}
private async Task WaitIsFullySynched()
private async Task WaitCanAccessHomepage(CancellationToken cancellationToken)
{
while (true)
{
var resp = await HttpClient.GetAsync("/", cancellationToken).ConfigureAwait(false);
if (resp.StatusCode == HttpStatusCode.OK)
break;
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
}
}
private async Task WaitIsFullySynched(CancellationToken cancellationToken)
{
var dashBoard = GetService<NBXplorerDashboard>();
while (!dashBoard.IsFullySynched())
{
await Task.Delay(10).ConfigureAwait(false);
await Task.Delay(10, cancellationToken).ConfigureAwait(false);
}
}

View File

@ -5,6 +5,7 @@ using BTCPayServer.Tests.Logging;
using Microsoft.AspNetCore.Mvc;
using Xunit;
using Xunit.Abstractions;
using BTCPayServer.Data;
namespace BTCPayServer.Tests
{

View File

@ -67,14 +67,15 @@ namespace BTCPayServer.Tests
return usr;
}
public string CreateNewStore()
public (string storeName, string storeId) CreateNewStore()
{
var usr = "Store" + RandomUtils.GetUInt64().ToString();
Driver.FindElement(By.Id("Stores")).Click();
Driver.FindElement(By.Id("CreateStore")).Click();
Driver.FindElement(By.Id("Name")).SendKeys(usr);
Driver.FindElement(By.Id("Create")).Click();
return usr;
return (usr, Driver.FindElement(By.Id("Id")).GetAttribute("value"));
}
public void AddDerivationScheme(string derivationScheme = "xpub661MyMwAqRbcGABgHMUXDzPzH1tU7eZaAaJQXhDXsSxsqyQzQeU6kznNfSuAyqAK9UaWSaZaMFdNiY5BCF4zBPAzSnwfUAwUhwttuAKwfRX-[legacy]")
@ -99,14 +100,24 @@ namespace BTCPayServer.Tests
}
}
public void CreateInvoice(string random)
public string CreateInvoice(string random, string refundEmail = "")
{
Driver.FindElement(By.Id("Invoices")).Click();
Driver.FindElement(By.Id("CreateNewInvoice")).Click();
Driver.FindElement(By.CssSelector("input#Amount.form-control")).SendKeys("100");
Driver.FindElement(By.Name("StoreId")).SendKeys("Deriv" + random + Keys.Enter);
Driver.FindElement(By.Id("Create")).Click();
return;
var statusElement = Driver.FindElement(By.ClassName("alert-success"));
var id = statusElement.Text.Split(" ")[1];
if (!string.IsNullOrEmpty(refundEmail))
{
GoToInvoiceCheckout(id);
Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys(refundEmail);
Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button"))
.Click();
}
return id;
}
@ -147,5 +158,31 @@ namespace BTCPayServer.Tests
Driver.FindElement(By.Id("LoginButton")).Click();
}
public void GoToStore(string storeId)
{
Driver.FindElement(By.Id("Stores")).Click();
Driver.FindElement(By.Id($"update-store-{storeId}")).Click();
}
public void GoToInvoiceCheckout(string invoiceId)
{
Driver.FindElement(By.Id("Invoices")).Click();
Driver.FindElement(By.Id($"invoice-checkout-{invoiceId}")).Click();
}
public void SetCheckbox(IWebElement element, bool value)
{
if ((value && !element.Selected) || (!value && element.Selected))
{
element.Click();
}
}
public void SetCheckbox(SeleniumTester s, string inputName, bool value)
{
SetCheckbox(s.Driver.FindElement(By.Name(inputName)), value);
}
}
}

View File

@ -151,7 +151,7 @@ namespace BTCPayServer.Tests
{
s.Start();
var alice = s.RegisterNewUser();
var store = s.CreateNewStore();
var store = s.CreateNewStore().storeName;
s.AddDerivationScheme();
s.Driver.AssertNoError();
Assert.Contains(store, s.Driver.PageSource);
@ -201,7 +201,7 @@ namespace BTCPayServer.Tests
{
s.Start();
s.RegisterNewUser();
var store = s.CreateNewStore();
var store = s.CreateNewStore().storeName;
s.AddDerivationScheme();
CreateInvoice(s, store);
@ -236,11 +236,11 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Apps")).Click();
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
s.Driver.FindElement(By.Name("Name")).SendKeys("PoS" + store);
s.Driver.FindElement(By.CssSelector("select#SelectedAppType.form-control")).SendKeys("PointOfSale" + Keys.Enter);
s.Driver.FindElement(By.CssSelector("select#SelectedStore.form-control")).SendKeys(store + Keys.Enter);
s.Driver.FindElement(By.Name("Name")).SendKeys("PoS" + Guid.NewGuid());
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("PointOfSale" + Keys.Enter);
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter);
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.CssSelector("input#EnableShoppingCart.form-check")).Click();
s.Driver.FindElement(By.Id("EnableShoppingCart")).Click();
s.Driver.FindElement(By.Id("SaveSettings")).ForceClick();
s.Driver.FindElement(By.Id("ViewApp")).ForceClick();
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
@ -261,9 +261,9 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Apps")).Click();
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
s.Driver.FindElement(By.Name("Name")).SendKeys("CF" + store);
s.Driver.FindElement(By.CssSelector("select#SelectedAppType.form-control")).SendKeys("Crowdfund" + Keys.Enter);
s.Driver.FindElement(By.CssSelector("select#SelectedStore.form-control")).SendKeys(store + Keys.Enter);
s.Driver.FindElement(By.Name("Name")).SendKeys("CF" + Guid.NewGuid());
s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund" + Keys.Enter);
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter);
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter");
s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC");

View File

@ -10,7 +10,6 @@ using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Authentication.OpenId.Models;
using Xunit;
using NBXplorer.DerivationStrategy;
using BTCPayServer.Payments;

View File

@ -44,9 +44,9 @@ namespace BTCPayServer.Tests
formFile.ContentDisposition = $"form-data; name=\"file\"; filename=\"{fileInfo.Name}\"";
return formFile;
}
public static void Eventually(Action act)
public static void Eventually(Action act, int ms = 200000)
{
CancellationTokenSource cts = new CancellationTokenSource(20000);
CancellationTokenSource cts = new CancellationTokenSource(ms);
while (true)
{
try

View File

@ -73,6 +73,49 @@ namespace BTCPayServer.Tests
Logs.LogProvider = new XUnitLogProvider(helper);
}
[Fact]
[Trait("Fast", "Fast")]
public async Task CheckNoDeadLink()
{
var views = Path.Combine(LanguageService.TryGetSolutionDirectoryInfo().FullName, "BTCPayServer", "Views");
var viewFiles = Directory.EnumerateFiles(views, "*.cshtml", SearchOption.AllDirectories).ToArray();
Assert.NotEmpty(viewFiles);
Regex regex = new Regex("href=\"(http.*?)[\"#]");
var httpClient = new HttpClient();
List<Task> checkLinks = new List<Task>();
foreach (var file in viewFiles)
{
checkLinks.Add(CheckLinks(regex, httpClient, file));
}
await Task.WhenAll(checkLinks);
}
private static async Task CheckLinks(Regex regex, HttpClient httpClient, string file)
{
List<Task> checkLinks = new List<Task>();
var text = await File.ReadAllTextAsync(file);
foreach (var match in regex.Matches(text).OfType<Match>())
{
checkLinks.Add(AssertLinkNotDead(httpClient, match, file));
}
await Task.WhenAll(checkLinks);
}
private static async Task AssertLinkNotDead(HttpClient httpClient, Match match, string file)
{
var url = match.Groups[1].Value;
try
{
Assert.Equal(HttpStatusCode.OK, (await httpClient.GetAsync(url)).StatusCode);
Logs.Tester.LogInformation($"OK: {url} ({file})");
}
catch
{
Logs.Tester.LogInformation($"FAILED: {url} ({file})");
throw;
}
}
[Fact]
[Trait("Fast", "Fast")]
public void CanHandleUriValidation()
@ -758,7 +801,7 @@ namespace BTCPayServer.Tests
rescan.GapLimit = 100;
// Sending a coin
var txId = tester.ExplorerNode.SendToAddress(btcDerivationScheme.Derive(new KeyPath("0/90")).ScriptPubKey, Money.Coins(1.0m));
var txId = tester.ExplorerNode.SendToAddress(btcDerivationScheme.GetDerivation(new KeyPath("0/90")).ScriptPubKey, Money.Coins(1.0m));
tester.ExplorerNode.Generate(1);
var transactions = Assert.IsType<ListTransactionsViewModel>(Assert.IsType<ViewResult>(walletController.WalletTransactions(walletId).Result).Model);
Assert.Empty(transactions.Transactions);
@ -1818,7 +1861,7 @@ namespace BTCPayServer.Tests
[Fact]
[Trait("Integration", "Integration")]
public void CanUsePoSApp()
public async Task CanUsePoSApp()
{
using (var tester = ServerTester.Create())
{
@ -1929,6 +1972,49 @@ donation:
Assert.Equal(test.ExpectedDivisibility, vmview.CurrencyInfo.Divisibility);
Assert.Equal(test.ExpectedSymbolSpace, vmview.CurrencyInfo.SymbolSpace);
}
//test inventory related features
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
vmpos.Title = "hello";
vmpos.Currency = "BTC";
vmpos.Template = @"
inventoryitem:
price: 1.0
title: good apple
inventory: 1
noninventoryitem:
price: 10.0";
Assert.IsType<RedirectToActionResult>(apps.UpdatePointOfSale(appId, vmpos).Result);
//inventoryitem has 1 item available
Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 1, null, null, null, null, "inventoryitem").Result);
//we already bought all available stock so this should fail
await Task.Delay(100);
Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 1, null, null, null, null, "inventoryitem").Result);
//inventoryitem has unlimited items available
Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 1, null, null, null, null, "noninventoryitem").Result);
Assert.IsType<RedirectToActionResult>(publicApps.ViewPointOfSale(appId, 1, null, null, null, null, "noninventoryitem").Result);
//verify invoices where created
invoices = user.BitPay.GetInvoices();
Assert.Equal(2, invoices.Count(invoice => invoice.ItemCode.Equals("noninventoryitem")));
var inventoryItemInvoice = invoices.SingleOrDefault(invoice => invoice.ItemCode.Equals("inventoryitem"));
Assert.NotNull(inventoryItemInvoice);
//let's mark the inventoryitem invoice as invalid, thsi should return the item to back in stock
var controller = tester.PayTester.GetController<InvoiceController>(user.UserId, user.StoreId);
var appService = tester.PayTester.GetService<AppService>();
var eventAggregator = tester.PayTester.GetService<EventAggregator>();
Assert.IsType<JsonResult>( await controller.ChangeInvoiceState(inventoryItemInvoice.Id, "invalid"));
//check that item is back in stock
TestUtils.Eventually(() =>
{
vmpos = Assert.IsType<UpdatePointOfSaleViewModel>(Assert.IsType<ViewResult>(apps.UpdatePointOfSale(appId).Result).Model);
Assert.Equal(1, appService.Parse(vmpos.Template, "BTC").Single(item => item.Id == "inventoryitem").Inventory);
}, 10000);
}
}
@ -2791,7 +2877,7 @@ donation:
Assert.Equal("Coldcard Import 0x60d1af8b", settings.Label);
Assert.Equal("49'/0'/0'", settings.AccountKeySettings[0].AccountKeyPath.ToString());
Assert.Equal("ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD", settings.AccountOriginal);
Assert.Equal(root.Derive(new KeyPath("m/49'/0'/0'")).Neuter().PubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey, settings.AccountDerivation.Derive(new KeyPath()).ScriptPubKey);
Assert.Equal(root.Derive(new KeyPath("m/49'/0'/0'")).Neuter().PubKey.WitHash.ScriptPubKey.Hash.ScriptPubKey, settings.AccountDerivation.GetDerivation().ScriptPubKey);
var testnet = new BTCPayNetworkProvider(NetworkType.Testnet).GetNetwork<BTCPayNetwork>("BTC");

View File

@ -68,7 +68,7 @@ services:
- customer_lnd
- merchant_lnd
nbxplorer:
image: nicolasdorier/nbxplorer:2.0.0.55
image: nicolasdorier/nbxplorer:2.0.0.57
restart: unless-stopped
ports:
- "32838:32838"
@ -119,16 +119,16 @@ services:
- "bitcoin_datadir:/data"
customer_lightningd:
image: btcpayserver/lightning:v0.7.0-1-dev
image: btcpayserver/lightning:v0.7.1-dev
stop_signal: SIGKILL
restart: unless-stopped
environment:
EXPOSE_TCP: "true"
LIGHTNINGD_CHAIN: "btc"
LIGHTNINGD_NETWORK: "regtest"
LIGHTNINGD_OPT: |
bitcoin-datadir=/etc/bitcoin
bitcoin-rpcconnect=bitcoind
network=regtest
bind-addr=0.0.0.0
announce-addr=customer_lightningd
log-level=debug
funding-confirms=1
@ -166,17 +166,17 @@ services:
- merchant_lightningd
merchant_lightningd:
image: btcpayserver/lightning:v0.7.0-1-dev
image: btcpayserver/lightning:v0.7.1-dev
stop_signal: SIGKILL
environment:
EXPOSE_TCP: "true"
LIGHTNINGD_CHAIN: "btc"
LIGHTNINGD_NETWORK: "regtest"
LIGHTNINGD_OPT: |
bitcoin-datadir=/etc/bitcoin
bitcoin-rpcconnect=bitcoind
bind-addr=0.0.0.0
announce-addr=merchant_lightningd
funding-confirms=1
network=regtest
log-level=debug
dev-broadcast-interval=1000
ports:

View File

@ -1,14 +1,20 @@
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace BTCPayServer.Authentication.OpenId
{
public class AuthorizationCodeGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
{
public AuthorizationCodeGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
public AuthorizationCodeGrantTypeEventHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions,
UserManager<ApplicationUser> userManager) : base(applicationManager, authorizationManager, signInManager,
identityOptions, userManager)
{
}
@ -18,4 +24,4 @@ namespace BTCPayServer.Authentication.OpenId
return request.IsAuthorizationCodeGrantType();
}
}
}
}

View File

@ -1,78 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Models;
using BTCPayServer.Security;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
{
public class AuthorizationEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleAuthorizationRequest>
{
private readonly UserManager<ApplicationUser> _userManager;
public override async Task<OpenIddictServerEventState> HandleAsync(
OpenIddictServerEvents.HandleAuthorizationRequest notification)
{
if (!notification.Context.Request.IsAuthorizationRequest())
{
return OpenIddictServerEventState.Unhandled;
}
var auth = await notification.Context.HttpContext.AuthenticateAsync();
if (!auth.Succeeded)
{
// If the client application request promptless authentication,
// return an error indicating that the user is not logged in.
if (notification.Context.Request.HasPrompt(OpenIdConnectConstants.Prompts.None))
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIdConnectConstants.Properties.Error] = OpenIdConnectConstants.Errors.LoginRequired,
[OpenIdConnectConstants.Properties.ErrorDescription] = "The user is not logged in."
});
// Ask OpenIddict to return a login_required error to the client application.
await notification.Context.HttpContext.ForbidAsync(properties);
notification.Context.HandleResponse();
return OpenIddictServerEventState.Handled;
}
await notification.Context.HttpContext.ChallengeAsync();
notification.Context.HandleResponse();
return OpenIddictServerEventState.Handled;
}
// Retrieve the profile of the logged in user.
var user = await _userManager.GetUserAsync(auth.Principal);
if (user == null)
{
notification.Context.Reject(
error: OpenIddictConstants.Errors.InvalidGrant,
description: "An internal error has occurred");
return OpenIddictServerEventState.Handled;
}
// Create a new authentication ticket.
var ticket = await CreateTicketAsync(notification.Context.Request, user);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
notification.Context.Validate(ticket);
return OpenIddictServerEventState.Handled;
}
public AuthorizationEventHandler(
UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions) : base(signInManager, identityOptions)
{
_userManager = userManager;
}
}
}

View File

@ -1,13 +1,11 @@
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
@ -15,88 +13,30 @@ namespace BTCPayServer.Authentication.OpenId
public abstract class BaseOpenIdGrantHandler<T> : IOpenIddictServerEventHandler<T>
where T : class, IOpenIddictServerEvent
{
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
private readonly OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> _authorizationManager;
protected readonly SignInManager<ApplicationUser> _signInManager;
protected readonly IOptions<IdentityOptions> _identityOptions;
protected BaseOpenIdGrantHandler(SignInManager<ApplicationUser> signInManager,
protected BaseOpenIdGrantHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions)
{
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_signInManager = signInManager;
_identityOptions = identityOptions;
}
protected async Task<AuthenticationTicket> CreateTicketAsync(
OpenIdConnectRequest request, ApplicationUser user,
AuthenticationProperties properties = null)
{
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await _signInManager.CreateUserPrincipalAsync(user);
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(principal, properties,
OpenIddictServerDefaults.AuthenticationScheme);
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())
{
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
ticket.SetScopes(request.GetScopes());
}
foreach (var claim in ticket.Principal.Claims)
{
claim.SetDestinations(GetDestinations(claim, ticket));
}
return ticket;
}
private IEnumerable<string> GetDestinations(Claim claim, AuthenticationTicket ticket)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
switch (claim.Type)
{
case OpenIddictConstants.Claims.Name:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Profile))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
case OpenIddictConstants.Claims.Email:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Email))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
case OpenIddictConstants.Claims.Role:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Roles))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
default:
if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType)
{
// Never include the security stamp in the access and identity tokens, as it's a secret value.
yield break;
}
else
{
yield return OpenIddictConstants.Destinations.AccessToken;
yield break;
}
}
return await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
_identityOptions.Value, _signInManager, request, user, properties);
}
public abstract Task<OpenIddictServerEventState> HandleAsync(T notification);

View File

@ -2,7 +2,7 @@ using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Authentication.OpenId.Models;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
@ -21,9 +21,12 @@ namespace BTCPayServer.Authentication.OpenId
private readonly UserManager<ApplicationUser> _userManager;
public ClientCredentialsGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
public ClientCredentialsGrantTypeEventHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions,
UserManager<ApplicationUser> userManager) : base(applicationManager, authorizationManager, signInManager,
identityOptions)
{
_applicationManager = applicationManager;

View File

@ -1,21 +1,28 @@
using System;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
{
public class LogoutEventHandler: BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleLogoutRequest>
public class LogoutEventHandler : BaseOpenIdGrantHandler<OpenIddictServerEvents.HandleLogoutRequest>
{
public LogoutEventHandler(SignInManager<ApplicationUser> signInManager, IOptions<IdentityOptions> identityOptions) : base(signInManager, identityOptions)
public LogoutEventHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager, IOptions<IdentityOptions> identityOptions) : base(
applicationManager, authorizationManager,
signInManager, identityOptions)
{
}
public override async Task<OpenIddictServerEventState> HandleAsync(OpenIddictServerEvents.HandleLogoutRequest notification)
public override async Task<OpenIddictServerEventState> HandleAsync(
OpenIddictServerEvents.HandleLogoutRequest notification)
{
// Ask ASP.NET Core Identity to delete the local and external cookies created
// when the user agent is redirected from the external identity provider

View File

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
{
public static class OpenIdExtensions
{
public static async Task<AuthenticationTicket> CreateAuthenticationTicket(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
IdentityOptions identityOptions,
SignInManager<ApplicationUser> signInManager,
OpenIdConnectRequest request,
ApplicationUser user,
AuthenticationProperties properties = null)
{
// Create a new ClaimsPrincipal containing the claims that
// will be used to create an id_token, a token or a code.
var principal = await signInManager.CreateUserPrincipalAsync(user);
// Create a new authentication ticket holding the user identity.
var ticket = new AuthenticationTicket(principal, properties,
OpenIddictServerDefaults.AuthenticationScheme);
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())
{
ticket.SetScopes(request.GetScopes());
}
else if (request.IsAuthorizationCodeGrantType() &&
string.IsNullOrEmpty(ticket.GetInternalAuthorizationId()))
{
var app = await applicationManager.FindByClientIdAsync(request.ClientId);
var authorizationId = await IsUserAuthorized(authorizationManager, request, user.Id, app.Id);
if (!string.IsNullOrEmpty(authorizationId))
{
ticket.SetInternalAuthorizationId(authorizationId);
}
}
foreach (var claim in ticket.Principal.Claims)
{
claim.SetDestinations(GetDestinations(identityOptions, claim, ticket));
}
return ticket;
}
private static IEnumerable<string> GetDestinations(IdentityOptions identityOptions, Claim claim,
AuthenticationTicket ticket)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
switch (claim.Type)
{
case OpenIddictConstants.Claims.Name:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Profile))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
case OpenIddictConstants.Claims.Email:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Email))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
case OpenIddictConstants.Claims.Role:
yield return OpenIddictConstants.Destinations.AccessToken;
if (ticket.HasScope(OpenIddictConstants.Scopes.Roles))
yield return OpenIddictConstants.Destinations.IdentityToken;
yield break;
default:
if (claim.Type == identityOptions.ClaimsIdentity.SecurityStampClaimType)
{
// Never include the security stamp in the access and identity tokens, as it's a secret value.
yield break;
}
else
{
yield return OpenIddictConstants.Destinations.AccessToken;
yield break;
}
}
}
public static async Task<string> IsUserAuthorized(
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
OpenIdConnectRequest request, string userId, string applicationId)
{
var authorizations =
await authorizationManager.ListAsync(queryable =>
queryable.Where(authorization =>
authorization.Subject.Equals(userId, StringComparison.OrdinalIgnoreCase) &&
applicationId.Equals(authorization.Application.Id, StringComparison.OrdinalIgnoreCase) &&
authorization.Status.Equals(OpenIddictConstants.Statuses.Valid,
StringComparison.OrdinalIgnoreCase)));
if (authorizations.Length > 0)
{
var scopeTasks = authorizations.Select(authorization =>
(authorizationManager.GetScopesAsync(authorization).AsTask(), authorization.Id));
await Task.WhenAll(scopeTasks.Select((tuple) => tuple.Item1));
var authorizationsWithSufficientScopes = scopeTasks
.Select((tuple) => (tuple.Id, Scopes: tuple.Item1.Result))
.Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any());
if (authorizationsWithSufficientScopes.Any())
{
return authorizationsWithSufficientScopes.First().Id;
}
}
return null;
}
}
}

View File

@ -1,10 +1,12 @@
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
@ -14,8 +16,12 @@ namespace BTCPayServer.Authentication.OpenId
{
private readonly UserManager<ApplicationUser> _userManager;
protected OpenIdGrantHandlerCheckCanSignIn(SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
protected OpenIdGrantHandlerCheckCanSignIn(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(
applicationManager, authorizationManager, signInManager,
identityOptions)
{
_userManager = userManager;
@ -36,7 +42,6 @@ namespace BTCPayServer.Authentication.OpenId
var scheme = notification.Context.Scheme.Name;
var authenticateResult = (await notification.Context.HttpContext.AuthenticateAsync(scheme));
var user = await _userManager.GetUserAsync(authenticateResult.Principal);
if (user == null)
{

View File

@ -1,10 +1,12 @@
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Services.U2F;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Authentication.OpenId
@ -14,9 +16,13 @@ namespace BTCPayServer.Authentication.OpenId
private readonly UserManager<ApplicationUser> _userManager;
private readonly U2FService _u2FService;
public PasswordGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
public PasswordGrantTypeEventHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
UserManager<ApplicationUser> userManager,
IOptions<IdentityOptions> identityOptions, U2FService u2FService) : base(signInManager, identityOptions)
IOptions<IdentityOptions> identityOptions, U2FService u2FService) : base(applicationManager,
authorizationManager, signInManager, identityOptions)
{
_userManager = userManager;
_u2FService = u2FService;
@ -54,4 +60,4 @@ namespace BTCPayServer.Authentication.OpenId
return OpenIddictServerEventState.Handled;
}
}
}
}

View File

@ -1,14 +1,20 @@
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using OpenIddict.Core;
namespace BTCPayServer.Authentication.OpenId
{
public class RefreshTokenGrantTypeEventHandler : OpenIdGrantHandlerCheckCanSignIn
{
public RefreshTokenGrantTypeEventHandler(SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(signInManager,
public RefreshTokenGrantTypeEventHandler(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
SignInManager<ApplicationUser> signInManager,
IOptions<IdentityOptions> identityOptions, UserManager<ApplicationUser> userManager) : base(
applicationManager, authorizationManager, signInManager,
identityOptions, userManager)
{
}
@ -18,4 +24,4 @@ namespace BTCPayServer.Authentication.OpenId
return request.IsRefreshTokenGrantType();
}
}
}
}

View File

@ -30,13 +30,12 @@
<EmbeddedResource Include="Currencies.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.23" />
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.1" />
<PackageReference Include="BuildBundlerMinifier" Version="2.9.406" />
<PackageReference Include="BundlerMinifier.Core" Version="2.9.406" />
<PackageReference Include="BundlerMinifier.TagHelpers" Version="2.9.406" />
<PackageReference Include="HtmlSanitizer" Version="4.0.217" />
<PackageReference Include="LedgerWallet" Version="2.0.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.6.2">
<PrivateAssets>all</PrivateAssets>
@ -49,10 +48,7 @@
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.9" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
<PackageReference Include="OpenIddict" Version="2.0.0" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="2.0.0" />
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
<PackageReference Include="Serilog" Version="2.7.1" />
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
@ -79,7 +75,6 @@
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\checkout\js\core.js" />
<None Include="wwwroot\vendor\bootstrap4-creativestart\creative.js" />
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.svg" />
<None Include="wwwroot\vendor\font-awesome\fonts\fontawesome-webfont.woff2" />
@ -125,6 +120,7 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Authentication\OpenId\Models\" />
<Folder Include="Build\" />
<Folder Include="U2F\Services" />
<Folder Include="wwwroot\vendor\clipboard.js\" />
@ -134,6 +130,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BTCPayServer.Data\BTCPayServer.Data.csproj" />
<ProjectReference Include="..\BTCPayServer.Rating\BTCPayServer.Rating.csproj" />
<ProjectReference Include="..\BTCPayServer.Common\BTCPayServer.Common.csproj" />
</ItemGroup>

View File

@ -175,12 +175,16 @@ namespace BTCPayServer.Configuration
try
{
sshSettings.CreateConnectionInfo();
SSHSettings = sshSettings;
}
catch (NotSupportedException ex)
{
Logs.Configuration.LogWarning($"The SSH key is not supported ({ex.Message}), try to generate the key with ssh-keygen using \"-m PEM\". Skipping SSH configuration...");
}
catch
{
throw new ConfigException($"sshkeyfilepassword is invalid");
}
SSHSettings = sshSettings;
}
var fingerPrints = conf.GetOrDefault<string>("sshtrustedfingerprints", "");
@ -190,7 +194,7 @@ namespace BTCPayServer.Configuration
{
if (!SSHFingerprint.TryParse(fingerprint, out var f))
throw new ConfigException($"Invalid ssh fingerprint format {fingerprint}");
TrustedFingerprints.Add(f);
SSHSettings?.TrustedFingerprints.Add(f);
}
}
@ -245,11 +249,6 @@ namespace BTCPayServer.Configuration
return settings;
}
internal bool IsTrustedFingerprint(byte[] fingerPrint, byte[] hostKey)
{
return TrustedFingerprints.Any(f => f.Match(fingerPrint, hostKey));
}
public string RootPath { get; set; }
public Dictionary<string, LightningConnectionString> InternalLightningByCryptoCode { get; set; } = new Dictionary<string, LightningConnectionString>();
@ -273,7 +272,6 @@ namespace BTCPayServer.Configuration
set;
}
public bool AllowAdminRegistration { get; set; }
public List<SSHFingerprint> TrustedFingerprints { get; set; } = new List<SSHFingerprint>();
public SSHSettings SSHSettings
{
get;

View File

@ -22,6 +22,7 @@ using BTCPayServer.Services.U2F;
using BTCPayServer.Services.U2F.Models;
using Newtonsoft.Json;
using NicolasDorier.RateLimits;
using BTCPayServer.Data;
namespace BTCPayServer.Controllers
{

View File

@ -156,7 +156,7 @@ namespace BTCPayServer.Controllers
app.TagAllInvoices = vm.UseAllStoreInvoices;
app.SetSettings(newSettings);
await UpdateAppSettings(app);
await _AppService.UpdateOrCreateApp(app);
_EventAggregator.Publish(new AppUpdated()
{

View File

@ -53,6 +53,7 @@ namespace BTCPayServer.Controllers
" title: Fruit Tea\n" +
" description: The Tibetan Himalayas, the land is majestic and beautiful—a spiritual place where, despite the perilous environment, many journey seeking enlightenment. Pay us what you want!\n" +
" image: https://cdn.pixabay.com/photo/2016/09/16/11/24/darts-1673812__480.jpg\n" +
" inventory: 5\n" +
" custom: true";
EnableShoppingCart = false;
ShowCustomAmount = true;
@ -78,6 +79,10 @@ namespace BTCPayServer.Controllers
public string CustomCSSLink { get; set; }
public string EmbeddedCSS { get; set; }
public string Description { get; set; }
public string NotificationEmail { get; set; }
public string NotificationUrl { get; set; }
public bool? RedirectAutomatically { get; set; }
@ -109,6 +114,8 @@ namespace BTCPayServer.Controllers
CustomTipText = settings.CustomTipText ?? PointOfSaleSettings.CUSTOM_TIP_TEXT_DEF,
CustomTipPercentages = settings.CustomTipPercentages != null ? string.Join(",", settings.CustomTipPercentages) : string.Join(",", PointOfSaleSettings.CUSTOM_TIP_PERCENTAGES_DEF),
CustomCSSLink = settings.CustomCSSLink,
EmbeddedCSS = settings.EmbeddedCSS,
Description = settings.Description,
NotificationEmail = settings.NotificationEmail,
NotificationUrl = settings.NotificationUrl,
RedirectAutomatically = settings.RedirectAutomatically.HasValue? settings.RedirectAutomatically.Value? "true": "false" : ""
@ -187,25 +194,16 @@ namespace BTCPayServer.Controllers
CustomCSSLink = vm.CustomCSSLink,
NotificationUrl = vm.NotificationUrl,
NotificationEmail = vm.NotificationEmail,
Description = vm.Description,
EmbeddedCSS = vm.EmbeddedCSS,
RedirectAutomatically = string.IsNullOrEmpty(vm.RedirectAutomatically)? (bool?) null: bool.Parse(vm.RedirectAutomatically)
});
await UpdateAppSettings(app);
await _AppService.UpdateOrCreateApp(app);
StatusMessage = "App updated";
return RedirectToAction(nameof(UpdatePointOfSale), new { appId });
}
private async Task UpdateAppSettings(AppData app)
{
using (var ctx = _ContextFactory.CreateContext())
{
ctx.Apps.Add(app);
ctx.Entry<AppData>(app).State = EntityState.Modified;
ctx.Entry<AppData>(app).Property(a => a.Settings).IsModified = true;
ctx.Entry<AppData>(app).Property(a => a.TagAllInvoices).IsModified = true;
await ctx.SaveChangesAsync();
}
}
private int[] ListSplit(string list, string separator = ",")
{

View File

@ -127,29 +127,25 @@ namespace BTCPayServer.Controllers
StatusMessage = "Error: You are not owner of this store";
return RedirectToAction(nameof(ListApps));
}
var id = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
using (var ctx = _ContextFactory.CreateContext())
var appData = new AppData
{
var appData = new AppData() { Id = id };
appData.StoreDataId = selectedStore;
appData.Name = vm.Name;
appData.AppType = appType.ToString();
ctx.Apps.Add(appData);
await ctx.SaveChangesAsync();
}
StoreDataId = selectedStore,
Name = vm.Name,
AppType = appType.ToString()
};
await _AppService.UpdateOrCreateApp(appData);
StatusMessage = "App successfully created";
CreatedAppId = id;
CreatedAppId = appData.Id;
switch (appType)
{
case AppType.PointOfSale:
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = id });
return RedirectToAction(nameof(UpdatePointOfSale), new { appId = appData.Id });
case AppType.Crowdfund:
return RedirectToAction(nameof(UpdateCrowdfund), new { appId = id });
return RedirectToAction(nameof(UpdateCrowdfund), new { appId = appData.Id });
default:
return RedirectToAction(nameof(ListApps));
}
}
[HttpGet]

View File

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Threading;
@ -13,40 +11,32 @@ using BTCPayServer.Filters;
using BTCPayServer.ModelBinders;
using BTCPayServer.Models;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Payments;
using BTCPayServer.Rating;
using BTCPayServer.Security;
using BTCPayServer.Services.Apps;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using Ganss.XSS;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NBitpayClient;
using YamlDotNet.RepresentationModel;
using static BTCPayServer.Controllers.AppsController;
namespace BTCPayServer.Controllers
{
public class AppsPublicController : Controller
{
public AppsPublicController(AppService AppService,
public AppsPublicController(AppService appService,
BTCPayServerOptions btcPayServerOptions,
InvoiceController invoiceController,
UserManager<ApplicationUser> userManager)
{
_AppService = AppService;
_AppService = appService;
_BtcPayServerOptions = btcPayServerOptions;
_InvoiceController = invoiceController;
_UserManager = userManager;
}
private AppService _AppService;
private readonly AppService _AppService;
private readonly BTCPayServerOptions _BtcPayServerOptions;
private InvoiceController _InvoiceController;
private readonly InvoiceController _InvoiceController;
private readonly UserManager<ApplicationUser> _UserManager;
[HttpGet]
@ -87,7 +77,9 @@ namespace BTCPayServer.Controllers
CustomTipText = settings.CustomTipText,
CustomTipPercentages = settings.CustomTipPercentages,
CustomCSSLink = settings.CustomCSSLink,
AppId = appId
AppId = appId,
Description = settings.Description,
EmbeddedCSS = settings.EmbeddedCSS
});
}
@ -130,6 +122,14 @@ namespace BTCPayServer.Controllers
price = choice.Price.Value;
if (amount > price)
price = amount;
if (choice.Inventory.HasValue)
{
if (choice.Inventory <= 0)
{
return RedirectToAction(nameof(ViewPointOfSale), new { appId = appId });
}
}
}
else
{
@ -137,6 +137,32 @@ namespace BTCPayServer.Controllers
return NotFound();
price = amount;
title = settings.Title;
//if cart IS enabled and we detect posdata that matches the cart system's, check inventory for the items
if (!string.IsNullOrEmpty(posData) &&
settings.EnableShoppingCart &&
AppService.TryParsePosCartItems(posData, out var cartItems))
{
var choices = _AppService.Parse(settings.Template, settings.Currency);
foreach (var cartItem in cartItems)
{
var itemChoice = choices.FirstOrDefault(c => c.Id == cartItem.Key);
if (itemChoice == null)
return NotFound();
if (itemChoice.Inventory.HasValue)
{
switch (itemChoice.Inventory)
{
case int i when i <= 0:
return RedirectToAction(nameof(ViewPointOfSale), new {appId});
case int inventory when inventory < cartItem.Value:
return RedirectToAction(nameof(ViewPointOfSale), new {appId});
}
}
}
}
}
var store = await _AppService.GetStore(app);
store.AdditionalClaims.Add(new Claim(Policies.CanCreateInvoice.Key, store.Id));
@ -162,7 +188,6 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(InvoiceController.Checkout), "Invoice", new { invoiceId = invoice.Data.Id });
}
[HttpGet]
[Route("/apps/{appId}/crowdfund")]
[XFrameOptionsAttribute(XFrameOptionsAttribute.XFrameOptions.AllowAll)]
@ -241,6 +266,15 @@ namespace BTCPayServer.Controllers
price = choice.Price.Value;
if (request.Amount > price)
price = request.Amount;
if (choice.Inventory.HasValue)
{
if (choice.Inventory <= 0)
{
return NotFound("Option was out of stock");
}
}
}
if (!isAdmin && (settings.EnforceTargetAmount && info.TargetAmount.HasValue && price >

View File

@ -0,0 +1,136 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/openiddict/openiddict-core for more information concerning
* the license and the contributors participating to this project.
*/
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using BTCPayServer.Authentication.OpenId;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Models.Authorization;
using BTCPayServer.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
namespace BTCPayServer.Controllers
{
public class AuthorizationController : Controller
{
private readonly OpenIddictApplicationManager<BTCPayOpenIdClient> _applicationManager;
private readonly SignInManager<ApplicationUser> _signInManager;
private readonly OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> _authorizationManager;
private readonly UserManager<ApplicationUser> _userManager;
private readonly IOptions<IdentityOptions> _IdentityOptions;
public AuthorizationController(
OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
SignInManager<ApplicationUser> signInManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
UserManager<ApplicationUser> userManager,
IOptions<IdentityOptions> identityOptions)
{
_applicationManager = applicationManager;
_signInManager = signInManager;
_authorizationManager = authorizationManager;
_userManager = userManager;
_IdentityOptions = identityOptions;
}
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
[HttpGet("/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request)
{
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
if (application == null)
{
return View("Error",
new ErrorViewModel
{
Error = OpenIddictConstants.Errors.InvalidClient,
ErrorDescription =
"Details concerning the calling client application cannot be found in the database"
});
}
var userId = _userManager.GetUserId(User);
if (!string.IsNullOrEmpty(
await OpenIdExtensions.IsUserAuthorized(_authorizationManager, request, userId, application.Id)))
{
return await Authorize(request, "YES", false);
}
// Flow the request_id to allow OpenIddict to restore
// the original authorization request from the cache.
return View(new AuthorizeViewModel
{
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
RequestId = request.RequestId,
Scope = request.Scope
});
}
[Authorize(AuthenticationSchemes = Policies.CookieAuthentication)]
[HttpPost("/connect/authorize")]
public async Task<IActionResult> Authorize(OpenIdConnectRequest request,
string consent, bool createAuthorization = true)
{
var user = await _userManager.GetUserAsync(User);
if (user == null)
{
return View("Error",
new ErrorViewModel
{
Error = OpenIddictConstants.Errors.ServerError,
ErrorDescription = "The specified user could not be found"
});
}
string type = null;
switch (consent.ToUpperInvariant())
{
case "YESTEMPORARY":
type = OpenIddictConstants.AuthorizationTypes.AdHoc;
break;
case "YES":
type = OpenIddictConstants.AuthorizationTypes.Permanent;
break;
case "NO":
default:
// Notify OpenIddict that the authorization grant has been denied by the resource owner
// to redirect the user agent to the client application using the appropriate response_mode.
return Forbid(OpenIddictServerDefaults.AuthenticationScheme);
}
// Create a new authentication ticket.
var ticket =
await OpenIdExtensions.CreateAuthenticationTicket(_applicationManager, _authorizationManager,
_IdentityOptions.Value, _signInManager,
request, user);
if (createAuthorization)
{
var application = await _applicationManager.FindByClientIdAsync(request.ClientId);
var authorization = await _authorizationManager.CreateAsync(User, user.Id, application.Id,
type, ticket.GetScopes().ToImmutableArray(),
ticket.Properties.Items.ToImmutableDictionary());
ticket.SetInternalAuthorizationId(authorization.Id);
}
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
}
}

View File

@ -6,6 +6,7 @@ using BTCPayServer.Payments.Changelly;
using BTCPayServer.Rating;
using BTCPayServer.Services.Rates;
using Microsoft.AspNetCore.Mvc;
using BTCPayServer.Data;
namespace BTCPayServer.Controllers
{

View File

@ -66,13 +66,14 @@ namespace BTCPayServer.Controllers
Fiat = _CurrencyNameTable.DisplayFormatCurrency(prodInfo.Price, prodInfo.Currency),
TaxIncluded = _CurrencyNameTable.DisplayFormatCurrency(prodInfo.TaxIncluded, prodInfo.Currency),
NotificationEmail = invoice.NotificationEmail,
NotificationUrl = invoice.NotificationURL,
RedirectUrl = invoice.RedirectURL,
NotificationUrl = invoice.NotificationURL?.AbsoluteUri,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri,
ProductInformation = invoice.ProductInformation,
StatusException = invoice.ExceptionStatus,
Events = invoice.Events,
PosData = PosDataParser.ParsePosData(invoice.PosData),
StatusMessage = StatusMessage
StatusMessage = StatusMessage,
};
model.Addresses = invoice.HistoricalAddresses.Select(h =>
@ -85,20 +86,21 @@ namespace BTCPayServer.Controllers
var details = InvoicePopulatePayments(invoice);
model.CryptoPayments = details.CryptoPayments;
model.OnChainPayments = details.OnChainPayments;
model.OffChainPayments = details.OffChainPayments;
model.Payments = details.Payments;
return View(model);
}
private InvoiceDetailsModel InvoicePopulatePayments(InvoiceEntity invoice)
{
var model = new InvoiceDetailsModel();
model.Payments = invoice.GetPayments();
foreach (var data in invoice.GetPaymentMethods())
{
var accounting = data.Calculate();
var paymentMethodId = data.GetId();
var cryptoPayment = new InvoiceDetailsModel.CryptoPayment();
cryptoPayment.PaymentMethodId = paymentMethodId;
cryptoPayment.PaymentMethod = paymentMethodId.ToPrettyString();
cryptoPayment.Due = _CurrencyNameTable.DisplayFormatCurrency(accounting.Due.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
cryptoPayment.Paid = _CurrencyNameTable.DisplayFormatCurrency(accounting.CryptoPaid.ToDecimal(MoneyUnit.BTC), paymentMethodId.CryptoCode);
@ -108,43 +110,6 @@ namespace BTCPayServer.Controllers
cryptoPayment.Rate = ExchangeRate(data);
model.CryptoPayments.Add(cryptoPayment);
}
foreach (var payment in invoice.GetPayments())
{
var paymentData = payment.GetCryptoPaymentData();
//TODO: abstract
if (paymentData is Payments.Bitcoin.BitcoinLikePaymentData onChainPaymentData)
{
var m = new InvoiceDetailsModel.Payment();
m.Crypto = payment.GetPaymentMethodId().CryptoCode;
m.DepositAddress = onChainPaymentData.GetDestination();
int confirmationCount = onChainPaymentData.ConfirmationCount;
if (confirmationCount >= payment.Network.MaxTrackedConfirmation)
{
m.Confirmations = "At least " + (payment.Network.MaxTrackedConfirmation);
}
else
{
m.Confirmations = confirmationCount.ToString(CultureInfo.InvariantCulture);
}
m.TransactionId = onChainPaymentData.Outpoint.Hash.ToString();
m.ReceivedTime = payment.ReceivedTime;
m.TransactionLink = string.Format(CultureInfo.InvariantCulture, payment.Network.BlockExplorerLink, m.TransactionId);
m.Replaced = !payment.Accounted;
model.OnChainPayments.Add(m);
}
else
{
var lightningPaymentData = (LightningLikePaymentData)paymentData;
model.OffChainPayments.Add(new InvoiceDetailsModel.OffChainPayment()
{
Crypto = payment.Network.CryptoCode,
BOLT11 = lightningPaymentData.BOLT11
});
}
}
return model;
}
@ -202,7 +167,6 @@ namespace BTCPayServer.Controllers
return View(model);
}
//TODO: abstract
private async Task<PaymentModel> GetInvoiceModel(string invoiceId, PaymentMethodId paymentMethodId)
{
var invoice = await _InvoiceRepository.GetInvoice(invoiceId);
@ -286,7 +250,7 @@ namespace BTCPayServer.Controllers
MaxTimeMinutes = (int)(invoice.ExpirationTime - invoice.InvoiceTime).TotalMinutes,
ItemDesc = invoice.ProductInformation.ItemDesc,
Rate = ExchangeRate(paymentMethod),
MerchantRefLink = invoice.RedirectURL ?? "/",
MerchantRefLink = invoice.RedirectURL?.AbsoluteUri ?? "/",
RedirectAutomatically = invoice.RedirectAutomatically,
StoreName = store.StoreName,
PeerInfo = (paymentMethodDetails as LightningLikePaymentMethodDetails)?.NodeInfo,
@ -332,6 +296,7 @@ namespace BTCPayServer.Controllers
};
paymentMethodHandler.PreparePaymentModel(model, dto);
model.UISettings = paymentMethodHandler.GetCheckoutUISettings();
model.PaymentMethodId = paymentMethodId.ToString();
var expiration = TimeSpan.FromSeconds(model.ExpirationSeconds);
model.TimeLeft = expiration.PrettyPrint();
@ -459,7 +424,7 @@ namespace BTCPayServer.Controllers
Date = invoice.InvoiceTime,
InvoiceId = invoice.Id,
OrderId = invoice.OrderId ?? string.Empty,
RedirectUrl = invoice.RedirectURL ?? string.Empty,
RedirectUrl = invoice.RedirectURL?.AbsoluteUri ?? string.Empty,
AmountCurrency = _CurrencyNameTable.DisplayFormatCurrency(invoice.ProductInformation.Price, invoice.ProductInformation.Currency),
CanMarkInvalid = state.CanMarkInvalid(),
CanMarkComplete = state.CanMarkComplete(),

View File

@ -81,13 +81,7 @@ namespace BTCPayServer.Controllers
entity.ServerUrl = serverUrl;
entity.FullNotifications = invoice.FullNotifications || invoice.ExtendedNotifications;
entity.ExtendedNotifications = invoice.ExtendedNotifications;
if (invoice.NotificationURL != null &&
Uri.TryCreate(invoice.NotificationURL, UriKind.Absolute, out var notificationUri) &&
(notificationUri.Scheme == "http" || notificationUri.Scheme == "https"))
{
entity.NotificationURL = notificationUri.AbsoluteUri;
}
entity.NotificationURLTemplate = invoice.NotificationURL;
entity.NotificationEmail = invoice.NotificationEmail;
entity.BuyerInformation = Map<CreateInvoiceRequest, BuyerInformation>(invoice);
entity.PaymentTolerance = storeBlob.PaymentTolerance;
@ -119,9 +113,7 @@ namespace BTCPayServer.Controllers
entity.ProductInformation = Map<CreateInvoiceRequest, ProductInformation>(invoice);
entity.RedirectURL = invoice.RedirectURL ?? store.StoreWebsite;
if (!Uri.IsWellFormedUriString(entity.RedirectURL, UriKind.Absolute))
entity.RedirectURL = null;
entity.RedirectURLTemplate = invoice.RedirectURL ?? store.StoreWebsite;
entity.RedirectAutomatically =
invoice.RedirectAutomatically.GetValueOrDefault(storeBlob.RedirectAutomatically);

View File

@ -3,6 +3,7 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Models.ManageViewModels;
using Microsoft.AspNetCore.Mvc;
@ -105,7 +106,7 @@ namespace BTCPayServer.Controllers
}
// Strip spaces and hypens
var verificationCode = model.Code.Replace(" ", string.Empty).Replace("-", string.Empty);
var verificationCode = model.Code.Replace(" ", string.Empty, StringComparison.OrdinalIgnoreCase).Replace("-", string.Empty, StringComparison.OrdinalIgnoreCase);
var is2faTokenValid = await _userManager.VerifyTwoFactorTokenAsync(
user, _userManager.Options.Tokens.AuthenticatorTokenProvider, verificationCode);

View File

@ -19,6 +19,7 @@ using BTCPayServer.Services.Mails;
using System.Globalization;
using BTCPayServer.Security;
using BTCPayServer.Services.U2F;
using BTCPayServer.Data;
namespace BTCPayServer.Controllers
{

View File

@ -10,6 +10,7 @@ using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using BTCPayServer.Data;
namespace BTCPayServer.Controllers
{

View File

@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Authorization;
using BTCPayServer.Authentication;
using Microsoft.AspNetCore.Cors;
using System.Threading;
using BTCPayServer.Data;
namespace BTCPayServer.Controllers
{

View File

@ -49,6 +49,7 @@ namespace BTCPayServer.Controllers
private readonly TorServices _torServices;
private BTCPayServerOptions _Options;
private readonly AppService _AppService;
private readonly CheckConfigurationHostedService _sshState;
private readonly StoredFileRepository _StoredFileRepository;
private readonly FileService _FileService;
private readonly IEnumerable<IStorageProviderService> _StorageProviderServices;
@ -64,7 +65,8 @@ namespace BTCPayServer.Controllers
LightningConfigurationProvider lnConfigProvider,
TorServices torServices,
StoreRepository storeRepository,
AppService appService)
AppService appService,
CheckConfigurationHostedService sshState)
{
_Options = options;
_StoredFileRepository = storedFileRepository;
@ -78,6 +80,7 @@ namespace BTCPayServer.Controllers
_LnConfigProvider = lnConfigProvider;
_torServices = torServices;
_AppService = appService;
_sshState = sshState;
}
[Route("server/rates")]
@ -186,9 +189,8 @@ namespace BTCPayServer.Controllers
public IActionResult Maintenance()
{
MaintenanceViewModel vm = new MaintenanceViewModel();
vm.UserName = "btcpayserver";
vm.CanUseSSH = _sshState.CanUseSSH;
vm.DNSDomain = this.Request.Host.Host;
vm.SetConfiguredSSH(_Options.SSHSettings);
if (IPAddress.TryParse(vm.DNSDomain, out var unused))
vm.DNSDomain = null;
return View(vm);
@ -198,9 +200,9 @@ namespace BTCPayServer.Controllers
[HttpPost]
public async Task<IActionResult> Maintenance(MaintenanceViewModel vm, string command)
{
vm.CanUseSSH = _sshState.CanUseSSH;
if (!ModelState.IsValid)
return View(vm);
vm.SetConfiguredSSH(_Options.SSHSettings);
if (command == "changedomain")
{
if (string.IsNullOrWhiteSpace(vm.DNSDomain))
@ -254,7 +256,7 @@ namespace BTCPayServer.Controllers
}
}
var error = RunSSH(vm, $"changedomain.sh {vm.DNSDomain}");
var error = await RunSSH(vm, $"changedomain.sh {vm.DNSDomain}");
if (error != null)
return error;
@ -264,14 +266,14 @@ namespace BTCPayServer.Controllers
}
else if (command == "update")
{
var error = RunSSH(vm, $"btcpay-update.sh");
var error = await RunSSH(vm, $"btcpay-update.sh");
if (error != null)
return error;
StatusMessage = $"The server might restart soon if an update is available...";
}
else if (command == "clean")
{
var error = RunSSH(vm, $"btcpay-clean.sh");
var error = await RunSSH(vm, $"btcpay-clean.sh");
if (error != null)
return error;
StatusMessage = $"The old docker images will be cleaned soon...";
@ -301,43 +303,13 @@ namespace BTCPayServer.Controllers
return BadRequest();
}
private IActionResult RunSSH(MaintenanceViewModel vm, string ssh)
private async Task<IActionResult> RunSSH(MaintenanceViewModel vm, string command)
{
ssh = $"sudo bash -c '. /etc/profile.d/btcpay-env.sh && nohup {ssh} > /dev/null 2>&1 & disown'";
var sshClient = _Options.SSHSettings == null ? vm.CreateSSHClient(this.Request.Host.Host)
: new SshClient(_Options.SSHSettings.CreateConnectionInfo());
if (_Options.TrustedFingerprints.Count != 0)
{
sshClient.HostKeyReceived += (object sender, Renci.SshNet.Common.HostKeyEventArgs e) =>
{
if (_Options.TrustedFingerprints.Count == 0)
{
Logs.Configuration.LogWarning($"SSH host fingerprint for {e.HostKeyName} is untrusted, start BTCPay with -sshtrustedfingerprints \"{Encoders.Hex.EncodeData(e.FingerPrint)}\"");
e.CanTrust = true; // Not a typo, we want the connection to succeed with a warning
}
else
{
e.CanTrust = _Options.IsTrustedFingerprint(e.FingerPrint, e.HostKey);
if (!e.CanTrust)
Logs.Configuration.LogError($"SSH host fingerprint for {e.HostKeyName} is untrusted, start BTCPay with -sshtrustedfingerprints \"{Encoders.Hex.EncodeData(e.FingerPrint)}\"");
}
};
}
else
{
}
SshClient sshClient = null;
try
{
sshClient.Connect();
}
catch (Renci.SshNet.Common.SshAuthenticationException)
{
ModelState.AddModelError(nameof(vm.Password), "Invalid credentials");
sshClient.Dispose();
return View(vm);
sshClient = await _Options.SSHSettings.ConnectAsync();
}
catch (Exception ex)
{
@ -346,30 +318,31 @@ namespace BTCPayServer.Controllers
{
message = aggrEx.InnerException.Message;
}
ModelState.AddModelError(nameof(vm.UserName), $"Connection problem ({message})");
sshClient.Dispose();
ModelState.AddModelError(string.Empty, $"Connection problem ({message})");
return View(vm);
}
var sshCommand = sshClient.CreateCommand(ssh);
sshCommand.CommandTimeout = TimeSpan.FromMinutes(1.0);
sshCommand.BeginExecute(ar =>
{
try
{
Logs.PayServer.LogInformation("Running SSH command: " + ssh);
var result = sshCommand.EndExecute(ar);
Logs.PayServer.LogInformation("SSH command executed: " + result);
}
catch (Exception ex)
{
Logs.PayServer.LogWarning("Error while executing SSH command: " + ex.Message);
}
sshClient.Dispose();
});
_ = RunSSHCore(sshClient, $". /etc/profile.d/btcpay-env.sh && nohup {command} > /dev/null 2>&1 & disown");
return null;
}
private static async Task RunSSHCore(SshClient sshClient, string ssh)
{
try
{
Logs.PayServer.LogInformation("Running SSH command: " + ssh);
var result = await sshClient.RunBash(ssh, TimeSpan.FromMinutes(1.0));
Logs.PayServer.LogInformation($"SSH command executed with exit status {result.ExitStatus}. Output: {result.Output}");
}
catch (Exception ex)
{
Logs.PayServer.LogWarning("Error while executing SSH command: " + ex.Message);
}
finally
{
sshClient.Dispose();
}
}
private static bool IsAdmin(IList<string> roles)
{
return roles.Contains(Roles.ServerAdmin, StringComparer.Ordinal);
@ -531,7 +504,7 @@ namespace BTCPayServer.Controllers
Link = this.Request.GetAbsoluteUriNoPathBase(externalService.Value).AbsoluteUri
});
}
if (_Options.SSHSettings != null)
if (_sshState.CanUseSSH)
{
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
{

View File

@ -330,14 +330,15 @@ namespace BTCPayServer.Controllers
private IActionResult ShowAddresses(DerivationSchemeViewModel vm, DerivationSchemeSettings strategy)
{
vm.DerivationScheme = strategy.AccountDerivation.ToString();
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
if (!string.IsNullOrEmpty(vm.DerivationScheme))
{
var line = strategy.AccountDerivation.GetLineFor(DerivationFeature.Deposit);
var line = strategy.AccountDerivation.GetLineFor(deposit);
for (int i = 0; i < 10; i++)
{
var address = line.Derive((uint)i);
vm.AddressSamples.Add((DerivationStrategyBase.GetKeyPath(DerivationFeature.Deposit).Derive((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork).ToString()));
vm.AddressSamples.Add((deposit.GetKeyPath((uint)i).ToString(), address.ScriptPubKey.GetDestinationAddress(strategy.Network.NBitcoinNetwork).ToString()));
}
}
vm.Confirmation = true;

View File

@ -178,7 +178,7 @@ namespace BTCPayServer.Controllers
private bool CanUseInternalLightning()
{
return (_BTCPayEnv.IsDevelopping || User.IsInRole(Roles.ServerAdmin));
return (_BTCPayEnv.IsDevelopping || User.IsInRole(Roles.ServerAdmin) || _CssThemeManager.AllowLightningInternalNodeForAll);
}
}
}

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using BTCPayServer.Authentication;
using BTCPayServer.Configuration;
using BTCPayServer.Data;
using BTCPayServer.HostedServices;
using BTCPayServer.Models;
using BTCPayServer.Models.AppViewModels;
using BTCPayServer.Models.StoreViewModels;
@ -58,7 +59,8 @@ namespace BTCPayServer.Controllers
ChangellyClientProvider changellyClientProvider,
IOptions<MvcJsonOptions> mvcJsonOptions,
IHostingEnvironment env, IHttpClientFactory httpClientFactory,
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary)
PaymentMethodHandlerDictionary paymentMethodHandlerDictionary,
CssThemeManager cssThemeManager)
{
_RateFactory = rateFactory;
_Repo = repo;
@ -72,6 +74,7 @@ namespace BTCPayServer.Controllers
_Env = env;
_httpClientFactory = httpClientFactory;
_paymentMethodHandlerDictionary = paymentMethodHandlerDictionary;
_CssThemeManager = cssThemeManager;
_NetworkProvider = networkProvider;
_ExplorerProvider = explorerProvider;
_FeeRateProvider = feeRateProvider;
@ -95,6 +98,7 @@ namespace BTCPayServer.Controllers
IHostingEnvironment _Env;
private IHttpClientFactory _httpClientFactory;
private readonly PaymentMethodHandlerDictionary _paymentMethodHandlerDictionary;
private readonly CssThemeManager _CssThemeManager;
[TempData]
public string StatusMessage
@ -502,7 +506,7 @@ namespace BTCPayServer.Controllers
}
var changellyEnabled = storeBlob.ChangellySettings != null && storeBlob.ChangellySettings.Enabled;
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.ThirdPartyPaymentMethod()
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.AdditionalPaymentMethod()
{
Enabled = changellyEnabled,
Action = nameof(UpdateChangellySettings),
@ -510,7 +514,7 @@ namespace BTCPayServer.Controllers
});
var coinSwitchEnabled = storeBlob.CoinSwitchSettings != null && storeBlob.CoinSwitchSettings.Enabled;
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.ThirdPartyPaymentMethod()
vm.ThirdPartyPaymentMethods.Add(new StoreViewModel.AdditionalPaymentMethod()
{
Enabled = coinSwitchEnabled,
Action = nameof(UpdateCoinSwitchSettings),

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Models;
using BTCPayServer.Models.StoreViewModels;
using BTCPayServer.Security;

View File

@ -1,68 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Payments;
using BTCPayServer.Services.Invoices;
using NBitcoin;
namespace BTCPayServer.Data
{
public class AddressInvoiceData
{
/// <summary>
/// Some crypto currencies share same address prefix
/// For not having exceptions thrown by two address on different network, we suffix by "#CRYPTOCODE"
/// </summary>
[Obsolete("Use GetHash instead")]
public string Address
{
get; set;
}
#pragma warning disable CS0618
public string GetAddress()
{
if (Address == null)
return null;
var index = Address.LastIndexOf("#", StringComparison.InvariantCulture);
if (index == -1)
return Address;
return Address.Substring(0, index);
}
public AddressInvoiceData Set(string address, PaymentMethodId paymentMethodId)
{
Address = address + "#" + paymentMethodId.ToString();
return this;
}
public PaymentMethodId GetpaymentMethodId()
{
if (Address == null)
return null;
var index = Address.LastIndexOf("#", StringComparison.InvariantCulture);
// Legacy AddressInvoiceData does not have the paymentMethodId attached to the Address
if (index == -1)
return PaymentMethodId.Parse("BTC");
/////////////////////////
return PaymentMethodId.Parse(Address.Substring(index + 1));
}
#pragma warning restore CS0618
public InvoiceData InvoiceData
{
get; set;
}
public string InvoiceDataId
{
get; set;
}
public DateTimeOffset? CreatedTime
{
get; set;
}
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Payments;
namespace BTCPayServer.Data
{
public static class AddressInvoiceDataExtensions
{
#pragma warning disable CS0618
public static string GetAddress(this AddressInvoiceData addressInvoiceData)
{
if (addressInvoiceData.Address == null)
return null;
var index = addressInvoiceData.Address.LastIndexOf("#", StringComparison.InvariantCulture);
if (index == -1)
return addressInvoiceData.Address;
return addressInvoiceData.Address.Substring(0, index);
}
public static AddressInvoiceData Set(this AddressInvoiceData addressInvoiceData, string address, PaymentMethodId paymentMethodId)
{
addressInvoiceData.Address = address + "#" + paymentMethodId.ToString();
return addressInvoiceData;
}
public static PaymentMethodId GetpaymentMethodId(this AddressInvoiceData addressInvoiceData)
{
if (addressInvoiceData.Address == null)
return null;
var index = addressInvoiceData.Address.LastIndexOf("#", StringComparison.InvariantCulture);
// Legacy AddressInvoiceData does not have the paymentMethodId attached to the Address
if (index == -1)
return PaymentMethodId.Parse("BTC");
/////////////////////////
return PaymentMethodId.Parse(addressInvoiceData.Address.Substring(index + 1));
}
#pragma warning restore CS0618
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace BTCPayServer.Data
{
public static class HistoricalAddressInvoiceDataExtensions
{
#pragma warning disable CS0618
public static Payments.PaymentMethodId GetPaymentMethodId(this HistoricalAddressInvoiceData historicalAddressInvoiceData)
{
return string.IsNullOrEmpty(historicalAddressInvoiceData.CryptoCode) ? new Payments.PaymentMethodId("BTC", Payments.PaymentTypes.BTCLike)
: Payments.PaymentMethodId.Parse(historicalAddressInvoiceData.CryptoCode);
}
public static string GetAddress(this HistoricalAddressInvoiceData historicalAddressInvoiceData)
{
if (historicalAddressInvoiceData.Address == null)
return null;
var index = historicalAddressInvoiceData.Address.IndexOf("#", StringComparison.InvariantCulture);
if (index == -1)
return historicalAddressInvoiceData.Address;
return historicalAddressInvoiceData.Address.Substring(0, index);
}
public static HistoricalAddressInvoiceData SetAddress(this HistoricalAddressInvoiceData historicalAddressInvoiceData, string depositAddress, string cryptoCode)
{
historicalAddressInvoiceData.Address = depositAddress + "#" + cryptoCode;
historicalAddressInvoiceData.CryptoCode = cryptoCode;
return historicalAddressInvoiceData;
}
#pragma warning restore CS0618
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BTCPayServer.Services.Invoices;
namespace BTCPayServer.Data
{
public static class InvoiceDataExtensions
{
public static InvoiceState GetInvoiceState(this InvoiceData invoiceData)
{
return new InvoiceState(invoiceData.Status, invoiceData.ExceptionStatus);
}
}
}

Some files were not shown because too many files have changed in this diff Show More