Skip to content

Commit

Permalink
Merge pull request #1938 from NicolasDorier/email-pwd
Browse files Browse the repository at this point in the history
Do not show password in clear text in email configuration (Fix #1790)
  • Loading branch information
NicolasDorier committed Oct 8, 2020
2 parents 83b28e0 + d8da802 commit d9d2c7d
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 54 deletions.
5 changes: 5 additions & 0 deletions BTCPayServer.Tests/SeleniumTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,11 @@ public void GoToWallet(WalletId walletId = null, WalletsNavPages navPages = Wall
Driver.FindElement(By.Id($"Wallet{navPages}")).Click();
}
}

public void GoToUrl(string relativeUrl)
{
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, relativeUrl));
}

public void GoToServer(ServerNavPages navPages = ServerNavPages.Index)
{
Expand Down
40 changes: 40 additions & 0 deletions BTCPayServer.Tests/SeleniumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,46 @@ public async Task CanUseSSHService()
}
}

[Fact(Timeout = TestTimeout)]
public async Task CanSetupEmailServer()
{
using (var s = SeleniumTester.Create())
{
await s.StartAsync();
var alice = s.RegisterNewUser(isAdmin: true);
s.Driver.Navigate().GoToUrl(s.Link("/server/emails"));
if (s.Driver.PageSource.Contains("Configured"))
{
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
s.AssertHappyMessage();
}
CanSetupEmailCore(s);
s.CreateNewStore();
s.GoToUrl($"stores/{s.StoreId}/emails");
CanSetupEmailCore(s);
}
}

