Compare commits
151 Commits
v1.0.3.120
...
v1.0.3.129
Author | SHA1 | Date | |
---|---|---|---|
3a87dc2223 | |||
67d3875c98 | |||
77d0f3d85c | |||
0798b95c6b | |||
c247e275f6 | |||
f17a359893 | |||
8e15707dc7 | |||
d890753ee2 | |||
00b82ad07a | |||
a21948cf16 | |||
eb583ba628 | |||
a4b61f8aab | |||
7208e63155 | |||
8f464b0838 | |||
233b799a46 | |||
d99beb9811 | |||
fefc45854e | |||
0047a5388d | |||
66064bd2eb | |||
6bd601137a | |||
eae913f809 | |||
bc8e7ce888 | |||
1ec342da1e | |||
a169179061 | |||
57b436417c | |||
0229b560e7 | |||
3c51bd3b23 | |||
b7ba97d86f | |||
0eb58e9a91 | |||
f257f9f91d | |||
8971dbc2f9 | |||
5b4e78f8d1 | |||
27f20386df | |||
9154e4264d | |||
7457e99451 | |||
c5227d9996 | |||
1447b5e8be | |||
efdb131c33 | |||
d9a0db3efc | |||
cb8c077c1e | |||
9688798a4a | |||
9a9e31c759 | |||
55c0c0ea6f | |||
43ee22f965 | |||
989a7b863e | |||
709ee54ac2 | |||
664b920a39 | |||
7ea3312534 | |||
3e9bee2d44 | |||
a571f77a40 | |||
13f2be7811 | |||
3d00611ddf | |||
576734b5cb | |||
6cd60732b5 | |||
1635e1e3fb | |||
b29b46bbc7 | |||
e45f1afd51 | |||
288dc9b626 | |||
81c6a76ea2 | |||
182f9b3cf6 | |||
e71fd4950f | |||
72d519bb45 | |||
e743b2e457 | |||
f932a34581 | |||
3543d9bd60 | |||
d387834c6c | |||
6ea15411b6 | |||
63df6ac5eb | |||
039bee5b65 | |||
be5597085b | |||
6b355cbe1b | |||
dec5d19a2f | |||
ff533994d8 | |||
221e2c7898 | |||
fb77fddcc3 | |||
c37086e000 | |||
3d6783b743 | |||
c479e6aae5 | |||
59a770e0d7 | |||
140259e737 | |||
59a391dcc9 | |||
3a1cdefa09 | |||
7be104f486 | |||
d90a65975c | |||
4e53f59a9c | |||
8e58fc128d | |||
756b6e9692 | |||
23d546c559 | |||
6d4ea6a951 | |||
f9b5dcd4a6 | |||
eab679cb2b | |||
ddf8b20091 | |||
f1457582fe | |||
7841f79f31 | |||
56e5acfb65 | |||
6b777878e3 | |||
428c7c5444 | |||
f8427eb801 | |||
2a53c056ca | |||
21d555ee6b | |||
d79fda166f | |||
c8025ebaac | |||
42d7ad02b0 | |||
21556d4c07 | |||
89a7166c1b | |||
5d6c28c997 | |||
717cadc64b | |||
3dac7ef3f3 | |||
056cb60d5d | |||
bb4e92ec50 | |||
9218fb6463 | |||
d9baea4c38 | |||
6df6537cf9 | |||
72d199f390 | |||
233bce578b | |||
63472d54d7 | |||
db57b5ae80 | |||
8896d89908 | |||
8e07bf3ffb | |||
6194d0ad44 | |||
138532d3d4 | |||
4716b704d4 | |||
109e576811 | |||
631c878722 | |||
4cbcdb8af5 | |||
d24628a386 | |||
7e714bdfa2 | |||
e3283fb29b | |||
be0285155f | |||
1c055a7282 | |||
8d3cdd39ca | |||
010ba4d5b6 | |||
d176a16caa | |||
fd4a27c1a3 | |||
ae73858e23 | |||
1427e5458b | |||
8853cf9f83 | |||
de7f22bcbc | |||
b8b2fa29d7 | |||
476a241936 | |||
dc97982fad | |||
e488f93b17 | |||
e6e9668bbb | |||
56976898bd | |||
221ff05c49 | |||
8f719d3e33 | |||
67c2abca2d | |||
36046f08f7 | |||
3c4455c23c | |||
e3db2e2b76 | |||
5567a26b33 |
.circleci
BTCPayServer.Common
BTCPayServer.Data
BTCPayServer.Data.csproj
Data
APIKeyData.csAddressInvoiceData.csAppData.csApplicationDbContext.csApplicationDbContextFactory.csApplicationUser.csBTCPayOpenIdAuthorization.csBTCPayOpenIdClient.csBTCPayOpenIdToken.csHistoricalAddressInvoiceData.csInvoiceData.csInvoiceEventData.csPairedSINData.csPairingCodeData.csPaymentData.csPaymentRequestData.csPendingInvoiceData.csRefundData.csSettingData.csStoreData.csStoredFile.csU2FDevice.csUserStore.csWalletData.csWalletTransactionData.cs
Migrations
20170913143004_Init.cs20170926073744_Settings.cs20170926084408_RequiresEmailConfirmation.cs20171006013443_AddressMapping.cs20171010082424_Tokens.cs20171012020112_PendingInvoices.cs20171023101754_StoreBlob.cs20171024163354_RenewUsedAddresses.cs20171105235734_PaymentAccounted.cs20171221054550_AltcoinSupport.cs20180106095215_DerivationStrategies.cs20180109021122_defaultcrypto.cs20180114123253_events.cs20180402095640_appdata.cs20180429083930_legacyapikey.cs20180719095626_CanDeleteStores.cs20190121133309_AddPaymentRequests.cs20190219032533_AppsTagging.cs20190225091644_AddOpenIddict.cs20190324141717_AddFiles.cs20190425081749_AddU2fDevices.cs20190701082105_sort_paymentrequests.cs20190802142637_WalletData.csApplicationDbContextModelSnapshot.cs
MigrationsExtensions.csBTCPayServer.Rating
BTCPayServer.Tests
AuthenticationTests.csBTCPayServer.Tests.csprojBTCPayServerTester.csCoinSwitchTests.csPaymentHandlerTest.csRateRulesTest.csSeleniumTester.csSeleniumTests.csTestAccount.csTestUtils.csUnitTest1.csUtilitiesTests.csdocker-compose.yml
BTCPayServer
Authentication/OpenId
AuthorizationCodeGrantTypeEventHandler.csAuthorizationEventHandler.csBaseOpenIdGrantHandler.csClientCredentialsGrantTypeEventHandler.csLogoutEventHandler.csOpenIdExtensions.csOpenIdGrantHandlerCheckCanSignIn.csPasswordGrantTypeEventHandler.csRefreshTokenGrantTypeEventHandler.cs
BTCPayServer.csprojConfiguration
Controllers
AccountController.csAppsController.Crowdfund.csAppsController.PointOfSale.csAppsController.csAppsPublicController.csAuthorizationController.csChangellyController.csInvoiceController.UI.csInvoiceController.csManageController.2FA.csManageController.csPaymentRequestController.csPublicController.csRateController.csServerController.csStoresController.BTCLike.csStoresController.Email.csStoresController.LightningLike.csStoresController.csUserStoresController.csWalletsController.PSBT.csWalletsController.cs
Data
AddressInvoiceData.csAddressInvoiceDataExtensions.csHistoricalAddressInvoiceDataExtensions.csInvoiceDataExtensions.csPaymentRequestDataExtensions.csStoreBlob.csStoreData.csStoreDataExtensions.csWalletDataExtensions.csWalletTransactionDataExtensions.cs
DerivationSchemeParser.csEvents
Extensions.csExtensions
HostedServices
AppInventoryUpdaterHostedService.csCheckConfigurationHostedService.csCssThemeManager.csDynamicDnsHostedService.csInvoiceNotificationManager.cs
Hosting
IStartupTask.csMigrationStartupTask.csMigrations
20170913143004_Init.Designer.cs20170926073744_Settings.Designer.cs20170926084408_RequiresEmailConfirmation.Designer.cs20171006013443_AddressMapping.Designer.cs20171010082424_Tokens.Designer.cs20171012020112_PendingInvoices.Designer.cs20171023101754_StoreBlob.Designer.cs20171024163354_RenewUsedAddresses.Designer.cs20171105235734_PaymentAccounted.Designer.cs20171221054550_AltcoinSupport.Designer.cs20180106095215_DerivationStrategies.Designer.cs20180109021122_defaultcrypto.Designer.cs20180114123253_events.Designer.cs20180402095640_appdata.Designer.cs20180429083930_legacyapikey.Designer.cs20180719095626_CanDeleteStores.Designer.cs20190121133309_AddPaymentRequests.Designer.cs20190219032533_AppsTagging.Designer.cs20190225091644_AddOpenIddict.Designer.cs20190324141717_AddFiles.Designer.cs20190425081749_AddU2fDevices.Designer.cs20190701082105_sort_paymentrequests.Designer.csApplicationDbContextModelSnapshot.cs
Models
AppViewModels
Authorization
InvoicingModels
ManageViewModels
PaymentRequestViewModels
ServerViewModels
StoreViewModels
WalletViewModels
PaymentRequest
Payments
Bitcoin
IPaymentMethodHandler.csLightning
PaymentTypes.Bitcoin.csPaymentTypes.Lightning.csPaymentTypes.csSSH
Security
Services
Apps
DynamicDnsSettings.csHardwareWalletService.csInvoices
LedgerHardwareWalletService.csMails
MigrationSettings.csPaymentRequests
PoliciesSettings.csSafe.csSettingsRepository.csStores
WalletRepository.csStorage/Services
FileService.cs
Providers
U2F/Models
Views
Account
Apps
AppsPublic
Authorization
Home
Invoice
Checkout-Body.cshtmlCheckout.cshtmlCheckoutNoScript.cshtmlInvoice.cshtmlInvoicePaymentsPartial.cshtmlListInvoices.cshtml
Manage
AddU2FDevice.cshtmlEnableAuthenticator.cshtmlGenerateRecoveryCodes.cshtmlTwoFactorAuthentication.cshtml
PaymentRequest
PublicLightningNodeInfo
Server
DynamicDnsService.cshtmlDynamicDnsServices.cshtmlLightningWalletServices.cshtmlListUsers.cshtmlLndServices.cshtmlMaintenance.cshtmlP2PService.cshtmlPolicies.cshtml
Shared
Bitcoin_Lightning_LikeMethodCheckout.cshtmlBitcoin_Lightning_LikeMethodCheckoutNoScript.cshtmlConfirm.cshtmlEmailsBody.cshtmlNotificationEmailWarning.cshtmlViewBitcoinLikePaymentData.cshtmlViewLightningLikePaymentData.cshtml_Layout.cshtml_StatusMessage.cshtml
Stores
UserStores
Wallets
_ViewImports.cshtmlwwwroot
cart
checkout/js
crowdfund
img
js
locales
main/css
paybutton
pos-admin
products/js
Build
README.mdamd64.Dockerfilearm32v7.Dockerfilebtcpayserver.sln@ -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
|
||||
|
@ -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>())
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.9" />
|
||||
<PackageReference Include="NBitcoin" Version="4.1.2.37" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="2.0.0.17" />
|
||||
<PackageReference Include="NBitcoin" Version="4.2.4" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="2.0.0.19" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
11
BTCPayServer.Data/BTCPayServer.Data.csproj
Normal file
11
BTCPayServer.Data/BTCPayServer.Data.csproj
Normal 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>
|
36
BTCPayServer.Data/Data/AddressInvoiceData.cs
Normal file
36
BTCPayServer.Data/Data/AddressInvoiceData.cs
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
@ -61,6 +57,9 @@ namespace BTCPayServer.Data
|
||||
get; set;
|
||||
}
|
||||
|
||||
public DbSet<WalletData> Wallets { get; set; }
|
||||
public DbSet<WalletTransactionData> WalletTransactions { get; set; }
|
||||
|
||||
public DbSet<StoreData> Stores
|
||||
{
|
||||
get; set;
|
||||
@ -226,11 +225,23 @@ 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);
|
||||
|
||||
builder.Entity<WalletTransactionData>()
|
||||
.HasKey(o => new
|
||||
{
|
||||
o.WalletDataId,
|
||||
#pragma warning disable CS0618
|
||||
o.TransactionId
|
||||
#pragma warning restore CS0618
|
||||
});
|
||||
builder.Entity<WalletTransactionData>()
|
||||
.HasOne(o => o.WalletData)
|
||||
.WithMany(w => w.WalletTransactions).OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.UseOpenIddict<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>, BTCPayOpenIdToken, string>();
|
||||
|
||||
}
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
@ -1,6 +1,6 @@
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId.Models
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class BTCPayOpenIdAuthorization : OpenIddictAuthorization<string, BTCPayOpenIdClient, BTCPayOpenIdToken> { }
|
||||
}
|
||||
}
|
@ -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>
|
||||
{
|
@ -1,6 +1,6 @@
|
||||
using OpenIddict.EntityFrameworkCore.Models;
|
||||
|
||||
namespace BTCPayServer.Authentication.OpenId.Models
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class BTCPayOpenIdToken : OpenIddictToken<string, BTCPayOpenIdClient, BTCPayOpenIdAuthorization> { }
|
||||
}
|
||||
}
|
@ -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;
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
45
BTCPayServer.Data/Data/PaymentRequestData.cs
Normal file
45
BTCPayServer.Data/Data/PaymentRequestData.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
102
BTCPayServer.Data/Data/StoreData.cs
Normal file
102
BTCPayServer.Data/Data/StoreData.cs
Normal 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
|
||||
}
|
||||
}
|
@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using BTCPayServer.Models;
|
||||
|
||||
namespace BTCPayServer.Storage.Models
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class StoredFile
|
||||
{
|
@ -1,8 +1,7 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using BTCPayServer.Models;
|
||||
|
||||
namespace BTCPayServer.Services.U2F.Models
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class U2FDevice
|
||||
{
|
@ -1,5 +1,4 @@
|
||||
using BTCPayServer.Models;
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
84
BTCPayServer.Data/Data/WalletData.cs
Normal file
84
BTCPayServer.Data/Data/WalletData.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class WalletData
|
||||
{
|
||||
[System.ComponentModel.DataAnnotations.Key]
|
||||
public string Id { get; set; }
|
||||
|
||||
public List<WalletTransactionData> WalletTransactions { get; set; }
|
||||
|
||||
public byte[] Blob { get; set; }
|
||||
}
|
||||
|
||||
public class Label
|
||||
{
|
||||
public Label(string value, string color)
|
||||
{
|
||||
if (value == null)
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
if (color == null)
|
||||
throw new ArgumentNullException(nameof(color));
|
||||
Value = value;
|
||||
Color = color;
|
||||
}
|
||||
|
||||
public string Value { get; }
|
||||
public string Color { get; }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
Label item = obj as Label;
|
||||
if (item == null)
|
||||
return false;
|
||||
return Value.Equals(item.Value, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
public static bool operator ==(Label a, Label b)
|
||||
{
|
||||
if (System.Object.ReferenceEquals(a, b))
|
||||
return true;
|
||||
if (((object)a == null) || ((object)b == null))
|
||||
return false;
|
||||
return a.Value == b.Value;
|
||||
}
|
||||
|
||||
public static bool operator !=(Label a, Label b)
|
||||
{
|
||||
return !(a == b);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return Value.GetHashCode(StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public class WalletBlobInfo
|
||||
{
|
||||
public Dictionary<string, string> LabelColors { get; set; } = new Dictionary<string, string>();
|
||||
|
||||
public IEnumerable<Label> GetLabels(WalletTransactionInfo transactionInfo)
|
||||
{
|
||||
foreach (var label in transactionInfo.Labels)
|
||||
{
|
||||
if (LabelColors.TryGetValue(label, out var color))
|
||||
{
|
||||
yield return new Label(label, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Label> GetLabels()
|
||||
{
|
||||
foreach (var kv in LabelColors)
|
||||
{
|
||||
yield return new Label(kv.Key, kv.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
24
BTCPayServer.Data/Data/WalletTransactionData.cs
Normal file
24
BTCPayServer.Data/Data/WalletTransactionData.cs
Normal 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>();
|
||||
}
|
||||
}
|
6
BTCPayServer/Migrations/20170913143004_Init.cs → BTCPayServer.Data/Migrations/20170913143004_Init.cs
6
BTCPayServer/Migrations/20170913143004_Init.cs → BTCPayServer.Data/Migrations/20170913143004_Init.cs
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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)
|
56
BTCPayServer.Data/Migrations/20190802142637_WalletData.cs
Normal file
56
BTCPayServer.Data/Migrations/20190802142637_WalletData.cs
Normal file
@ -0,0 +1,56 @@
|
||||
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)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Wallets",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<string>(nullable: false),
|
||||
Blob = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Wallets", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "WalletTransactions",
|
||||
columns: table => new
|
||||
{
|
||||
WalletDataId = table.Column<string>(nullable: false),
|
||||
TransactionId = table.Column<string>(nullable: false),
|
||||
Labels = table.Column<string>(nullable: true),
|
||||
Blob = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_WalletTransactions", x => new { x.WalletDataId, x.TransactionId });
|
||||
table.ForeignKey(
|
||||
name: "FK_WalletTransactions_Wallets_WalletDataId",
|
||||
column: x => x.WalletDataId,
|
||||
principalTable: "Wallets",
|
||||
principalColumn: "Id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "WalletTransactions");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "Wallets");
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
24
BTCPayServer.Data/MigrationsExtensions.cs
Normal file
24
BTCPayServer.Data/MigrationsExtensions.cs
Normal 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";
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,61 +70,73 @@ namespace BTCPayServer.Services.Rates
|
||||
//}
|
||||
//b.AppendLine("}.ToArray()");
|
||||
AvailableExchanges = new CoinAverageExchanges();
|
||||
foreach(var item in
|
||||
foreach (var item in
|
||||
new[] {
|
||||
(DisplayName: "BitBargain", Name: "bitbargain"),
|
||||
(DisplayName: "Tidex", Name: "tidex"),
|
||||
(DisplayName: "LocalBitcoins", Name: "localbitcoins"),
|
||||
(DisplayName: "EtherDelta", Name: "etherdelta"),
|
||||
(DisplayName: "Kraken", Name: "kraken"),
|
||||
(DisplayName: "BitBay", Name: "bitbay"),
|
||||
(DisplayName: "Independent Reserve", Name: "independentreserve"),
|
||||
(DisplayName: "Exmoney", Name: "exmoney"),
|
||||
(DisplayName: "Bitcoin.co.id", Name: "bitcoin_co_id"),
|
||||
(DisplayName: "Huobi", Name: "huobi"),
|
||||
(DisplayName: "GDAX", Name: "gdax"),
|
||||
(DisplayName: "Coincheck", Name: "coincheck"),
|
||||
(DisplayName: "Bittylicious", Name: "bittylicious"),
|
||||
(DisplayName: "Gemini", Name: "gemini"),
|
||||
(DisplayName: "Bit2C", Name: "bit2c"),
|
||||
(DisplayName: "Luno", Name: "luno"),
|
||||
(DisplayName: "Negocie Coins", Name: "negociecoins"),
|
||||
(DisplayName: "FYB-SE", Name: "fybse"),
|
||||
(DisplayName: "Hitbtc", Name: "hitbtc"),
|
||||
(DisplayName: "Bitex.la", Name: "bitex"),
|
||||
(DisplayName: "Korbit", Name: "korbit"),
|
||||
(DisplayName: "itBit", Name: "itbit"),
|
||||
(DisplayName: "Okex", Name: "okex"),
|
||||
(DisplayName: "Bitsquare", Name: "bitsquare"),
|
||||
(DisplayName: "Bitfinex", Name: "bitfinex"),
|
||||
(DisplayName: "CoinMate", Name: "coinmate"),
|
||||
(DisplayName: "Bitstamp", Name: "bitstamp"),
|
||||
(DisplayName: "Cryptonit", Name: "cryptonit"),
|
||||
(DisplayName: "Foxbit", Name: "foxbit"),
|
||||
(DisplayName: "QuickBitcoin", Name: "quickbitcoin"),
|
||||
(DisplayName: "Poloniex", Name: "poloniex"),
|
||||
(DisplayName: "Bit-Z", Name: "bitz"),
|
||||
(DisplayName: "Liqui", Name: "liqui"),
|
||||
(DisplayName: "BitKonan", Name: "bitkonan"),
|
||||
(DisplayName: "Kucoin", Name: "kucoin"),
|
||||
(DisplayName: "Binance", Name: "binance"),
|
||||
(DisplayName: "Rock Trading", Name: "rocktrading"),
|
||||
(DisplayName: "Mercado Bitcoin", Name: "mercado"),
|
||||
(DisplayName: "Coinsecure", Name: "coinsecure"),
|
||||
(DisplayName: "Idex", Name: "idex"),
|
||||
(DisplayName: "Coinfloor", Name: "coinfloor"),
|
||||
(DisplayName: "bitFlyer", Name: "bitflyer"),
|
||||
(DisplayName: "BTCTurk", Name: "btcturk"),
|
||||
(DisplayName: "Bittrex", Name: "bittrex"),
|
||||
(DisplayName: "CampBX", Name: "campbx"),
|
||||
(DisplayName: "Zaif", Name: "zaif"),
|
||||
(DisplayName: "FYB-SG", Name: "fybsg"),
|
||||
(DisplayName: "Quoine", Name: "quoine"),
|
||||
(DisplayName: "Okex", Name: "okex"),
|
||||
(DisplayName: "Bitfinex", Name: "bitfinex"),
|
||||
(DisplayName: "Bittylicious", Name: "bittylicious"),
|
||||
(DisplayName: "BTC Markets", Name: "btcmarkets"),
|
||||
(DisplayName: "Kucoin", Name: "kucoin"),
|
||||
(DisplayName: "IDAX", Name: "idax"),
|
||||
(DisplayName: "Kraken", Name: "kraken"),
|
||||
(DisplayName: "Bit2C", Name: "bit2c"),
|
||||
(DisplayName: "Mercado Bitcoin", Name: "mercado"),
|
||||
(DisplayName: "CEX.IO", Name: "cex"),
|
||||
(DisplayName: "Bitex.la", Name: "bitex"),
|
||||
(DisplayName: "Quoine", Name: "quoine"),
|
||||
(DisplayName: "Stex", Name: "stex"),
|
||||
(DisplayName: "CoinTiger", Name: "cointiger"),
|
||||
(DisplayName: "Poloniex", Name: "poloniex"),
|
||||
(DisplayName: "Zaif", Name: "zaif"),
|
||||
(DisplayName: "Huobi", Name: "huobi"),
|
||||
(DisplayName: "QuickBitcoin", Name: "quickbitcoin"),
|
||||
(DisplayName: "Tidex", Name: "tidex"),
|
||||
(DisplayName: "Tokenomy", Name: "tokenomy"),
|
||||
(DisplayName: "Bitcoin.co.id", Name: "bitcoin_co_id"),
|
||||
(DisplayName: "Kryptono", Name: "kryptono"),
|
||||
(DisplayName: "Bitso", Name: "bitso"),
|
||||
(DisplayName: "Korbit", Name: "korbit"),
|
||||
(DisplayName: "Yobit", Name: "yobit"),
|
||||
(DisplayName: "BitBargain", Name: "bitbargain"),
|
||||
(DisplayName: "Livecoin", Name: "livecoin"),
|
||||
(DisplayName: "Hotbit", Name: "hotbit"),
|
||||
(DisplayName: "Coincheck", Name: "coincheck"),
|
||||
(DisplayName: "Binance", Name: "binance"),
|
||||
(DisplayName: "Bit-Z", Name: "bitz"),
|
||||
(DisplayName: "Coinbase Pro", Name: "coinbasepro"),
|
||||
(DisplayName: "Rock Trading", Name: "rocktrading"),
|
||||
(DisplayName: "Bittrex", Name: "bittrex"),
|
||||
(DisplayName: "BitBay", Name: "bitbay"),
|
||||
(DisplayName: "Tokenize", Name: "tokenize"),
|
||||
(DisplayName: "Hitbtc", Name: "hitbtc"),
|
||||
(DisplayName: "Upbit", Name: "upbit"),
|
||||
(DisplayName: "Bitstamp", Name: "bitstamp"),
|
||||
(DisplayName: "Luno", Name: "luno"),
|
||||
(DisplayName: "Trade.io", Name: "tradeio"),
|
||||
(DisplayName: "LocalBitcoins", Name: "localbitcoins"),
|
||||
(DisplayName: "Independent Reserve", Name: "independentreserve"),
|
||||
(DisplayName: "Coinsquare", Name: "coinsquare"),
|
||||
(DisplayName: "Exmoney", Name: "exmoney"),
|
||||
(DisplayName: "Coinegg", Name: "coinegg"),
|
||||
(DisplayName: "FYB-SG", Name: "fybsg"),
|
||||
(DisplayName: "Cryptonit", Name: "cryptonit"),
|
||||
(DisplayName: "BTCTurk", Name: "btcturk"),
|
||||
(DisplayName: "bitFlyer", Name: "bitflyer"),
|
||||
(DisplayName: "Negocie Coins", Name: "negociecoins"),
|
||||
(DisplayName: "OasisDEX", Name: "oasisdex"),
|
||||
(DisplayName: "CoinMate", Name: "coinmate"),
|
||||
(DisplayName: "BitForex", Name: "bitforex"),
|
||||
(DisplayName: "Bitsquare", Name: "bitsquare"),
|
||||
(DisplayName: "FYB-SE", Name: "fybse"),
|
||||
(DisplayName: "itBit", Name: "itbit"),
|
||||
})
|
||||
{
|
||||
AvailableExchanges.TryAdd(item.Name, new CoinAverageExchange(item.Name, item.DisplayName, $"https://apiv2.bitcoinaverage.com/exchanges/{item.Name}"));
|
||||
}
|
||||
// Keep back-compat
|
||||
AvailableExchanges.Add(new CoinAverageExchange("gdax", string.Empty, $"https://apiv2.bitcoinaverage.com/exchanges/coinbasepro"));
|
||||
}
|
||||
|
||||
public Task AddHeader(HttpRequestMessage message)
|
||||
|
@ -114,9 +114,9 @@ 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("gdax", new ExchangeSharpRateProvider("gdax", new ExchangeGdaxAPI()));
|
||||
//DirectProviders.Add("gemini", new ExchangeSharpRateProvider("gemini", new ExchangeGeminiAPI()));
|
||||
//DirectProviders.Add("bitfinex", new ExchangeSharpRateProvider("bitfinex", new ExchangeBitfinexAPI()));
|
||||
//DirectProviders.Add("okex", new ExchangeSharpRateProvider("okex", new ExchangeOkexAPI()));
|
||||
|
@ -114,24 +114,31 @@ 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);
|
||||
|
||||
await LogoutFlow(tester, id, s);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LogoutFlow(ServerTester tester, string clientId, SeleniumTester seleniumTester )
|
||||
void LogoutFlow(ServerTester tester, string clientId, SeleniumTester seleniumTester)
|
||||
{
|
||||
var logoutUrl = new Uri(tester.PayTester.ServerUri,
|
||||
$"connect/logout?response_type=token&client_id={clientId}");
|
||||
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -314,7 +331,7 @@ namespace BTCPayServer.Tests
|
||||
return result.AccessToken;
|
||||
}
|
||||
|
||||
public async Task TestApiAgainstAccessToken(string accessToken, ServerTester tester, TestAccount testAccount)
|
||||
async Task TestApiAgainstAccessToken(string accessToken, ServerTester tester, TestAccount testAccount)
|
||||
{
|
||||
var resultUser =
|
||||
await TestApiAgainstAccessToken<string>(accessToken, "api/test/me/id",
|
||||
|
@ -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>
|
||||
|
@ -148,7 +148,7 @@ namespace BTCPayServer.Tests
|
||||
.UseKestrel()
|
||||
.UseStartup<Startup>()
|
||||
.Build();
|
||||
_Host.Start();
|
||||
_Host.StartWithTasksAsync().GetAwaiter().GetResult();
|
||||
|
||||
var urls = _Host.ServerFeatures.Get<IServerAddressesFeature>().Addresses;
|
||||
foreach (var url in urls)
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,7 @@ using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
239
BTCPayServer.Tests/PaymentHandlerTest.cs
Normal file
239
BTCPayServer.Tests/PaymentHandlerTest.cs
Normal file
@ -0,0 +1,239 @@
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using NBitcoin;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Services.Rates;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Rating;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
public class PaymentHandlerTest
|
||||
{
|
||||
private BitcoinLikePaymentHandler handlerBTC;
|
||||
private LightningLikePaymentHandler handlerLN;
|
||||
private Dictionary<CurrencyPair, Task<RateResult>> currencyPairRateResult;
|
||||
|
||||
public PaymentHandlerTest(ITestOutputHelper helper)
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
|
||||
Logs.LogProvider = new XUnitLogProvider(helper);
|
||||
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
var networkProvider = new BTCPayNetworkProvider(NetworkType.Regtest);
|
||||
|
||||
currencyPairRateResult = new Dictionary<CurrencyPair, Task<RateResult>>();
|
||||
|
||||
var rateResultUSDBTC = new RateResult();
|
||||
rateResultUSDBTC.BidAsk= new BidAsk(1m);
|
||||
|
||||
var rateResultBTCUSD = new RateResult();
|
||||
rateResultBTCUSD.BidAsk= new BidAsk(1m);
|
||||
|
||||
currencyPairRateResult.Add(new CurrencyPair("USD", "BTC"), Task.FromResult(rateResultUSDBTC));
|
||||
currencyPairRateResult.Add(new CurrencyPair("BTC", "USD"), Task.FromResult(rateResultBTCUSD));
|
||||
|
||||
handlerBTC = new BitcoinLikePaymentHandler(null, networkProvider, null, null);
|
||||
handlerLN = new LightningLikePaymentHandler(null, null, networkProvider, null);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanPayWithLightningWhenInvoiceTotalUnderLightningMaxValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = null,
|
||||
LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}
|
||||
};
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike);
|
||||
|
||||
//When
|
||||
var totalInvoiceAmount = new Money(98m, MoneyUnit.BTC);
|
||||
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.Equal(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CannotPayWithLightningWhenInvoiceTotalAboveLightningMaxValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = null,
|
||||
LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}
|
||||
};
|
||||
var totalInvoiceAmount = new Money(102m, MoneyUnit.BTC);
|
||||
|
||||
//When
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike);
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.NotEqual(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanPayWithLightningWhenInvoiceTotalEqualLightningMaxValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = null,
|
||||
LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}
|
||||
};
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.LightningLike);
|
||||
|
||||
//When
|
||||
var totalInvoiceAmount = new Money(100m, MoneyUnit.BTC);
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerLN.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.Equal(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanPayWithBitcoinWhenInvoiceTotalAboveOnChainMinValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"},
|
||||
LightningMaxValue = null
|
||||
};
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
|
||||
|
||||
//When
|
||||
var totalInvoiceAmount = new Money(105m, MoneyUnit.BTC);
|
||||
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.Equal(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CannotPayWithBitcoinWhenInvoiceTotalUnderOnChainMinValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"},
|
||||
LightningMaxValue = null
|
||||
};
|
||||
var totalInvoiceAmount = new Money(98m, MoneyUnit.BTC);
|
||||
|
||||
//When
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.NotEqual(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanPayWithBitcoinWhenInvoiceTotalEqualOnChainMinValue()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"},
|
||||
LightningMaxValue = null
|
||||
};
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
|
||||
|
||||
//When
|
||||
var totalInvoiceAmount = new Money(100m, MoneyUnit.BTC);
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.Equal(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void CannotPayWithBitcoinWhenInvoiceTotalUnderOnChainMinValueWhenLightningMaxValueIsGreater()
|
||||
{
|
||||
|
||||
#pragma warning disable CS0618
|
||||
|
||||
//Given
|
||||
var store = new StoreBlob
|
||||
{
|
||||
OnChainMinValue = new CurrencyValue() {Value = 50.00m, Currency = "USD"},
|
||||
LightningMaxValue = new CurrencyValue() {Value = 100.00m, Currency = "USD"}
|
||||
};
|
||||
var paymentMethodId = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
|
||||
|
||||
//When
|
||||
var totalInvoiceAmount = new Money(45m, MoneyUnit.BTC);
|
||||
|
||||
//Then
|
||||
var errorMessage = handlerBTC.IsPaymentMethodAllowedBasedOnInvoiceAmount(store, currencyPairRateResult,
|
||||
totalInvoiceAmount, paymentMethodId);
|
||||
|
||||
Assert.NotEqual(errorMessage.Result, string.Empty);
|
||||
|
||||
#pragma warning restore CS0618
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
@ -34,7 +34,7 @@ namespace BTCPayServer.Tests
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("// Some other cool comments");
|
||||
builder.AppendLine("BTC_usd = GDax(BTC_USD)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X);");
|
||||
builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");
|
||||
|
||||
@ -45,14 +45,14 @@ namespace BTCPayServer.Tests
|
||||
"DOGE_X = DOGE_BTC * BTC_X * 1.1;\n" +
|
||||
"DOGE_BTC = bittrex(DOGE_BTC);\n" +
|
||||
"// Some other cool comments\n" +
|
||||
"BTC_USD = gdax(BTC_USD);\n" +
|
||||
"BTC_USD = kraken(BTC_USD);\n" +
|
||||
"BTC_X = coinbase(BTC_X);\n" +
|
||||
"X_X = coinaverage(X_X) * 1.02;",
|
||||
rules.ToString());
|
||||
var tests = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * gdax(BTC_USD) * 1.1"),
|
||||
(Pair: "BTC_USD", Expected: "gdax(BTC_USD)"),
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02"),
|
||||
@ -62,14 +62,14 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(test.Expected, rules.GetRuleFor(CurrencyPair.Parse(test.Pair)).ToString());
|
||||
}
|
||||
rules.Spread = 0.2m;
|
||||
Assert.Equal("(bittrex(DOGE_BTC) * gdax(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
Assert.Equal("(bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
////////////////
|
||||
|
||||
// Check errors conditions
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = LTC_CAD * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = GDax(BTC_USD)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("LTC_CHF = LTC_CHF * 1.01");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
Assert.True(RateRules.TryParse(builder.ToString(), out rules));
|
||||
@ -77,7 +77,7 @@ namespace BTCPayServer.Tests
|
||||
tests = new[]
|
||||
{
|
||||
(Pair: "LTC_CAD", Expected: "ERR_NO_RULE_MATCH(LTC_CAD)"),
|
||||
(Pair: "DOGE_USD", Expected: "ERR_NO_RULE_MATCH(LTC_CAD) * gdax(BTC_USD) * 1.1"),
|
||||
(Pair: "DOGE_USD", Expected: "ERR_NO_RULE_MATCH(LTC_CAD) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "LTC_CHF", Expected: "ERR_TOO_MUCH_NESTED_CALLS(LTC_CHF) * 1.01"),
|
||||
};
|
||||
foreach (var test in tests)
|
||||
@ -90,15 +90,15 @@ namespace BTCPayServer.Tests
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = GDax(BTC_USD)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");
|
||||
Assert.True(RateRules.TryParse(builder.ToString(), out rules));
|
||||
|
||||
var tests2 = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * gdax(BTC_USD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),gdax(BTC_USD)"),
|
||||
(Pair: "BTC_USD", Expected: "gdax(BTC_USD)", ExpectedExchangeRates: "gdax(BTC_USD)"),
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),kraken(BTC_USD)"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)", ExpectedExchangeRates: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)", ExpectedExchangeRates: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),coinbase(BTC_CAD)"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02", ExpectedExchangeRates: "coinaverage(LTC_CAD)"),
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public static void LogIn(SeleniumTester s, string email)
|
||||
static void LogIn(SeleniumTester s, string email)
|
||||
{
|
||||
s.Driver.FindElement(By.Id("Login")).Click();
|
||||
s.Driver.FindElement(By.Id("Email")).SendKeys(email);
|
||||
@ -94,6 +94,55 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("LoginButton")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
}
|
||||
[Fact]
|
||||
public void CanUseDynamicDns()
|
||||
{
|
||||
using (var s = SeleniumTester.Create())
|
||||
{
|
||||
s.Start();
|
||||
var alice = s.RegisterNewUser(isAdmin: true);
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services"));
|
||||
Assert.Contains("Dynamic DNS", s.Driver.PageSource);
|
||||
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns"));
|
||||
s.Driver.AssertNoError();
|
||||
if (s.Driver.PageSource.Contains("pouet.hello.com"))
|
||||
{
|
||||
// Cleanup old test run
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns/pouet.hello.com/delete"));
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
}
|
||||
s.Driver.FindElement(By.Id("AddDynamicDNS")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
// We will just cheat for test purposes by only querying the server
|
||||
s.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(s.Link("/"));
|
||||
s.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com");
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog");
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter);
|
||||
s.Driver.AssertNoError();
|
||||
Assert.Contains("The Dynamic DNS has been successfully queried", s.Driver.PageSource);
|
||||
Assert.EndsWith("/server/services/dynamic-dns", s.Driver.Url);
|
||||
|
||||
// Try to do the same thing should fail (hostname already exists)
|
||||
s.Driver.FindElement(By.Id("AddDynamicDNS")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
s.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(s.Link("/"));
|
||||
s.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com");
|
||||
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog");
|
||||
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter);
|
||||
s.Driver.AssertNoError();
|
||||
Assert.Contains("This hostname already exists", s.Driver.PageSource);
|
||||
|
||||
// Delete it
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns"));
|
||||
Assert.Contains("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource);
|
||||
s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns/pouet.hello.com/delete"));
|
||||
s.Driver.FindElement(By.Id("continue")).Click();
|
||||
s.Driver.AssertNoError();
|
||||
|
||||
Assert.DoesNotContain("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCreateStores()
|
||||
@ -102,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);
|
||||
@ -152,7 +201,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
s.Start();
|
||||
s.RegisterNewUser();
|
||||
var store = s.CreateNewStore();
|
||||
var store = s.CreateNewStore().storeName;
|
||||
s.AddDerivationScheme();
|
||||
|
||||
CreateInvoice(s, store);
|
||||
@ -166,7 +215,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
public static void CreateInvoice(SeleniumTester s, string store)
|
||||
static void CreateInvoice(SeleniumTester s, string store)
|
||||
{
|
||||
s.Driver.FindElement(By.Id("Invoices")).Click();
|
||||
s.Driver.FindElement(By.Id("CreateNewInvoice")).Click();
|
||||
@ -187,13 +236,15 @@ 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();
|
||||
Assert.True(s.Driver.PageSource.Contains("App updated"), "Unable to create PoS");
|
||||
s.Driver.FindElement(By.Id("ViewApp")).ForceClick();
|
||||
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
|
||||
Assert.True(s.Driver.PageSource.Contains("Tea shop"), "Unable to create PoS");
|
||||
s.Driver.Quit();
|
||||
}
|
||||
}
|
||||
@ -210,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");
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
@ -727,7 +770,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public void CanRescanWallet()
|
||||
public async Task CanRescanWallet()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
@ -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);
|
||||
@ -789,6 +832,32 @@ namespace BTCPayServer.Tests
|
||||
transactions = Assert.IsType<ListTransactionsViewModel>(Assert.IsType<ViewResult>(walletController.WalletTransactions(walletId).Result).Model);
|
||||
var tx = Assert.Single(transactions.Transactions);
|
||||
Assert.Equal(tx.Id, txId.ToString());
|
||||
|
||||
// Hijack the test to see if we can add label and comments
|
||||
Assert.IsType<RedirectToActionResult>(await walletController.ModifyTransaction(walletId, tx.Id, addlabel: "test"));
|
||||
Assert.IsType<RedirectToActionResult>(await walletController.ModifyTransaction(walletId, tx.Id, addlabelclick: "test2"));
|
||||
Assert.IsType<RedirectToActionResult>(await walletController.ModifyTransaction(walletId, tx.Id, addcomment: "hello"));
|
||||
|
||||
transactions = Assert.IsType<ListTransactionsViewModel>(Assert.IsType<ViewResult>(walletController.WalletTransactions(walletId).Result).Model);
|
||||
tx = Assert.Single(transactions.Transactions);
|
||||
|
||||
Assert.Equal("hello", tx.Comment);
|
||||
Assert.Contains("test", tx.Labels.Select(l => l.Value));
|
||||
Assert.Contains("test2", tx.Labels.Select(l => l.Value));
|
||||
Assert.Equal(2, tx.Labels.GroupBy(l => l.Color).Count());
|
||||
|
||||
Assert.IsType<RedirectToActionResult>(await walletController.ModifyTransaction(walletId, tx.Id, removelabel: "test2"));
|
||||
|
||||
transactions = Assert.IsType<ListTransactionsViewModel>(Assert.IsType<ViewResult>(walletController.WalletTransactions(walletId).Result).Model);
|
||||
tx = Assert.Single(transactions.Transactions);
|
||||
|
||||
Assert.Equal("hello", tx.Comment);
|
||||
Assert.Contains("test", tx.Labels.Select(l => l.Value));
|
||||
Assert.DoesNotContain("test2", tx.Labels.Select(l => l.Value));
|
||||
Assert.Single(tx.Labels.GroupBy(l => l.Color));
|
||||
|
||||
var walletInfo = await tester.PayTester.GetService<WalletRepository>().GetWalletInfo(walletId);
|
||||
Assert.Single(walletInfo.LabelColors); // the test2 color should have been removed
|
||||
}
|
||||
}
|
||||
|
||||
@ -1792,7 +1861,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
[Fact]
|
||||
[Trait("Integration", "Integration")]
|
||||
public void CanUsePoSApp()
|
||||
public async Task CanUsePoSApp()
|
||||
{
|
||||
using (var tester = ServerTester.Create())
|
||||
{
|
||||
@ -1903,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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -2765,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");
|
||||
|
||||
|
@ -9,6 +9,7 @@ using NBitcoin.DataEncoders;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
using System.IO;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
|
@ -67,11 +67,8 @@ services:
|
||||
- mysql
|
||||
- customer_lnd
|
||||
- merchant_lnd
|
||||
|
||||
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:2.0.0.52
|
||||
image: nicolasdorier/nbxplorer:2.0.0.57
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
@ -122,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
|
||||
@ -169,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:
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
139
BTCPayServer/Authentication/OpenId/OpenIdExtensions.cs
Normal file
139
BTCPayServer/Authentication/OpenId/OpenIdExtensions.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,13 +30,12 @@
|
||||
<EmbeddedResource Include="Currencies.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.22" />
|
||||
<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.207" />
|
||||
<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>
|
||||
@ -47,12 +46,9 @@
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.RateLimits" 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>
|
||||
@ -146,6 +143,9 @@
|
||||
<Content Update="Views\Home\BitpayTranslator.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\DynamicDnsServices.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\LightningChargeServices.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
@ -155,6 +155,9 @@
|
||||
<Content Update="Views\Server\P2PService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\DynamicDnsService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Server\SSHService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
@ -73,6 +74,8 @@ namespace BTCPayServer.Controllers
|
||||
[AllowAnonymous]
|
||||
public async Task<IActionResult> Login(string returnUrl = null)
|
||||
{
|
||||
if (User.Identity.IsAuthenticated && string.IsNullOrEmpty(returnUrl))
|
||||
return RedirectToLocal();
|
||||
// Clear the existing external cookie to ensure a clean login process
|
||||
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
|
||||
|
||||
@ -181,7 +184,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
Version = u2fChallenge[0].version,
|
||||
Challenge = u2fChallenge[0].challenge,
|
||||
Challenges = JsonConvert.SerializeObject(u2fChallenge),
|
||||
Challenges = u2fChallenge,
|
||||
AppId = u2fChallenge[0].appId,
|
||||
UserId = user.Id,
|
||||
RememberMe = rememberMe
|
||||
@ -645,9 +648,9 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private IActionResult RedirectToLocal(string returnUrl)
|
||||
private IActionResult RedirectToLocal(string returnUrl = null)
|
||||
{
|
||||
if (Url.IsLocalUrl(returnUrl))
|
||||
if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
|
||||
{
|
||||
return Redirect(returnUrl);
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
NotificationEmailWarning = !await IsEmailConfigured(app.StoreDataId),
|
||||
Title = settings.Title,
|
||||
StoreId = app.StoreDataId,
|
||||
Enabled = settings.Enabled,
|
||||
EnforceTargetAmount = settings.EnforceTargetAmount,
|
||||
StartDate = settings.StartDate,
|
||||
@ -131,7 +132,7 @@ namespace BTCPayServer.Controllers
|
||||
EnforceTargetAmount = vm.EnforceTargetAmount,
|
||||
StartDate = vm.StartDate?.ToUniversalTime(),
|
||||
TargetCurrency = vm.TargetCurrency,
|
||||
Description = _htmlSanitizer.Sanitize( vm.Description),
|
||||
Description = vm.Description,
|
||||
EndDate = vm.EndDate?.ToUniversalTime(),
|
||||
TargetAmount = vm.TargetAmount,
|
||||
CustomCSSLink = vm.CustomCSSLink,
|
||||
@ -155,7 +156,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
app.TagAllInvoices = vm.UseAllStoreInvoices;
|
||||
app.SetSettings(newSettings);
|
||||
await UpdateAppSettings(app);
|
||||
await _AppService.UpdateOrCreateApp(app);
|
||||
|
||||
_EventAggregator.Publish(new AppUpdated()
|
||||
{
|
||||
|
@ -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; }
|
||||
@ -96,6 +101,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
NotificationEmailWarning = !await IsEmailConfigured(app.StoreDataId),
|
||||
Id = appId,
|
||||
StoreId = app.StoreDataId,
|
||||
Title = settings.Title,
|
||||
EnableShoppingCart = settings.EnableShoppingCart,
|
||||
ShowCustomAmount = settings.ShowCustomAmount,
|
||||
@ -108,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" : ""
|
||||
@ -186,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(ListApps));
|
||||
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 = ",")
|
||||
{
|
||||
|
@ -30,7 +30,6 @@ namespace BTCPayServer.Controllers
|
||||
EventAggregator eventAggregator,
|
||||
BTCPayNetworkProvider networkProvider,
|
||||
CurrencyNameTable currencies,
|
||||
HtmlSanitizer htmlSanitizer,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
AppService AppService)
|
||||
{
|
||||
@ -39,7 +38,6 @@ namespace BTCPayServer.Controllers
|
||||
_EventAggregator = eventAggregator;
|
||||
_NetworkProvider = networkProvider;
|
||||
_currencies = currencies;
|
||||
_htmlSanitizer = htmlSanitizer;
|
||||
_emailSenderFactory = emailSenderFactory;
|
||||
_AppService = AppService;
|
||||
}
|
||||
@ -49,7 +47,6 @@ namespace BTCPayServer.Controllers
|
||||
private readonly EventAggregator _EventAggregator;
|
||||
private BTCPayNetworkProvider _NetworkProvider;
|
||||
private readonly CurrencyNameTable _currencies;
|
||||
private readonly HtmlSanitizer _htmlSanitizer;
|
||||
private readonly EmailSenderFactory _emailSenderFactory;
|
||||
private AppService _AppService;
|
||||
|
||||
@ -130,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]
|
||||
|
@ -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 >
|
||||
|
136
BTCPayServer/Controllers/AuthorizationController.cs
Normal file
136
BTCPayServer/Controllers/AuthorizationController.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
{
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
@ -257,7 +249,7 @@ namespace BTCPayServer.Controllers
|
||||
paymentMethod.Network = network;
|
||||
paymentMethod.SetId(supportedPaymentMethod.PaymentId);
|
||||
paymentMethod.Rate = rate.BidAsk.Bid;
|
||||
paymentMethod.PreferOnion = this.Request.IsOnion();
|
||||
paymentMethod.PreferOnion = Uri.TryCreate(entity.ServerUrl, UriKind.Absolute, out var u) && u.DnsSafeHost.EndsWith(".onion", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
using (logs.Measure($"{logPrefix} Payment method details creation"))
|
||||
{
|
||||
@ -269,7 +261,7 @@ namespace BTCPayServer.Controllers
|
||||
handler
|
||||
.IsPaymentMethodAllowedBasedOnInvoiceAmount(storeBlob, fetchingByCurrencyPair,
|
||||
paymentMethod.Calculate().Due, supportedPaymentMethod.PaymentId);
|
||||
if (errorMessage != null)
|
||||
if (!string.IsNullOrEmpty(errorMessage))
|
||||
{
|
||||
logs.Write($"{logPrefix} {errorMessage}");
|
||||
return null;
|
||||
|
@ -3,6 +3,8 @@ 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;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -11,6 +13,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
public partial class ManageController
|
||||
{
|
||||
private const string RecoveryCodesKey = nameof(RecoveryCodesKey);
|
||||
private const string AuthenicatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6";
|
||||
|
||||
[HttpGet]
|
||||
@ -80,18 +83,8 @@ namespace BTCPayServer.Controllers
|
||||
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
if (string.IsNullOrEmpty(unformattedKey))
|
||||
{
|
||||
await _userManager.ResetAuthenticatorKeyAsync(user);
|
||||
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
}
|
||||
|
||||
var model = new EnableAuthenticatorViewModel
|
||||
{
|
||||
SharedKey = FormatKey(unformattedKey),
|
||||
AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey)
|
||||
};
|
||||
var model = new EnableAuthenticatorViewModel();
|
||||
await LoadSharedKeyAndQrCodeUriAsync(user, model);
|
||||
|
||||
return View(model);
|
||||
}
|
||||
@ -100,32 +93,36 @@ namespace BTCPayServer.Controllers
|
||||
[ValidateAntiForgeryToken]
|
||||
public async Task<IActionResult> EnableAuthenticator(EnableAuthenticatorViewModel model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
await LoadSharedKeyAndQrCodeUriAsync(user, model);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
// Strip spaces and hypens
|
||||
var verificationCode = model.Code.Replace(" ", string.Empty, StringComparison.InvariantCulture)
|
||||
.Replace("-", string.Empty, StringComparison.InvariantCulture);
|
||||
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);
|
||||
|
||||
if (!is2faTokenValid)
|
||||
{
|
||||
ModelState.AddModelError(nameof(model.Code), "Verification code is invalid.");
|
||||
ModelState.AddModelError("Code", "Verification code is invalid.");
|
||||
await LoadSharedKeyAndQrCodeUriAsync(user, model);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
await _userManager.SetTwoFactorEnabledAsync(user, true);
|
||||
_logger.LogInformation("User with ID {UserId} has enabled 2FA with an authenticator app.", user.Id);
|
||||
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
|
||||
TempData[RecoveryCodesKey] = recoveryCodes.ToArray();
|
||||
|
||||
return RedirectToAction(nameof(GenerateRecoveryCodes));
|
||||
}
|
||||
|
||||
@ -153,7 +150,20 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GenerateRecoveryCodes()
|
||||
public IActionResult GenerateRecoveryCodes()
|
||||
{
|
||||
var recoveryCodes = (string[])TempData[RecoveryCodesKey];
|
||||
if (recoveryCodes == null)
|
||||
{
|
||||
return RedirectToAction(nameof(TwoFactorAuthentication));
|
||||
}
|
||||
|
||||
var model = new GenerateRecoveryCodesViewModel {RecoveryCodes = recoveryCodes};
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GenerateRecoveryCodesWarning()
|
||||
{
|
||||
var user = await _userManager.GetUserAsync(User);
|
||||
if (user == null)
|
||||
@ -163,16 +173,10 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (!user.TwoFactorEnabled)
|
||||
{
|
||||
throw new ApplicationException(
|
||||
$"Cannot generate recovery codes for user with ID '{user.Id}' as they do not have 2FA enabled.");
|
||||
throw new ApplicationException($"Cannot generate recovery codes for user with ID '{user.Id}' because they do not have 2FA enabled.");
|
||||
}
|
||||
|
||||
var recoveryCodes = await _userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
|
||||
var model = new GenerateRecoveryCodesViewModel {RecoveryCodes = recoveryCodes.ToArray()};
|
||||
|
||||
_logger.LogInformation("User with ID {UserId} has generated new 2FA recovery codes.", user.Id);
|
||||
|
||||
return View(model);
|
||||
return View(nameof(GenerateRecoveryCodesWarning));
|
||||
}
|
||||
|
||||
private string GenerateQrCodeUri(string email, string unformattedKey)
|
||||
@ -201,5 +205,19 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
return result.ToString().ToLowerInvariant();
|
||||
}
|
||||
|
||||
|
||||
private async Task LoadSharedKeyAndQrCodeUriAsync(ApplicationUser user, EnableAuthenticatorViewModel model)
|
||||
{
|
||||
var unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
if (string.IsNullOrEmpty(unformattedKey))
|
||||
{
|
||||
await _userManager.ResetAuthenticatorKeyAsync(user);
|
||||
unformattedKey = await _userManager.GetAuthenticatorKeyAsync(user);
|
||||
}
|
||||
|
||||
model.SharedKey = FormatKey(unformattedKey);
|
||||
model.AuthenticatorUri = GenerateQrCodeUri(user.Email, unformattedKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ using BTCPayServer.Services.Mails;
|
||||
using System.Globalization;
|
||||
using BTCPayServer.Security;
|
||||
using BTCPayServer.Services.U2F;
|
||||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
@ -41,7 +41,6 @@ namespace BTCPayServer.Controllers
|
||||
private readonly PaymentRequestService _PaymentRequestService;
|
||||
private readonly EventAggregator _EventAggregator;
|
||||
private readonly CurrencyNameTable _Currencies;
|
||||
private readonly HtmlSanitizer _htmlSanitizer;
|
||||
private readonly InvoiceRepository _InvoiceRepository;
|
||||
|
||||
public PaymentRequestController(
|
||||
@ -52,7 +51,6 @@ namespace BTCPayServer.Controllers
|
||||
PaymentRequestService paymentRequestService,
|
||||
EventAggregator eventAggregator,
|
||||
CurrencyNameTable currencies,
|
||||
HtmlSanitizer htmlSanitizer,
|
||||
InvoiceRepository invoiceRepository)
|
||||
{
|
||||
_InvoiceController = invoiceController;
|
||||
@ -62,7 +60,6 @@ namespace BTCPayServer.Controllers
|
||||
_PaymentRequestService = paymentRequestService;
|
||||
_EventAggregator = eventAggregator;
|
||||
_Currencies = currencies;
|
||||
_htmlSanitizer = htmlSanitizer;
|
||||
_InvoiceRepository = invoiceRepository;
|
||||
}
|
||||
|
||||
@ -152,7 +149,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
blob.Title = viewModel.Title;
|
||||
blob.Email = viewModel.Email;
|
||||
blob.Description = _htmlSanitizer.Sanitize(viewModel.Description);
|
||||
blob.Description = viewModel.Description;
|
||||
blob.Amount = viewModel.Amount;
|
||||
blob.ExpiryDate = viewModel.ExpiryDate?.ToUniversalTime();
|
||||
blob.Currency = viewModel.Currency;
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using Microsoft.AspNetCore.Authorization;
|
||||
using BTCPayServer.Authentication;
|
||||
using Microsoft.AspNetCore.Cors;
|
||||
using System.Threading;
|
||||
using BTCPayServer.Data;
|
||||
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
|
@ -44,12 +44,12 @@ namespace BTCPayServer.Controllers
|
||||
private UserManager<ApplicationUser> _UserManager;
|
||||
SettingsRepository _SettingsRepository;
|
||||
private readonly NBXplorerDashboard _dashBoard;
|
||||
private RateFetcher _RateProviderFactory;
|
||||
private StoreRepository _StoreRepository;
|
||||
LightningConfigurationProvider _LnConfigProvider;
|
||||
private readonly TorServices _torServices;
|
||||
BTCPayServerOptions _Options;
|
||||
ApplicationDbContextFactory _ContextFactory;
|
||||
private BTCPayServerOptions _Options;
|
||||
private readonly AppService _AppService;
|
||||
private readonly CheckConfigurationHostedService _sshState;
|
||||
private readonly StoredFileRepository _StoredFileRepository;
|
||||
private readonly FileService _FileService;
|
||||
private readonly IEnumerable<IStorageProviderService> _StorageProviderServices;
|
||||
@ -59,14 +59,14 @@ namespace BTCPayServer.Controllers
|
||||
FileService fileService,
|
||||
IEnumerable<IStorageProviderService> storageProviderServices,
|
||||
BTCPayServerOptions options,
|
||||
RateFetcher rateProviderFactory,
|
||||
SettingsRepository settingsRepository,
|
||||
NBXplorerDashboard dashBoard,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
LightningConfigurationProvider lnConfigProvider,
|
||||
TorServices torServices,
|
||||
StoreRepository storeRepository,
|
||||
ApplicationDbContextFactory contextFactory)
|
||||
AppService appService,
|
||||
CheckConfigurationHostedService sshState)
|
||||
{
|
||||
_Options = options;
|
||||
_StoredFileRepository = storedFileRepository;
|
||||
@ -76,11 +76,11 @@ namespace BTCPayServer.Controllers
|
||||
_SettingsRepository = settingsRepository;
|
||||
_dashBoard = dashBoard;
|
||||
HttpClientFactory = httpClientFactory;
|
||||
_RateProviderFactory = rateProviderFactory;
|
||||
_StoreRepository = storeRepository;
|
||||
_LnConfigProvider = lnConfigProvider;
|
||||
_torServices = torServices;
|
||||
_ContextFactory = contextFactory;
|
||||
_AppService = appService;
|
||||
_sshState = sshState;
|
||||
}
|
||||
|
||||
[Route("server/rates")]
|
||||
@ -154,17 +154,20 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
[Route("server/users")]
|
||||
public IActionResult ListUsers()
|
||||
public IActionResult ListUsers(int skip = 0, int count = 50)
|
||||
{
|
||||
var users = new UsersViewModel();
|
||||
users.StatusMessage = StatusMessage;
|
||||
users.Users
|
||||
= _UserManager.Users.Select(u => new UsersViewModel.UserViewModel()
|
||||
users.Users = _UserManager.Users.Skip(skip).Take(count)
|
||||
.Select(u => new UsersViewModel.UserViewModel
|
||||
{
|
||||
Name = u.UserName,
|
||||
Email = u.Email,
|
||||
Id = u.Id
|
||||
}).ToList();
|
||||
users.Skip = skip;
|
||||
users.Count = count;
|
||||
users.Total = _UserManager.Users.Count();
|
||||
return View(users);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
@ -479,7 +452,7 @@ namespace BTCPayServer.Controllers
|
||||
if (command.StartsWith("remove-domain", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
ModelState.Clear();
|
||||
var index = int.Parse(command.Substring(command.IndexOf(":",StringComparison.InvariantCultureIgnoreCase) + 1), CultureInfo.InvariantCulture);
|
||||
var index = int.Parse(command.Substring(command.IndexOf(":", StringComparison.InvariantCultureIgnoreCase) + 1), CultureInfo.InvariantCulture);
|
||||
settings.DomainToAppMapping.RemoveAt(index);
|
||||
return View(settings);
|
||||
}
|
||||
@ -500,19 +473,16 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
if (appIdsToFetch.Any())
|
||||
{
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
var apps = (await _AppService.GetApps(appIdsToFetch.ToArray()))
|
||||
.ToDictionary(data => data.Id, data => Enum.Parse<AppType>(data.AppType));;
|
||||
if (!string.IsNullOrEmpty(settings.RootAppId))
|
||||
{
|
||||
var apps = await ctx.Apps.Where(data => appIdsToFetch.Contains(data.Id))
|
||||
.ToDictionaryAsync(data => data.Id, data => Enum.Parse<AppType>(data.AppType));
|
||||
if (!string.IsNullOrEmpty(settings.RootAppId))
|
||||
{
|
||||
settings.RootAppType = apps[settings.RootAppId];
|
||||
}
|
||||
settings.RootAppType = apps[settings.RootAppId];
|
||||
}
|
||||
|
||||
foreach (var domainToAppMappingItem in settings.DomainToAppMapping)
|
||||
{
|
||||
domainToAppMappingItem.AppType = apps[domainToAppMappingItem.AppId];
|
||||
}
|
||||
foreach (var domainToAppMappingItem in settings.DomainToAppMapping)
|
||||
{
|
||||
domainToAppMappingItem.AppType = apps[domainToAppMappingItem.AppId];
|
||||
}
|
||||
}
|
||||
|
||||
@ -534,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()
|
||||
{
|
||||
@ -542,6 +512,11 @@ namespace BTCPayServer.Controllers
|
||||
Link = this.Url.Action(nameof(SSHService))
|
||||
});
|
||||
}
|
||||
result.OtherExternalServices.Add(new ServicesViewModel.OtherExternalService()
|
||||
{
|
||||
Name = "Dynamic DNS",
|
||||
Link = this.Url.Action(nameof(DynamicDnsServices))
|
||||
});
|
||||
foreach (var torService in _torServices.Services)
|
||||
{
|
||||
if (torService.VirtualPort == 80)
|
||||
@ -569,7 +544,7 @@ namespace BTCPayServer.Controllers
|
||||
var storageSettings = await _SettingsRepository.GetSettingAsync<StorageSettings>();
|
||||
result.ExternalStorageServices.Add(new ServicesViewModel.OtherExternalService()
|
||||
{
|
||||
Name = storageSettings == null? "Not set": storageSettings.Provider.ToString(),
|
||||
Name = storageSettings == null ? "Not set" : storageSettings.Provider.ToString(),
|
||||
Link = Url.Action("Storage")
|
||||
});
|
||||
return View(result);
|
||||
@ -577,18 +552,10 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<List<SelectListItem>> GetAppSelectList()
|
||||
{
|
||||
// load display app dropdown
|
||||
using (var ctx = _ContextFactory.CreateContext())
|
||||
{
|
||||
var userId = _UserManager.GetUserId(base.User);
|
||||
var selectList = await ctx.Users.Where(user => user.Id == userId)
|
||||
.SelectMany(s => s.UserStores)
|
||||
.Select(s => s.StoreData)
|
||||
.SelectMany(s => s.Apps)
|
||||
.Select(a => new SelectListItem($"{a.AppType} - {a.Name}", a.Id)).ToListAsync();
|
||||
selectList.Insert(0, new SelectListItem("(None)", null));
|
||||
return selectList;
|
||||
}
|
||||
var apps = (await _AppService.GetAllApps(null, true))
|
||||
.Select(a => new SelectListItem($"{a.AppType} - {a.AppName} - {a.StoreName}", a.Id)).ToList();
|
||||
apps.Insert(0, new SelectListItem("(None)", null));
|
||||
return apps;
|
||||
}
|
||||
|
||||
private static bool TryParseAsExternalService(TorService torService, out ExternalService externalService)
|
||||
@ -798,6 +765,138 @@ namespace BTCPayServer.Controllers
|
||||
return RedirectToAction(nameof(Service), new { cryptoCode = cryptoCode, serviceName = serviceName, nonce = nonce });
|
||||
}
|
||||
|
||||
|
||||
[Route("server/services/dynamic-dns")]
|
||||
public async Task<IActionResult> DynamicDnsServices()
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
return View(settings.Services.Select(s => new DynamicDnsViewModel()
|
||||
{
|
||||
Settings = s
|
||||
}).ToArray());
|
||||
}
|
||||
[Route("server/services/dynamic-dns/{hostname}")]
|
||||
public async Task<IActionResult> DynamicDnsServices(string hostname)
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
var service = settings.Services.FirstOrDefault(s => s.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
|
||||
if (service == null)
|
||||
return NotFound();
|
||||
var vm = new DynamicDnsViewModel();
|
||||
vm.Modify = true;
|
||||
vm.Settings = service;
|
||||
return View(nameof(DynamicDnsService), vm);
|
||||
}
|
||||
[Route("server/services/dynamic-dns")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string command = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(viewModel);
|
||||
}
|
||||
if (command == "Save")
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
var i = settings.Services.FindIndex(d => d.Hostname.Equals(viewModel.Settings.Hostname, StringComparison.OrdinalIgnoreCase));
|
||||
if (i != -1)
|
||||
{
|
||||
ModelState.AddModelError(nameof(viewModel.Settings.Hostname), "This hostname already exists");
|
||||
return View(viewModel);
|
||||
}
|
||||
if (viewModel.Settings.Hostname != null)
|
||||
viewModel.Settings.Hostname = viewModel.Settings.Hostname.Trim().ToLowerInvariant();
|
||||
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
|
||||
if (errorMessage == null)
|
||||
{
|
||||
StatusMessage = $"The Dynamic DNS has been successfully queried, your configuration is saved";
|
||||
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
|
||||
settings.Services.Add(viewModel.Settings);
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
return RedirectToAction(nameof(DynamicDnsServices));
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, errorMessage);
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return View(new DynamicDnsViewModel() { Settings = new DynamicDnsService() });
|
||||
}
|
||||
}
|
||||
[Route("server/services/dynamic-dns/{hostname}")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> DynamicDnsService(DynamicDnsViewModel viewModel, string hostname, string command = null)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(viewModel);
|
||||
}
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
|
||||
var i = settings.Services.FindIndex(d => d.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
|
||||
if (i == -1)
|
||||
return NotFound();
|
||||
if (viewModel.Settings.Password == null)
|
||||
viewModel.Settings.Password = settings.Services[i].Password;
|
||||
if (viewModel.Settings.Hostname != null)
|
||||
viewModel.Settings.Hostname = viewModel.Settings.Hostname.Trim().ToLowerInvariant();
|
||||
if (!viewModel.Settings.Enabled)
|
||||
{
|
||||
StatusMessage = $"The Dynamic DNS service has been disabled";
|
||||
viewModel.Settings.LastUpdated = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
string errorMessage = await viewModel.Settings.SendUpdateRequest(HttpClientFactory.CreateClient());
|
||||
if (errorMessage == null)
|
||||
{
|
||||
StatusMessage = $"The Dynamic DNS has been successfully queried, your configuration is saved";
|
||||
viewModel.Settings.LastUpdated = DateTimeOffset.UtcNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
ModelState.AddModelError(string.Empty, errorMessage);
|
||||
return View(viewModel);
|
||||
}
|
||||
}
|
||||
settings.Services[i] = viewModel.Settings;
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
this.RouteData.Values.Remove(nameof(hostname));
|
||||
return RedirectToAction(nameof(DynamicDnsServices));
|
||||
}
|
||||
[HttpGet]
|
||||
[Route("server/services/dynamic-dns/{hostname}/delete")]
|
||||
public async Task<IActionResult> DeleteDynamicDnsService(string hostname)
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
var i = settings.Services.FindIndex(d => d.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
|
||||
if (i == -1)
|
||||
return NotFound();
|
||||
return View("Confirm", new ConfirmModel()
|
||||
{
|
||||
Title = "Delete the dynamic dns service for " + hostname,
|
||||
Description = "BTCPayServer will stop updating this DNS record periodically",
|
||||
Action = "Delete"
|
||||
});
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("server/services/dynamic-dns/{hostname}/delete")]
|
||||
public async Task<IActionResult> DeleteDynamicDnsServicePost(string hostname)
|
||||
{
|
||||
var settings = (await _SettingsRepository.GetSettingAsync<DynamicDnsSettings>()) ?? new DynamicDnsSettings();
|
||||
var i = settings.Services.FindIndex(d => d.Hostname.Equals(hostname, StringComparison.OrdinalIgnoreCase));
|
||||
if (i == -1)
|
||||
return NotFound();
|
||||
settings.Services.RemoveAt(i);
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
StatusMessage = "Dynamic DNS service successfully removed";
|
||||
this.RouteData.Values.Remove(nameof(hostname));
|
||||
return RedirectToAction(nameof(DynamicDnsServices));
|
||||
}
|
||||
|
||||
[Route("server/services/ssh")]
|
||||
public IActionResult SSHService(bool downloadKeyFile = false)
|
||||
{
|
||||
@ -859,7 +958,8 @@ namespace BTCPayServer.Controllers
|
||||
try
|
||||
{
|
||||
var client = model.Settings.CreateSmtpClient();
|
||||
await client.SendMailAsync(model.Settings.From, model.TestEmail, "BTCPay test", "BTCPay test");
|
||||
var message = model.Settings.CreateMailMessage(new MailAddress(model.TestEmail), "BTCPay test", "BTCPay test");
|
||||
await client.SendMailAsync(message);
|
||||
model.StatusMessage = "Email sent to " + model.TestEmail + ", please, verify you received it";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -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;
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Net.Mail;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Models.ServerViewModels;
|
||||
@ -39,7 +40,8 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
var client = model.Settings.CreateSmtpClient();
|
||||
await client.SendMailAsync(model.Settings.From, model.TestEmail, "BTCPay test", "BTCPay test");
|
||||
var message = model.Settings.CreateMailMessage(new MailAddress(model.TestEmail), "BTCPay test", "BTCPay test");
|
||||
await client.SendMailAsync(message);
|
||||
model.StatusMessage = "Email sent to " + model.TestEmail + ", please, verify you received it";
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user