Files
btcpayserver/BTCPayServer/Hosting/BTCPayServerServices.cs
Andrew Camilleri 916323bb3b [WIP] Further abstractions to Payment Handlers (#867)
* mark items to abstract


wip


wip


wip


wip


wip


wip


wip


cleanup


parse other types


compile and fix tests


fix bug 


fix warnings


fix rebase error


reduce payment method handler passings


more cleanup


switch tests to Fast mode 


fix obsolete warning


remove argument requirement 


rebase fixes


remove overcomplicated code


better parsing


remove dependency on environement


remove async

* fixes and simplification

* simplify

* clean up even more

* replace nuglify dependency

* remove extra space

* Fix tests

* fix booboo

* missing setter

* change url resolver

* reduce payment method handlers

* wrap payment method handlers in a custom type

* fix tests

* make invoice controller UI selectlist population cleaner

* make store controller use payment handler dictionary

* fix ln flag

* fix store controller test

* remove null checks on payment handlers

* remove unused imports

* BitcoinSpecificBtcPayNetwork - abstract BTCPayNetwork

* some type fixes

* fix tests

* simplify fetching handler in invoice controller

* rename network base and bitcoin classes

* abstract serializer to network level

* fix serializer when network not provided

* fix serializer when network not provided

* fix serializer when network not provided

* Abstract more payment type specific logic to handlers

* fix merge issue

* small fixes

* make use of repository instead of direct context usage

* reduce redundant code

* sanity check

* test fixes
2019-05-30 16:02:52 +09:00

302 lines
13 KiB
C#

using BTCPayServer.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.IdentityModel.Tokens.Jwt;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.AspNetCore.Http;
using NBitpayClient;
using NBitcoin;
using BTCPayServer.Data;
using Microsoft.EntityFrameworkCore;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using BTCPayServer.Services;
using BTCPayServer.Services.Invoices;
using BTCPayServer.Services.Rates;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Fees;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using BTCPayServer.Controllers;
using BTCPayServer.Services.Mails;
using Microsoft.AspNetCore.Identity;
using System.Threading;
using BTCPayServer.Services.Wallets;
using BTCPayServer.Authentication;
using BTCPayServer.Authentication.OpenId.Models;
using BTCPayServer.Logging;
using BTCPayServer.HostedServices;
using System.Security.Claims;
using BTCPayServer.PaymentRequest;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Bitcoin;
using BTCPayServer.Payments.Changelly;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Security;
using BTCPayServer.Services.PaymentRequests;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using NBXplorer.DerivationStrategy;
using NicolasDorier.RateLimits;
using Npgsql;
using BTCPayServer.Services.Apps;
using OpenIddict.EntityFrameworkCore.Models;
using BTCPayServer.Services.U2F;
using BundlerMinifier.TagHelpers;
using System.Collections.Generic;
namespace BTCPayServer.Hosting
{
public static class BTCPayServerServices
{
public static IServiceCollection AddBTCPayServer(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>((provider, o) =>
{
var factory = provider.GetRequiredService<ApplicationDbContextFactory>();
factory.ConfigureBuilder(o);
o.UseOpenIddict<BTCPayOpenIdClient, BTCPayOpenIdAuthorization, OpenIddictScope<string>, BTCPayOpenIdToken, string>();
});
services.AddHttpClient();
services.TryAddSingleton<SettingsRepository>();
services.TryAddSingleton<TorServices>();
services.TryAddSingleton<SocketFactory>();
services.TryAddSingleton<LightningClientFactoryService>();
services.TryAddSingleton<InvoicePaymentNotification>();
services.TryAddSingleton<BTCPayServerOptions>(o =>
o.GetRequiredService<IOptions<BTCPayServerOptions>>().Value);
services.TryAddSingleton<InvoiceRepository>(o =>
{
var opts = o.GetRequiredService<BTCPayServerOptions>();
var dbContext = o.GetRequiredService<ApplicationDbContextFactory>();
var paymentMethodHandlerDictionary = o.GetService<PaymentMethodHandlerDictionary>();
var dbpath = Path.Combine(opts.DataDir, "InvoiceDB");
if (!Directory.Exists(dbpath))
Directory.CreateDirectory(dbpath);
return new InvoiceRepository(dbContext, dbpath, o.GetRequiredService<BTCPayNetworkProvider>(),
paymentMethodHandlerDictionary);
});
services.AddSingleton<BTCPayServerEnvironment>();
services.TryAddSingleton<TokenRepository>();
services.TryAddSingleton<EventAggregator>();
services.TryAddSingleton<PaymentRequestService>();
services.TryAddSingleton<U2FService>();
services.TryAddSingleton<CoinAverageSettings>();
services.TryAddSingleton<ApplicationDbContextFactory>(o =>
{
var opts = o.GetRequiredService<BTCPayServerOptions>();
ApplicationDbContextFactory dbContext = null;
if (!String.IsNullOrEmpty(opts.PostgresConnectionString))
{
Logs.Configuration.LogInformation($"Postgres DB used ({opts.PostgresConnectionString})");
dbContext = new ApplicationDbContextFactory(DatabaseType.Postgres, opts.PostgresConnectionString);
}
else if(!String.IsNullOrEmpty(opts.MySQLConnectionString))
{
Logs.Configuration.LogInformation($"MySQL DB used ({opts.MySQLConnectionString})");
dbContext = new ApplicationDbContextFactory(DatabaseType.MySQL, opts.MySQLConnectionString);
}
else
{
var connStr = "Data Source=" + Path.Combine(opts.DataDir, "sqllite.db");
Logs.Configuration.LogInformation($"SQLite DB used ({connStr})");
dbContext = new ApplicationDbContextFactory(DatabaseType.Sqlite, connStr);
}
return dbContext;
});
services.TryAddSingleton<BTCPayNetworkProvider>(o =>
{
var opts = o.GetRequiredService<BTCPayServerOptions>();
return opts.NetworkProvider;
});
services.TryAddSingleton<AppService>();
services.TryAddSingleton<Ganss.XSS.HtmlSanitizer>(o =>
{
var htmlSanitizer = new Ganss.XSS.HtmlSanitizer();
htmlSanitizer.RemovingAtRule += (sender, args) =>
{
};
htmlSanitizer.RemovingTag += (sender, args) =>
{
if (args.Tag.TagName.Equals("img", StringComparison.InvariantCultureIgnoreCase))
{
if (!args.Tag.ClassList.Contains("img-fluid"))
{
args.Tag.ClassList.Add("img-fluid");
}
args.Cancel = true;
}
};
htmlSanitizer.RemovingAttribute += (sender, args) =>
{
if (args.Tag.TagName.Equals("img", StringComparison.InvariantCultureIgnoreCase) &&
args.Attribute.Name.Equals("src", StringComparison.InvariantCultureIgnoreCase) &&
args.Reason == Ganss.XSS.RemoveReason.NotAllowedUrlValue)
{
args.Cancel = true;
}
};
htmlSanitizer.RemovingStyle += (sender, args) => { args.Cancel = true; };
htmlSanitizer.AllowedAttributes.Add("class");
htmlSanitizer.AllowedTags.Add("iframe");
htmlSanitizer.AllowedTags.Remove("img");
htmlSanitizer.AllowedAttributes.Add("webkitallowfullscreen");
htmlSanitizer.AllowedAttributes.Add("allowfullscreen");
return htmlSanitizer;
});
services.TryAddSingleton<LightningConfigurationProvider>();
services.TryAddSingleton<LanguageService>();
services.TryAddSingleton<NBXplorerDashboard>();
services.TryAddSingleton<StoreRepository>();
services.TryAddSingleton<PaymentRequestRepository>();
services.TryAddSingleton<BTCPayWalletProvider>();
services.TryAddSingleton<CurrencyNameTable>();
services.TryAddSingleton<IFeeProviderFactory>(o => new NBXplorerFeeProviderFactory(o.GetRequiredService<ExplorerClientProvider>())
{
Fallback = new FeeRate(100L, 1),
BlockTarget = 20
});
services.AddSingleton<CssThemeManager>();
services.Configure<MvcOptions>((o) => {
o.Filters.Add(new ContentSecurityPolicyCssThemeManager());
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(WalletId)));
o.ModelMetadataDetailsProviders.Add(new SuppressChildValidationMetadataProvider(typeof(DerivationStrategyBase)));
});
services.AddSingleton<IHostedService, CssThemeManagerHostedService>();
services.AddSingleton<IHostedService, MigratorHostedService>();
services.AddSingleton<IHostedService, HostedServices.CheckConfigurationHostedService>();
services.AddSingleton<BitcoinLikePaymentHandler>();
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<BitcoinLikePaymentHandler>());
services.AddSingleton<IHostedService, NBXplorerListener>();
services.AddSingleton<LightningLikePaymentHandler>();
services.AddSingleton<IPaymentMethodHandler>(provider => provider.GetService<LightningLikePaymentHandler>());
services.AddSingleton<IHostedService, LightningListener>();
services.AddSingleton<PaymentMethodHandlerDictionary>();
services.AddSingleton<ChangellyClientProvider>();
services.AddSingleton<IHostedService, NBXplorerWaiters>();
services.AddSingleton<IHostedService, InvoiceNotificationManager>();
services.AddSingleton<IHostedService, InvoiceWatcher>();
services.AddSingleton<IHostedService, RatesHostedService>();
services.AddSingleton<IHostedService, BackgroundJobSchedulerHostedService>();
services.AddSingleton<IHostedService, AppHubStreamer>();
services.AddSingleton<IHostedService, TorServicesHostedService>();
services.AddSingleton<IHostedService, PaymentRequestStreamer>();
services.AddSingleton<IBackgroundJobClient, BackgroundJobClient>();
services.AddTransient<IConfigureOptions<MvcOptions>, BTCPayClaimsFilter>();
services.TryAddSingleton<ExplorerClientProvider>();
services.TryAddSingleton<Bitpay>(o =>
{
if (o.GetRequiredService<BTCPayServerOptions>().NetworkType == NetworkType.Mainnet)
return new Bitpay(new Key(), new Uri("https://bitpay.com/"));
else
return new Bitpay(new Key(), new Uri("https://test.bitpay.com/"));
});
services.TryAddSingleton<RateProviderFactory>();
services.TryAddSingleton<RateFetcher>();
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<AccessTokenController>();
services.AddTransient<InvoiceController>();
services.AddTransient<AppsPublicController>();
services.AddTransient<PaymentRequestController>();
// Add application services.
services.AddSingleton<EmailSenderFactory>();
// bundling
services.AddAuthorization(o => Policies.AddBTCPayPolicies(o));
services.AddBtcPayServerAuthenticationSchemes(configuration);
services.AddSingleton<IBundleProvider, ResourceBundleProvider>();
services.AddTransient<BundleOptions>(provider =>
{
var opts = provider.GetRequiredService<BTCPayServerOptions>();
var bundle = new BundleOptions();
bundle.UseBundles = opts.BundleJsCss;
bundle.AppendVersion = true;
return bundle;
});
services.AddCors(options =>
{
options.AddPolicy(CorsPolicies.All, p => p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
});
var rateLimits = new RateLimitService();
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=5r/min burst=3 nodelay");
services.AddSingleton(rateLimits);
return services;
}
private static void AddBtcPayServerAuthenticationSchemes(this IServiceCollection services, IConfiguration configuration)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
services.AddAuthentication()
.AddJwtBearer(options =>
{
// options.RequireHttpsMetadata = false;
// options.TokenValidationParameters.ValidateAudience = false;
options.TokenValidationParameters.ValidateIssuer = false;
})
.AddCookie()
.AddBitpayAuthentication();
}
public static IApplicationBuilder UsePayServer(this IApplicationBuilder app)
{
using (var scope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
{
//Wait the DB is ready
Retry(() =>
{
scope.ServiceProvider.GetRequiredService<ApplicationDbContext>().Database.Migrate();
});
}
app.UseMiddleware<BTCPayMiddleware>();
return app;
}
static void Retry(Action act)
{
CancellationTokenSource cts = new CancellationTokenSource(1000);
while (true)
{
try
{
act();
return;
}
// Starting up
catch (PostgresException ex) when (ex.SqlState == "57P03") { Thread.Sleep(1000); }
catch when (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
}
}
}
}
}