private static void CanSetupEmailCore(SeleniumTester s)
{
s.Driver.FindElement(By.ClassName("dropdown-toggle")).Click();
s.Driver.FindElement(By.ClassName("dropdown-item")).Click();
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
s.AssertHappyMessage();
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("mypassword");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
Assert.Contains("Configured", s.Driver.PageSource);
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test_fix@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
Assert.Contains("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
s.AssertHappyMessage();
Assert.DoesNotContain("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
}

[Fact(Timeout = TestTimeout)]
public async Task CanUseDynamicDns()
{
Expand Down
34 changes: 26 additions & 8 deletions BTCPayServer/Controllers/ServerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -945,23 +945,28 @@ public async Task<IActionResult> Theme(ThemeSettings settings)
public async Task<IActionResult> Emails()
{
var data = (await _SettingsRepository.GetSettingAsync<EmailSettings>()) ?? new EmailSettings();
return View(new EmailsViewModel() { Settings = data });
return View(new EmailsViewModel(data));
}

[Route("server/emails")]
[HttpPost]
public async Task<IActionResult> Emails(EmailsViewModel model, string command)
{
if (!model.Settings.IsComplete())
{
TempData[WellKnownTempData.ErrorMessage] = "Required fields missing";
return View(model);
}


if (command == "Test")
{
try
{
if (model.PasswordSet)
{
var settings = await _SettingsRepository.GetSettingAsync<EmailSettings>();
model.Settings.Password = settings.Password;
}
if (!model.Settings.IsComplete())
{
TempData[WellKnownTempData.ErrorMessage] = "Required fields missing";
return View(model);
}
using (var client = model.Settings.CreateSmtpClient())
using (var message = model.Settings.CreateMailMessage(new MailAddress(model.TestEmail), "BTCPay test", "BTCPay test"))
{
Expand All @@ -975,11 +980,24 @@ public async Task<IActionResult> Emails(EmailsViewModel model, string command)
}
return View(model);
}
else if (command == "ResetPassword")
{
var settings = await _SettingsRepository.GetSettingAsync<EmailSettings>();
settings.Password = null;
await _SettingsRepository.UpdateSetting(model.Settings);
TempData[WellKnownTempData.SuccessMessage] = "Email server password reset";
return RedirectToAction(nameof(Emails));
}
else // if(command == "Save")
{
var oldSettings = await _SettingsRepository.GetSettingAsync<EmailSettings>();
if (new EmailsViewModel(oldSettings).PasswordSet)
{
model.Settings.Password = oldSettings.Password;
}
await _SettingsRepository.UpdateSetting(model.Settings);
TempData[WellKnownTempData.SuccessMessage] = "Email settings saved";
return View(model);
return RedirectToAction(nameof(Emails));
}
}

Expand Down
27 changes: 23 additions & 4 deletions BTCPayServer/Controllers/StoresController.Email.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public IActionResult Emails()
if (store == null)
return NotFound();
var data = store.GetStoreBlob().EmailSettings ?? new EmailSettings();
return View(new EmailsViewModel() { Settings = data });
return View(new EmailsViewModel(data));
}

[Route("{storeId}/emails")]
Expand All @@ -32,6 +32,10 @@ public async Task<IActionResult> Emails(string storeId, EmailsViewModel model, s
{
try
{
if (model.PasswordSet)
{
model.Settings.Password = store.GetStoreBlob().EmailSettings.Password;
}
if (!model.Settings.IsComplete())
{
TempData[WellKnownTempData.ErrorMessage] = "Required fields missing";
Expand All @@ -48,19 +52,34 @@ public async Task<IActionResult> Emails(string storeId, EmailsViewModel model, s
}
return View(model);
}
else if (command == "ResetPassword")
{
var storeBlob = store.GetStoreBlob();
storeBlob.EmailSettings.Password = null;
store.SetStoreBlob(storeBlob);
await _Repo.UpdateStore(store);
TempData[WellKnownTempData.SuccessMessage] = "Email server password reset";
return RedirectToAction(nameof(Emails), new
{
storeId
});
}
else // if(command == "Save")
{

var storeBlob = store.GetStoreBlob();
var oldPassword = storeBlob.EmailSettings?.Password;
if (new EmailsViewModel(storeBlob.EmailSettings).PasswordSet)
{
model.Settings.Password = storeBlob.EmailSettings.Password;
}
storeBlob.EmailSettings = model.Settings;
store.SetStoreBlob(storeBlob);
await _Repo.UpdateStore(store);
TempData[WellKnownTempData.SuccessMessage] = "Email settings modified";
return RedirectToAction(nameof(UpdateStore), new
return RedirectToAction(nameof(Emails), new
{
storeId
});

}
}
}
Expand Down
11 changes: 10 additions & 1 deletion BTCPayServer/Models/ServerViewModels/EmailsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ namespace BTCPayServer.Models.ServerViewModels
{
public class EmailsViewModel
{
public EmailsViewModel()
{

}
public EmailsViewModel(EmailSettings settings)
{
Settings = settings;
PasswordSet = !string.IsNullOrEmpty(settings?.Password);
}
public EmailSettings Settings
{
get; set;
}

public bool PasswordSet { get; set; }
[EmailAddress]
[Display(Name = "Test Email")]
public string TestEmail
Expand Down
1 change: 1 addition & 0 deletions BTCPayServer/Services/Mails/EmailSettings.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Mail;
using Newtonsoft.Json;

namespace BTCPayServer.Services.Mails
{
Expand Down
97 changes: 56 additions & 41 deletions BTCPayServer/Views/Shared/EmailsBody.cshtml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel
@model BTCPayServer.Models.ServerViewModels.EmailsViewModel


<partial name="_StatusMessage" />
Expand Down Expand Up @@ -27,83 +27,98 @@
</div>
</div>
<script>
$(document).ready(function(){
$('.row-quick-fill').show();
$('.dropdown.quick-fill a').click(function(e){
e.preventDefault();
var aNode = $(this);
var data = aNode.data();
for(var key in data){
var value = data[key];
var inputNodes = $('input[name*="Settings.'+key+'" i]');
if(inputNodes.length){
inputNodes.each(function(i, input){
input = $(input);
var type = input.attr('type');
if(type === 'checkbox'){
input.prop('checked', value);
}else{
input.val(value);
}
});
$(document).ready(function () {
$('.row-quick-fill').show();
$('.dropdown.quick-fill a').click(function (e) {
e.preventDefault();
var aNode = $(this);
var data = aNode.data();
for (var key in data) {
var value = data[key];
var inputNodes = $('input[name*="Settings.' + key + '" i]');
if (inputNodes.length) {
inputNodes.each(function (i, input) {
input = $(input);
var type = input.attr('type');
if (type === 'checkbox') {
input.prop('checked', value);
} else {
input.val(value);
}
});
}
}
}
});
});
});
</script>

<form method="post" autocomplete="off">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Settings.Server"></label>
<input asp-for="Settings.Server" class="form-control"/>
<input asp-for="Settings.Server" class="form-control" />
<span asp-validation-for="Settings.Server" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.Port"></label>
<input asp-for="Settings.Port" class="form-control"/>
<input asp-for="Settings.Port" class="form-control" />
<span asp-validation-for="Settings.Port" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.FromDisplay"></label>
<input asp-for="Settings.FromDisplay" class="form-control"/>
<input asp-for="Settings.FromDisplay" class="form-control" />
<small class="form-text text-muted">
Some email providers (like Gmail) don't allow you to set your display name, so this setting may not have any effect.
</small>
<span asp-validation-for="Settings.FromDisplay" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.From"></label>
<input asp-for="Settings.From" class="form-control"/>
<input asp-for="Settings.From" class="form-control" />
<span asp-validation-for="Settings.From" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.Login"></label>
<input asp-for="Settings.Login" class="form-control"/>
<input asp-for="Settings.Login" class="form-control" />
<small class="form-text text-muted">
For many email providers (like Gmail) your login is your email address.
For many email providers (like Gmail) your login is your email address.
</small>
<span asp-validation-for="Settings.Login" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Settings.Password"></label>
<input asp-for="Settings.Password" value="@Model.Settings.Password" class="form-control"/>
<span asp-validation-for="Settings.Password" class="text-danger"></span>
@if (!Model.PasswordSet)
{

<label asp-for="Settings.Password"></label>
<input asp-for="Settings.Password" type="password" class="form-control" />
<span asp-validation-for="Settings.Password" class="text-danger"></span>
}
else
{
<label asp-for="Settings.Password"></label>
<div class="input-group">
<input value="Configured" type="text" readonly class="form-control" />
<div class="input-group-append">
<button type="submit" class="btn btn-danger" name="command" value="ResetPassword">Reset</button>
</div>
</div>
}
</div>
<div class="form-group">
<div class="form-check">
<input asp-for="Settings.EnableSSL" type="checkbox" class="form-check-input"/>
<input asp-for="Settings.EnableSSL" type="checkbox" class="form-check-input" />
<label asp-for="Settings.EnableSSL" class="form-check-label"></label>
</div>
</div>
<input asp-for="PasswordSet" type="hidden" />
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
</div>
</div>
Expand All @@ -116,11 +131,11 @@ $(document).ready(function(){
<p class="form-text text-muted">
If you want to test your settings, enter an email address here and click "Send Test Email".
<strong>Your settings won't be saved</strong>, only a test email will be sent.
<br/>
<br />
After a successful test, you can click "Save".
</p>
<label asp-for="TestEmail"></label>
<input asp-for="TestEmail" class="form-control"/>
<input asp-for="TestEmail" class="form-control" />
<span asp-validation-for="TestEmail" class="text-danger"></span>
</div>
<button type="submit" class="btn btn-secondary" name="command" value="Test">Send Test Email</button>
Expand Down

0 comments on commit d9d2c7d

Please sign in to comment.