Introduce Spam protection

fixes #1958

Adds 2 new options:
* Do not allow stores to use the email settings of the server. Instead, they would need to fill in the email settings in their own store
* Do not allow user creation through the API unless you are an admin.

Both are opt-in and turned off by default.
This commit is contained in:
Kukks
2020-12-04 08:08:05 +01:00
parent ba027de3f7
commit c17b8e4d9e
4 changed files with 35 additions and 11 deletions

View File

@@ -8,6 +8,7 @@ using BTCPayServer.Client.Models;
using BTCPayServer.Configuration; using BTCPayServer.Configuration;
using BTCPayServer.Data; using BTCPayServer.Data;
using BTCPayServer.Events; using BTCPayServer.Events;
using BTCPayServer.HostedServices;
using BTCPayServer.Logging; using BTCPayServer.Logging;
using BTCPayServer.Security; using BTCPayServer.Security;
using BTCPayServer.Security.GreenField; using BTCPayServer.Security.GreenField;
@@ -35,6 +36,7 @@ namespace BTCPayServer.Controllers.GreenField
private readonly RateLimitService _throttleService; private readonly RateLimitService _throttleService;
private readonly BTCPayServerOptions _options; private readonly BTCPayServerOptions _options;
private readonly IAuthorizationService _authorizationService; private readonly IAuthorizationService _authorizationService;
private readonly CssThemeManager _themeManager;
public UsersController(UserManager<ApplicationUser> userManager, BTCPayServerOptions btcPayServerOptions, public UsersController(UserManager<ApplicationUser> userManager, BTCPayServerOptions btcPayServerOptions,
RoleManager<IdentityRole> roleManager, SettingsRepository settingsRepository, RoleManager<IdentityRole> roleManager, SettingsRepository settingsRepository,
@@ -42,7 +44,8 @@ namespace BTCPayServer.Controllers.GreenField
IPasswordValidator<ApplicationUser> passwordValidator, IPasswordValidator<ApplicationUser> passwordValidator,
RateLimitService throttleService, RateLimitService throttleService,
BTCPayServerOptions options, BTCPayServerOptions options,
IAuthorizationService authorizationService) IAuthorizationService authorizationService,
CssThemeManager themeManager)
{ {
_userManager = userManager; _userManager = userManager;
_btcPayServerOptions = btcPayServerOptions; _btcPayServerOptions = btcPayServerOptions;
@@ -53,6 +56,7 @@ namespace BTCPayServer.Controllers.GreenField
_throttleService = throttleService; _throttleService = throttleService;
_options = options; _options = options;
_authorizationService = authorizationService; _authorizationService = authorizationService;
_themeManager = themeManager;
} }
[Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.Greenfield)] [Authorize(Policy = Policies.CanViewProfile, AuthenticationSchemes = AuthenticationSchemes.Greenfield)]
@@ -100,7 +104,7 @@ namespace BTCPayServer.Controllers.GreenField
if (request.IsAdministrator is true && !isAdmin) if (request.IsAdministrator is true && !isAdmin)
return Forbid(AuthenticationSchemes.GreenfieldBasic); return Forbid(AuthenticationSchemes.GreenfieldBasic);
if (!isAdmin && policies.LockSubscription) if (!isAdmin && (policies.LockSubscription || _themeManager.Policies.DisableUnauthenticatedUserApi))
{ {
// If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission // If we are not admin and subscriptions are locked, we need to check the Policies.CanCreateUser.Key permission
var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded; var canCreateUser = (await _authorizationService.AuthorizeAsync(User, null, new PolicyRequirement(Policies.CanCreateUser))).Succeeded;

View File

@@ -1,28 +1,34 @@
using BTCPayServer.HostedServices;
using BTCPayServer.Services.Stores; using BTCPayServer.Services.Stores;
namespace BTCPayServer.Services.Mails namespace BTCPayServer.Services.Mails
{ {
public class EmailSenderFactory public class EmailSenderFactory
{ {
private readonly IBackgroundJobClient _JobClient; private readonly IBackgroundJobClient _jobClient;
private readonly SettingsRepository _Repository; private readonly SettingsRepository _repository;
private readonly StoreRepository _StoreRepository; private readonly StoreRepository _storeRepository;
private readonly CssThemeManager _cssThemeManager;
public EmailSenderFactory(IBackgroundJobClient jobClient, public EmailSenderFactory(IBackgroundJobClient jobClient,
SettingsRepository repository, SettingsRepository repository,
StoreRepository storeRepository) StoreRepository storeRepository,
CssThemeManager cssThemeManager)
{ {
_JobClient = jobClient; _jobClient = jobClient;
_Repository = repository; _repository = repository;
_StoreRepository = storeRepository; _storeRepository = storeRepository;
_cssThemeManager = cssThemeManager;
} }
public IEmailSender GetEmailSender(string storeId = null) public IEmailSender GetEmailSender(string storeId = null)
{ {
var serverSender = new ServerEmailSender(_Repository, _JobClient); var serverSender = new ServerEmailSender(_repository, _jobClient);
if (string.IsNullOrEmpty(storeId)) if (string.IsNullOrEmpty(storeId))
return serverSender; return serverSender;
return new StoreEmailSender(_StoreRepository, serverSender, _JobClient, storeId); return new StoreEmailSender(_storeRepository,
!_cssThemeManager.Policies.DisableStoresToUseServerEmailSettings ? serverSender : null, _jobClient,
storeId);
} }
} }
} }

View File

@@ -28,6 +28,10 @@ namespace BTCPayServer.Services
public bool CheckForNewVersions { get; set; } public bool CheckForNewVersions { get; set; }
[Display(Name = "Disable notifications automatically showing (no websockets)")] [Display(Name = "Disable notifications automatically showing (no websockets)")]
public bool DisableInstantNotifications { get; set; } public bool DisableInstantNotifications { get; set; }
[Display(Name = "Disable stores falling back to using the server's email settings")]
public bool DisableStoresToUseServerEmailSettings { get; set; }
[Display(Name = "Disable unauthenticated Create User API")]
public bool DisableUnauthenticatedUserApi { get; set; }
[Display(Name = "Display app on website root")] [Display(Name = "Display app on website root")]
public string RootAppId { get; set; } public string RootAppId { get; set; }

View File

@@ -63,6 +63,16 @@
<label asp-for="DisableInstantNotifications" class="form-check-label"></label> <label asp-for="DisableInstantNotifications" class="form-check-label"></label>
<span asp-validation-for="DisableInstantNotifications" class="text-danger"></span> <span asp-validation-for="DisableInstantNotifications" class="text-danger"></span>
</div> </div>
<div class="form-check">
<input asp-for="DisableStoresToUseServerEmailSettings" type="checkbox" class="form-check-input"/>
<label asp-for="DisableStoresToUseServerEmailSettings" class="form-check-label"></label>
<span asp-validation-for="DisableStoresToUseServerEmailSettings" class="text-danger"></span>
</div>
<div class="form-check">
<input asp-for="DisableUnauthenticatedUserApi" type="checkbox" class="form-check-input"/>
<label asp-for="DisableUnauthenticatedUserApi" class="form-check-label"></label>
<span asp-validation-for="DisableUnauthenticatedUserApi" class="text-danger"></span>
</div>
@if (ViewBag.UpdateUrlPresent) @if (ViewBag.UpdateUrlPresent)
{ {
<div class="form-check"> <div class="form-check">