mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 14:04:26 +01:00
Feature: Plugin can extend whether an account can login or not
This commit is contained in:
@@ -21,6 +21,7 @@ using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Localization;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@@ -31,60 +32,32 @@ using NicolasDorier.RateLimits;
|
||||
namespace BTCPayServer.Controllers
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
public class UIAccountController : Controller
|
||||
public class UIAccountController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
RoleManager<IdentityRole> roleManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
PoliciesSettings policiesSettings,
|
||||
SettingsRepository settingsRepository,
|
||||
Configuration.BTCPayServerOptions options,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment,
|
||||
EventAggregator eventAggregator,
|
||||
Fido2Service fido2Service,
|
||||
UserLoginCodeService userLoginCodeService,
|
||||
LnurlAuthService lnurlAuthService,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
CallbackGenerator callbackGenerator,
|
||||
IStringLocalizer stringLocalizer,
|
||||
ViewLocalizer viewLocalizer,
|
||||
UserService userService,
|
||||
Logs logs)
|
||||
: Controller
|
||||
{
|
||||
private readonly UserManager<ApplicationUser> _userManager;
|
||||
private readonly SignInManager<ApplicationUser> _signInManager;
|
||||
readonly RoleManager<IdentityRole> _RoleManager;
|
||||
readonly Configuration.BTCPayServerOptions _Options;
|
||||
private readonly BTCPayServerEnvironment _btcPayServerEnvironment;
|
||||
readonly SettingsRepository _SettingsRepository;
|
||||
private readonly Fido2Service _fido2Service;
|
||||
private readonly LnurlAuthService _lnurlAuthService;
|
||||
private readonly CallbackGenerator _callbackGenerator;
|
||||
private readonly UserLoginCodeService _userLoginCodeService;
|
||||
private readonly EventAggregator _eventAggregator;
|
||||
readonly ILogger _logger;
|
||||
readonly ILogger _logger = logs.PayServer;
|
||||
|
||||
public PoliciesSettings PoliciesSettings { get; }
|
||||
public EmailSenderFactory EmailSenderFactory { get; }
|
||||
public IStringLocalizer StringLocalizer { get; }
|
||||
public Logs Logs { get; }
|
||||
|
||||
public UIAccountController(
|
||||
UserManager<ApplicationUser> userManager,
|
||||
RoleManager<IdentityRole> roleManager,
|
||||
SignInManager<ApplicationUser> signInManager,
|
||||
PoliciesSettings policiesSettings,
|
||||
SettingsRepository settingsRepository,
|
||||
Configuration.BTCPayServerOptions options,
|
||||
BTCPayServerEnvironment btcPayServerEnvironment,
|
||||
EventAggregator eventAggregator,
|
||||
Fido2Service fido2Service,
|
||||
UserLoginCodeService userLoginCodeService,
|
||||
LnurlAuthService lnurlAuthService,
|
||||
EmailSenderFactory emailSenderFactory,
|
||||
CallbackGenerator callbackGenerator,
|
||||
IStringLocalizer stringLocalizer,
|
||||
Logs logs)
|
||||
{
|
||||
_userManager = userManager;
|
||||
_signInManager = signInManager;
|
||||
PoliciesSettings = policiesSettings;
|
||||
_SettingsRepository = settingsRepository;
|
||||
_RoleManager = roleManager;
|
||||
_Options = options;
|
||||
_btcPayServerEnvironment = btcPayServerEnvironment;
|
||||
_fido2Service = fido2Service;
|
||||
_lnurlAuthService = lnurlAuthService;
|
||||
EmailSenderFactory = emailSenderFactory;
|
||||
_callbackGenerator = callbackGenerator;
|
||||
_userLoginCodeService = userLoginCodeService;
|
||||
_eventAggregator = eventAggregator;
|
||||
_logger = logs.PayServer;
|
||||
Logs = logs;
|
||||
StringLocalizer = stringLocalizer;
|
||||
}
|
||||
public PoliciesSettings PoliciesSettings { get; } = policiesSettings;
|
||||
public EmailSenderFactory EmailSenderFactory { get; } = emailSenderFactory;
|
||||
public IStringLocalizer StringLocalizer { get; } = stringLocalizer;
|
||||
public Logs Logs { get; } = logs;
|
||||
|
||||
[TempData]
|
||||
public string ErrorMessage
|
||||
@@ -152,31 +125,31 @@ namespace BTCPayServer.Controllers
|
||||
if (!string.IsNullOrEmpty(loginCode))
|
||||
{
|
||||
var code = loginCode.Split(';').First();
|
||||
var userId = _userLoginCodeService.Verify(code);
|
||||
var userId = userLoginCodeService.Verify(code);
|
||||
if (userId is null)
|
||||
{
|
||||
TempData[WellKnownTempData.ErrorMessage] = StringLocalizer["Login code was invalid"].Value;
|
||||
return await Login(returnUrl);
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
var user = await userManager.FindByIdAsync(userId);
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = user == null ? StatusMessageModel.StatusSeverity.Error : StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loginContext);
|
||||
return await Login(returnUrl);
|
||||
}
|
||||
|
||||
_logger.LogInformation("User {Email} logged in with a login code", user!.Email);
|
||||
await _signInManager.SignInAsync(user, false, "LoginCode");
|
||||
await signInManager.SignInAsync(user, false, "LoginCode");
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
return await Login(returnUrl);
|
||||
}
|
||||
|
||||
private UserService.CanLoginContext CreateLoginContext(ApplicationUser user)
|
||||
=> new(user, StringLocalizer, viewLocalizer, this.HttpContext.Request.GetRequestBaseUrl());
|
||||
|
||||
[HttpPost("/login")]
|
||||
[AllowAnonymous]
|
||||
[ValidateAntiForgeryToken]
|
||||
@@ -192,30 +165,27 @@ namespace BTCPayServer.Controllers
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
// Require the user to pass basic checks (approval, confirmed email, not disabled) before they can log on
|
||||
var user = await _userManager.FindByEmailAsync(model.Email);
|
||||
var user = await userManager.FindByEmailAsync(model.Email);
|
||||
var errorMessage = StringLocalizer["Invalid login attempt."].Value;
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = user == null ? StatusMessageModel.StatusSeverity.Error : StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loginContext);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var fido2Devices = await _fido2Service.HasCredentials(user!.Id);
|
||||
var lnurlAuthCredentials = await _lnurlAuthService.HasCredentials(user.Id);
|
||||
if (!await _userManager.IsLockedOutAsync(user) && (fido2Devices || lnurlAuthCredentials))
|
||||
var fido2Devices = await fido2Service.HasCredentials(user!.Id);
|
||||
var lnurlAuthCredentials = await lnurlAuthService.HasCredentials(user.Id);
|
||||
if (!await userManager.IsLockedOutAsync(user) && (fido2Devices || lnurlAuthCredentials))
|
||||
{
|
||||
if (await _userManager.CheckPasswordAsync(user, model.Password))
|
||||
if (await userManager.CheckPasswordAsync(user, model.Password))
|
||||
{
|
||||
LoginWith2faViewModel twoFModel = null;
|
||||
|
||||
if (user.TwoFactorEnabled)
|
||||
{
|
||||
// we need to do an actual sign in attempt so that 2fa can function in next step
|
||||
await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
|
||||
await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
|
||||
twoFModel = new LoginWith2faViewModel
|
||||
{
|
||||
RememberMe = model.RememberMe
|
||||
@@ -230,12 +200,12 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
}
|
||||
|
||||
await _userManager.AccessFailedAsync(user);
|
||||
await userManager.AccessFailedAsync(user);
|
||||
ModelState.AddModelError(string.Empty, errorMessage!);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
|
||||
var result = await signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User {Email} logged in", user.Email);
|
||||
@@ -267,9 +237,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<LoginWithFido2ViewModel> BuildFido2ViewModel(bool rememberMe, ApplicationUser user)
|
||||
{
|
||||
if (_btcPayServerEnvironment.IsSecure(HttpContext))
|
||||
if (btcPayServerEnvironment.IsSecure(HttpContext))
|
||||
{
|
||||
var r = await _fido2Service.RequestLogin(user.Id);
|
||||
var r = await fido2Service.RequestLogin(user.Id);
|
||||
if (r is null)
|
||||
{
|
||||
return null;
|
||||
@@ -286,9 +256,9 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<LoginWithLNURLAuthViewModel> BuildLNURLAuthViewModel(bool rememberMe, ApplicationUser user)
|
||||
{
|
||||
if (_btcPayServerEnvironment.IsSecure(HttpContext))
|
||||
if (btcPayServerEnvironment.IsSecure(HttpContext))
|
||||
{
|
||||
var r = await _lnurlAuthService.RequestLogin(user.Id);
|
||||
var r = await lnurlAuthService.RequestLogin(user.Id);
|
||||
if (r is null)
|
||||
{
|
||||
return null;
|
||||
@@ -297,7 +267,7 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
RememberMe = rememberMe,
|
||||
UserId = user.Id,
|
||||
LNURLEndpoint = new Uri(_callbackGenerator.ForLNUrlAuth(user, r, Request))
|
||||
LNURLEndpoint = new Uri(callbackGenerator.ForLNUrlAuth(user, r, Request))
|
||||
};
|
||||
}
|
||||
return null;
|
||||
@@ -315,25 +285,22 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
var errorMessage = StringLocalizer["Invalid login attempt."].Value;
|
||||
var user = await _userManager.FindByIdAsync(viewModel.UserId);
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
var user = await userManager.FindByIdAsync(viewModel.UserId);
|
||||
var loggingContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loggingContext))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = user == null ? StatusMessageModel.StatusSeverity.Error : StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loggingContext);
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var k1 = Encoders.Hex.DecodeData(viewModel.LNURLEndpoint.ParseQueryString().Get("k1"));
|
||||
if (_lnurlAuthService.FinalLoginStore.TryRemove(viewModel.UserId, out var storedk1) &&
|
||||
if (lnurlAuthService.FinalLoginStore.TryRemove(viewModel.UserId, out var storedk1) &&
|
||||
storedk1.SequenceEqual(k1))
|
||||
{
|
||||
_lnurlAuthService.FinalLoginStore.TryRemove(viewModel.UserId, out _);
|
||||
await _signInManager.SignInAsync(user!, viewModel.RememberMe, "FIDO2");
|
||||
lnurlAuthService.FinalLoginStore.TryRemove(viewModel.UserId, out _);
|
||||
await signInManager.SignInAsync(user!, viewModel.RememberMe, "FIDO2");
|
||||
_logger.LogInformation("User logged in");
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
@@ -349,7 +316,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel
|
||||
{
|
||||
LoginWithFido2ViewModel = await _fido2Service.HasCredentials(user!.Id) ? await BuildFido2ViewModel(viewModel.RememberMe, user) : null,
|
||||
LoginWithFido2ViewModel = await fido2Service.HasCredentials(user!.Id) ? await BuildFido2ViewModel(viewModel.RememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = viewModel,
|
||||
LoginWith2FaViewModel = !user.TwoFactorEnabled
|
||||
? null
|
||||
@@ -372,22 +339,19 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
var errorMessage = "Invalid login attempt.";
|
||||
var user = await _userManager.FindByIdAsync(viewModel.UserId);
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
var user = await userManager.FindByIdAsync(viewModel.UserId);
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = user == null ? StatusMessageModel.StatusSeverity.Error : StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loginContext);
|
||||
return RedirectToAction("Login");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (await _fido2Service.CompleteLogin(viewModel.UserId, System.Text.Json.JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(viewModel.Response)))
|
||||
if (await fido2Service.CompleteLogin(viewModel.UserId, System.Text.Json.JsonSerializer.Deserialize<AuthenticatorAssertionRawResponse>(viewModel.Response)))
|
||||
{
|
||||
await _signInManager.SignInAsync(user!, viewModel.RememberMe, "FIDO2");
|
||||
await signInManager.SignInAsync(user!, viewModel.RememberMe, "FIDO2");
|
||||
_logger.LogInformation("User {Email} logged in with FIDO2", user.Email);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
@@ -405,7 +369,7 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel
|
||||
{
|
||||
LoginWithFido2ViewModel = viewModel,
|
||||
LoginWithLNURLAuthViewModel = await _lnurlAuthService.HasCredentials(user!.Id) ? await BuildLNURLAuthViewModel(viewModel.RememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = await lnurlAuthService.HasCredentials(user!.Id) ? await BuildLNURLAuthViewModel(viewModel.RememberMe, user) : null,
|
||||
LoginWith2FaViewModel = !user.TwoFactorEnabled
|
||||
? null
|
||||
: new LoginWith2faViewModel
|
||||
@@ -425,7 +389,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
// Ensure the user has gone through the username & password screen first
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||
@@ -436,8 +400,8 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel
|
||||
{
|
||||
LoginWith2FaViewModel = new LoginWith2faViewModel { RememberMe = rememberMe },
|
||||
LoginWithFido2ViewModel = await _fido2Service.HasCredentials(user.Id) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = await _lnurlAuthService.HasCredentials(user.Id) ? await BuildLNURLAuthViewModel(rememberMe, user) : null,
|
||||
LoginWithFido2ViewModel = await fido2Service.HasCredentials(user.Id) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = await lnurlAuthService.HasCredentials(user.Id) ? await BuildLNURLAuthViewModel(rememberMe, user) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -456,23 +420,16 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
throw new ApplicationException($"Unable to load user with ID '{_userManager.GetUserId(User)}'.");
|
||||
}
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loginContext);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty, StringComparison.InvariantCulture).Replace("-", string.Empty, StringComparison.InvariantCulture);
|
||||
var result = await _signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);
|
||||
var result = await signInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, model.RememberMachine);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User {Email} logged in with 2FA", user.Email);
|
||||
@@ -484,8 +441,8 @@ namespace BTCPayServer.Controllers
|
||||
return View("SecondaryLogin", new SecondaryLoginViewModel
|
||||
{
|
||||
LoginWith2FaViewModel = model,
|
||||
LoginWithFido2ViewModel = await _fido2Service.HasCredentials(user.Id) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = await _lnurlAuthService.HasCredentials(user.Id) ? await BuildLNURLAuthViewModel(rememberMe, user) : null,
|
||||
LoginWithFido2ViewModel = await fido2Service.HasCredentials(user.Id) ? await BuildFido2ViewModel(rememberMe, user) : null,
|
||||
LoginWithLNURLAuthViewModel = await lnurlAuthService.HasCredentials(user.Id) ? await BuildLNURLAuthViewModel(rememberMe, user) : null,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -499,7 +456,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
// Ensure the user has gone through the username & password screen first
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load two-factor authentication user.");
|
||||
@@ -525,23 +482,16 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
if (user == null)
|
||||
var user = await signInManager.GetTwoFactorAuthenticationUserAsync();
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
throw new ApplicationException("Unable to load two-factor authentication user.");
|
||||
}
|
||||
if (!UserService.TryCanLogin(user, out var message))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
Severity = StatusMessageModel.StatusSeverity.Warning,
|
||||
Message = message
|
||||
});
|
||||
TempData.SetStatusLoginResult(loginContext);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var recoveryCode = model.RecoveryCode.Replace(" ", string.Empty, StringComparison.InvariantCulture);
|
||||
var result = await _signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
|
||||
var result = await signInManager.TwoFactorRecoveryCodeSignInAsync(recoveryCode);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User {Email} logged in with a recovery code", user.Email);
|
||||
@@ -592,14 +542,14 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
ViewData["ReturnUrl"] = returnUrl;
|
||||
ViewData["Logon"] = logon.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
|
||||
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
|
||||
var policies = await settingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
|
||||
if (policies.LockSubscription && !User.IsInRole(Roles.ServerAdmin))
|
||||
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
|
||||
|
||||
if (ModelState.IsValid)
|
||||
{
|
||||
var anyAdmin = (await _userManager.GetUsersInRoleAsync(Roles.ServerAdmin)).Any();
|
||||
var isFirstAdmin = !anyAdmin || (model.IsAdmin && _Options.CheatMode);
|
||||
var anyAdmin = (await userManager.GetUsersInRoleAsync(Roles.ServerAdmin)).Any();
|
||||
var isFirstAdmin = !anyAdmin || (model.IsAdmin && options.CheatMode);
|
||||
var user = new ApplicationUser
|
||||
{
|
||||
UserName = model.Email,
|
||||
@@ -609,21 +559,21 @@ namespace BTCPayServer.Controllers
|
||||
Created = DateTimeOffset.UtcNow,
|
||||
Approved = isFirstAdmin // auto-approve first admin
|
||||
};
|
||||
var result = await _userManager.CreateAsync(user, model.Password);
|
||||
var result = await userManager.CreateAsync(user, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
if (isFirstAdmin)
|
||||
{
|
||||
await _RoleManager.CreateAsync(new IdentityRole(Roles.ServerAdmin));
|
||||
await _userManager.AddToRoleAsync(user, Roles.ServerAdmin);
|
||||
var settings = await _SettingsRepository.GetSettingAsync<ThemeSettings>() ?? new ThemeSettings();
|
||||
await roleManager.CreateAsync(new IdentityRole(Roles.ServerAdmin));
|
||||
await userManager.AddToRoleAsync(user, Roles.ServerAdmin);
|
||||
var settings = await settingsRepository.GetSettingAsync<ThemeSettings>() ?? new ThemeSettings();
|
||||
settings.FirstRun = false;
|
||||
await _SettingsRepository.UpdateSetting(settings);
|
||||
await _SettingsRepository.FirstAdminRegistered(policies, _Options.UpdateUrl != null, _Options.DisableRegistration, Logs);
|
||||
await settingsRepository.UpdateSetting(settings);
|
||||
await settingsRepository.FirstAdminRegistered(policies, options.UpdateUrl != null, options.DisableRegistration, Logs);
|
||||
RegisteredAdmin = true;
|
||||
}
|
||||
|
||||
_eventAggregator.Publish(await UserEvent.Registered.Create(user, _callbackGenerator, Request));
|
||||
eventAggregator.Publish(await UserEvent.Registered.Create(user, callbackGenerator, Request));
|
||||
RegisteredUserId = user.Id;
|
||||
|
||||
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Account created."].Value;
|
||||
@@ -643,7 +593,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
if (logon)
|
||||
{
|
||||
await _signInManager.SignInAsync(user, isPersistent: false);
|
||||
await signInManager.SignInAsync(user, isPersistent: false);
|
||||
_logger.LogInformation("User {Email} logged in", user.Email);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
@@ -665,9 +615,9 @@ namespace BTCPayServer.Controllers
|
||||
[HttpGet("/logout")]
|
||||
public async Task<IActionResult> Logout()
|
||||
{
|
||||
var userId = _signInManager.UserManager.GetUserId(HttpContext.User);
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
await _signInManager.SignOutAsync();
|
||||
var userId = signInManager.UserManager.GetUserId(HttpContext.User);
|
||||
var user = await userManager.FindByIdAsync(userId);
|
||||
await signInManager.SignOutAsync();
|
||||
HttpContext.DeleteUserPrefsCookie();
|
||||
_logger.LogInformation("User {Email} logged out", user!.Email);
|
||||
return RedirectToAction(nameof(Login));
|
||||
@@ -681,19 +631,19 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
return RedirectToAction(nameof(UIHomeController.Index), "UIHome");
|
||||
}
|
||||
var user = await _userManager.FindByIdAsync(userId);
|
||||
var user = await userManager.FindByIdAsync(userId);
|
||||
if (user == null)
|
||||
{
|
||||
throw new ApplicationException($"Unable to load user with ID '{userId}'.");
|
||||
}
|
||||
|
||||
var result = await _userManager.ConfirmEmailAsync(user, code);
|
||||
var result = await userManager.ConfirmEmailAsync(user, code);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
var approvalLink = _callbackGenerator.ForApproval(user, Request);
|
||||
_eventAggregator.Publish(new UserEvent.ConfirmedEmail(user, approvalLink));
|
||||
var approvalLink = callbackGenerator.ForApproval(user, Request);
|
||||
eventAggregator.Publish(new UserEvent.ConfirmedEmail(user, approvalLink));
|
||||
|
||||
var hasPassword = await _userManager.HasPasswordAsync(user);
|
||||
var hasPassword = await userManager.HasPasswordAsync(user);
|
||||
if (hasPassword)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
@@ -730,14 +680,14 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (ModelState.IsValid && await EmailSenderFactory.IsComplete())
|
||||
{
|
||||
var user = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (!UserService.TryCanLogin(user, out _))
|
||||
var user = await userManager.FindByEmailAsync(model.Email);
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext))
|
||||
{
|
||||
// Don't reveal that the user does not exist or is not confirmed
|
||||
return RedirectToAction(nameof(ForgotPasswordConfirmation));
|
||||
}
|
||||
var callbackUri = await _callbackGenerator.ForPasswordReset(user, Request);
|
||||
_eventAggregator.Publish(new UserEvent.PasswordResetRequested(user, callbackUri));
|
||||
var callbackUri = await callbackGenerator.ForPasswordReset(user, Request);
|
||||
eventAggregator.Publish(new UserEvent.PasswordResetRequested(user, callbackUri));
|
||||
return RedirectToAction(nameof(ForgotPasswordConfirmation));
|
||||
}
|
||||
|
||||
@@ -761,8 +711,8 @@ namespace BTCPayServer.Controllers
|
||||
throw new ApplicationException("A code must be supplied for this action.");
|
||||
}
|
||||
|
||||
var user = string.IsNullOrEmpty(userId) ? null : await _userManager.FindByIdAsync(userId);
|
||||
var hasPassword = user != null && await _userManager.HasPasswordAsync(user);
|
||||
var user = string.IsNullOrEmpty(userId) ? null : await userManager.FindByIdAsync(userId);
|
||||
var hasPassword = user != null && await userManager.HasPasswordAsync(user);
|
||||
if (!string.IsNullOrEmpty(userId))
|
||||
{
|
||||
email = user?.Email;
|
||||
@@ -787,17 +737,17 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByEmailAsync(model.Email);
|
||||
var hasPassword = user != null && await _userManager.HasPasswordAsync(user);
|
||||
var needsInitialPassword = user != null && !await _userManager.HasPasswordAsync(user);
|
||||
// Let unapproved users set a password. Otherwise, don't reveal that the user does not exist.
|
||||
if (!UserService.TryCanLogin(user, out var message) && !needsInitialPassword || user == null)
|
||||
{
|
||||
_logger.LogWarning("User {Email} tried to reset password, but failed: {Message}", user?.Email ?? "(NO EMAIL)", message);
|
||||
var user = await userManager.FindByEmailAsync(model.Email);
|
||||
if (user is null)
|
||||
return RedirectToAction(nameof(Login));
|
||||
var hasPassword = await userManager.HasPasswordAsync(user);
|
||||
var needsInitialPassword = !await userManager.HasPasswordAsync(user);
|
||||
// Let unapproved users set a password. Otherwise, don't reveal that the user does not exist.
|
||||
var loginContext = CreateLoginContext(user);
|
||||
if (!await userService.CanLogin(loginContext) && !needsInitialPassword)
|
||||
return RedirectToAction(nameof(Login));
|
||||
}
|
||||
|
||||
var result = await _userManager.ResetPasswordAsync(user!, model.Code, model.Password);
|
||||
var result = await userManager.ResetPasswordAsync(user!, model.Code, model.Password);
|
||||
if (result.Succeeded)
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
@@ -809,12 +759,12 @@ namespace BTCPayServer.Controllers
|
||||
});
|
||||
|
||||
// see if we can sign in user after accepting an invitation and setting the password
|
||||
if (needsInitialPassword && UserService.TryCanLogin(user, out _))
|
||||
loginContext = CreateLoginContext(user);
|
||||
if (needsInitialPassword && await userService.CanLogin(loginContext))
|
||||
{
|
||||
var signInResult = await _signInManager.PasswordSignInAsync(user.Email!, model.Password, true, true);
|
||||
var signInResult = await signInManager.PasswordSignInAsync(user.Email!, model.Password, true, true);
|
||||
if (signInResult.Succeeded)
|
||||
{
|
||||
_logger.LogInformation("User {Email} logged in", user.Email);
|
||||
return RedirectToLocal(returnUrl);
|
||||
}
|
||||
}
|
||||
@@ -822,7 +772,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
AddErrors(result);
|
||||
model.HasPassword = await _userManager.HasPasswordAsync(user);
|
||||
model.HasPassword = await userManager.HasPasswordAsync(user);
|
||||
return View(model);
|
||||
}
|
||||
|
||||
@@ -835,14 +785,14 @@ namespace BTCPayServer.Controllers
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var user = await _userManager.FindByInvitationTokenAsync<ApplicationUser>(userId, Uri.UnescapeDataString(code));
|
||||
var user = await userManager.FindByInvitationTokenAsync<ApplicationUser>(userId, Uri.UnescapeDataString(code));
|
||||
if (user == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var requiresEmailConfirmation = user.RequiresEmailConfirmation && !user.EmailConfirmed;
|
||||
var requiresSetPassword = !await _userManager.HasPasswordAsync(user);
|
||||
var requiresSetPassword = !await userManager.HasPasswordAsync(user);
|
||||
if (requiresEmailConfirmation)
|
||||
{
|
||||
return await RedirectToConfirmEmail(user);
|
||||
@@ -868,13 +818,13 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private async Task<IActionResult> RedirectToConfirmEmail(ApplicationUser user)
|
||||
{
|
||||
var code = await _userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
var code = await userManager.GenerateEmailConfirmationTokenAsync(user);
|
||||
return RedirectToAction(nameof(ConfirmEmail), new { userId = user.Id, code });
|
||||
}
|
||||
|
||||
private async Task<IActionResult> RedirectToSetPassword(ApplicationUser user)
|
||||
{
|
||||
var code = await _userManager.GeneratePasswordResetTokenAsync(user);
|
||||
var code = await userManager.GeneratePasswordResetTokenAsync(user);
|
||||
return RedirectToAction(nameof(SetPassword), new { userId = user.Id, email = user.Email, code });
|
||||
}
|
||||
|
||||
@@ -912,7 +862,7 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
private bool CanLoginOrRegister()
|
||||
{
|
||||
return _btcPayServerEnvironment.IsDeveloping || _btcPayServerEnvironment.IsSecure(HttpContext);
|
||||
return btcPayServerEnvironment.IsDeveloping || btcPayServerEnvironment.IsSecure(HttpContext);
|
||||
}
|
||||
|
||||
private void SetInsecureFlags()
|
||||
|
||||
Reference in New Issue
Block a user