Merge pull request #1158 from Kukks/register-login-secure-lock

Do not allow login or register over an insecure connection
This commit is contained in:
Nicolas Dorier
2019-11-16 18:15:16 +09:00
committed by GitHub
3 changed files with 146 additions and 59 deletions

View File

@@ -74,21 +74,32 @@ 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);
if (!CanLoginOrRegister())
{
SetInsecureFlags();
}
ViewData["ReturnUrl"] = returnUrl;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[RateLimitsFilter(ZoneLimits.Login, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
@@ -199,6 +210,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithU2F(LoginWithU2FViewModel viewModel, string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
ViewData["ReturnUrl"] = returnUrl;
var user = await _userManager.FindByIdAsync(viewModel.UserId);
@@ -242,6 +258,11 @@ namespace BTCPayServer.Controllers
[AllowAnonymous]
public async Task<IActionResult> LoginWith2fa(bool rememberMe, string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
@@ -264,6 +285,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWith2fa(LoginWith2faViewModel model, bool rememberMe, string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
if (!ModelState.IsValid)
{
return View(model);
@@ -305,6 +331,11 @@ namespace BTCPayServer.Controllers
[AllowAnonymous]
public async Task<IActionResult> LoginWithRecoveryCode(string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
// Ensure the user has gone through the username & password screen first
var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
if (user == null)
@@ -322,6 +353,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken]
public async Task<IActionResult> LoginWithRecoveryCode(LoginWithRecoveryCodeViewModel model, string returnUrl = null)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Login");
}
if (!ModelState.IsValid)
{
return View(model);
@@ -366,6 +402,10 @@ namespace BTCPayServer.Controllers
[AllowAnonymous]
public async Task<IActionResult> Register(string returnUrl = null, bool logon = true, bool useBasicLayout = false)
{
if (!CanLoginOrRegister())
{
SetInsecureFlags();
}
var policies = await _SettingsRepository.GetSettingAsync<PoliciesSettings>() ?? new PoliciesSettings();
if (policies.LockSubscription && !User.IsInRole(Roles.ServerAdmin))
return RedirectToAction(nameof(HomeController.Index), "Home");
@@ -381,6 +421,11 @@ namespace BTCPayServer.Controllers
[ValidateAntiForgeryToken]
public async Task<IActionResult> Register(RegisterViewModel model, string returnUrl = null, bool logon = true)
{
if (!CanLoginOrRegister())
{
return RedirectToAction("Register");
}
ViewData["ReturnUrl"] = returnUrl;
ViewData["Logon"] = logon.ToString(CultureInfo.InvariantCulture).ToLowerInvariant();
ViewData["AllowIsAdmin"] = _Options.AllowAdminRegistration;
@@ -580,6 +625,23 @@ namespace BTCPayServer.Controllers
return RedirectToAction(nameof(HomeController.Index), "Home");
}
}
private bool CanLoginOrRegister()
{
return _btcPayServerEnvironment.IsDevelopping || _btcPayServerEnvironment.IsSecure;
}
private void SetInsecureFlags()
{
TempData.SetStatusMessageModel(new StatusMessageModel()
{
Severity = StatusMessageModel.StatusSeverity.Error,
Message = "You cannot login over an insecure connection. Please use HTTPS or Tor."
});
ViewData["disabled"] = true;
}
#endregion
}

View File

@@ -4,7 +4,14 @@
ViewData["Title"] = "Log in";
Layout = "_WelcomeLayout.cshtml";
}
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage"/>
</div>
</div>
}
<div class="modal-dialog modal-login">
<div class="modal-content">
<div class="modal-header">
@@ -12,30 +19,32 @@
</div>
<div class="modal-body">
<form asp-route-returnurl="@ViewData["ReturnUrl"]" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label>
</div>
<fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)" >
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required" />
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
<input asp-for="Email" class="form-control" placeholder="Email" required="required" />
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="LoginButton">Sign in</button>
</div>
<p class="hint-text"><a asp-action="ForgotPassword">Forgot your password?</a></p>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required" />
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="LoginButton">Sign in</button>
</div>
<p class="hint-text"><a asp-action="ForgotPassword">Forgot your password?</a></p>
</fieldset>
</form>
</div>
@if (themeManager.ShowRegister)

View File

@@ -4,6 +4,14 @@
var useBasicLayout = ViewData["UseBasicLayout"] is true;
Layout = useBasicLayout ? "../Shared/_Layout.cshtml" : "_WelcomeLayout.cshtml";
}
@if (TempData.HasStatusMessage())
{
<div class="row">
<div class="col-lg-12 text-center">
<partial name="_StatusMessage"/>
</div>
</div>
}
<!-- We want to center the dialog box in case we are not using the Welcome layout -->
<div class="modal-dialog @(useBasicLayout ? "modal-dialog-centered" : "")">
@@ -13,45 +21,53 @@
</div>
<div class="modal-body">
<form asp-route-returnUrl="@ViewData["ReturnUrl"]" asp-route-logon="@ViewData["Logon"]" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Email" class="input-group-text"><span class="input-group-addon fa fa-user"></span></label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required" />
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required" />
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="ConfirmPassword" class="input-group-text"><span class="input-group-addon fa fa-lock"></span></label>
</div>
<input asp-for="ConfirmPassword" class="form-control" placeholder="Repeat password" required="required" />
</div>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
@if (ViewData["AllowIsAdmin"] is true)
{
<fieldset disabled="@(ViewData.ContainsKey("disabled") ? "disabled" : null)" >
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="IsAdmin"></label>
<input asp-for="IsAdmin" type="checkbox" class="form-check-inline" />
<span asp-validation-for="IsAdmin" class="text-danger"></span>
<div class="input-group">
<div class="input-group-prepend">
<label for="Email" class="input-group-text">
<span class="input-group-addon fa fa-user"></span>
</label>
</div>
<input asp-for="Email" class="form-control" placeholder="Email" required="required"/>
</div>
<span asp-validation-for="Email" class="text-danger"></span>
</div>
}
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="RegisterButton">Create account</button>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="Password" class="input-group-text">
<span class="input-group-addon fa fa-lock"></span>
</label>
</div>
<input asp-for="Password" class="form-control" placeholder="Password" required="required"/>
</div>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<div class="input-group">
<div class="input-group-prepend">
<label for="ConfirmPassword" class="input-group-text">
<span class="input-group-addon fa fa-lock"></span>
</label>
</div>
<input asp-for="ConfirmPassword" class="form-control" placeholder="Repeat password" required="required"/>
</div>
<span asp-validation-for="ConfirmPassword" class="text-danger"></span>
</div>
@if (ViewData["AllowIsAdmin"] is true)
{
<div class="form-group">
<label asp-for="IsAdmin"></label>
<input asp-for="IsAdmin" type="checkbox" class="form-check-inline"/>
<span asp-validation-for="IsAdmin" class="text-danger"></span>
</div>
}
<div class="form-group">
<button type="submit" class="btn btn-primary btn-block btn-lg" id="RegisterButton">Create account</button>
</div>
</fieldset>
</form>
</div>
</div>