fix(ui): mirror the EWS username:password credentials rule on the SMTP page
This commit is contained in:
+18
@@ -308,6 +308,24 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mirror of the same gate's credentials rule, with the identical FIRST-colon-only
|
||||||
|
// split: a domain-qualified user name contains no colon but a password may. This
|
||||||
|
// page has no preserve-if-blank behaviour — the field's value IS what gets
|
||||||
|
// persisted, and a blank field clears the stored credential — so the rule applies
|
||||||
|
// on edit as well as add, exactly as the management gate validates the effective row.
|
||||||
|
if (IsEwsForm)
|
||||||
|
{
|
||||||
|
var credentialParts = _credentials?.Trim().Split(':', 2) ?? [];
|
||||||
|
if (credentialParts.Length != 2
|
||||||
|
|| credentialParts[0].Length == 0
|
||||||
|
|| credentialParts[1].Length == 0)
|
||||||
|
{
|
||||||
|
_formError = "EWS credentials must be in 'username:password' form "
|
||||||
|
+ @"(username may be 'domain\username').";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_editingSmtp != null)
|
if (_editingSmtp != null)
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ public class SmtpConfigurationPageTests : BunitContext
|
|||||||
// The forced auth type is only observable through what gets persisted: the row
|
// The forced auth type is only observable through what gets persisted: the row
|
||||||
// loaded as OAuth2 must save as Basic because EWS supports Basic only.
|
// loaded as OAuth2 must save as Basic because EWS supports Basic only.
|
||||||
cut.Find("input[type=text]").Change("https://mail.example.com/ews/exchange.asmx");
|
cut.Find("input[type=text]").Change("https://mail.example.com/ews/exchange.asmx");
|
||||||
|
cut.Find("input[type=password]").Change(@"dom\user:pw");
|
||||||
ClickSave(cut);
|
ClickSave(cut);
|
||||||
|
|
||||||
cut.WaitForAssertion(() =>
|
cut.WaitForAssertion(() =>
|
||||||
@@ -233,6 +234,47 @@ public class SmtpConfigurationPageTests : BunitContext
|
|||||||
repo.DidNotReceive().SaveChangesAsync();
|
repo.DidNotReceive().SaveChangesAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Save_WithEwsTransportAndCredentialsWithoutColon_ShowsFormErrorAndDoesNotPersist()
|
||||||
|
{
|
||||||
|
var repo = Substitute.For<INotificationRepository>();
|
||||||
|
// The realistic mistake: an existing SMTP row whose credential is a bare password
|
||||||
|
// is flipped to Ews. Host is valid, so only the credentials rule can reject it.
|
||||||
|
var cut = RenderWith(repo, OAuth2Sample());
|
||||||
|
|
||||||
|
ClickEdit(cut);
|
||||||
|
SelectWithOption(cut, "Ews").Change("Ews");
|
||||||
|
cut.Find("input[type=text]").Change("https://mail.example.com/ews/exchange.asmx");
|
||||||
|
cut.Find("input[type=password]").Change("bare-password");
|
||||||
|
ClickSave(cut);
|
||||||
|
|
||||||
|
cut.WaitForAssertion(() => Assert.Contains("username:password", cut.Markup));
|
||||||
|
repo.DidNotReceive().UpdateSmtpConfigurationAsync(Arg.Any<SmtpConfiguration>());
|
||||||
|
repo.DidNotReceive().SaveChangesAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Save_WithEwsTransportAndDomainQualifiedCredentials_Persists()
|
||||||
|
{
|
||||||
|
var repo = Substitute.For<INotificationRepository>();
|
||||||
|
var cut = RenderWith(repo, OAuth2Sample());
|
||||||
|
|
||||||
|
ClickEdit(cut);
|
||||||
|
SelectWithOption(cut, "Ews").Change("Ews");
|
||||||
|
cut.Find("input[type=text]").Change("https://mail.example.com/ews/exchange.asmx");
|
||||||
|
// A domain-qualified user name carries no colon of its own; the split takes the
|
||||||
|
// FIRST colon only, so a password containing colons stays intact.
|
||||||
|
cut.Find("input[type=password]").Change(@"dom\user:pw:with:colons");
|
||||||
|
ClickSave(cut);
|
||||||
|
|
||||||
|
cut.WaitForAssertion(() =>
|
||||||
|
{
|
||||||
|
repo.Received().UpdateSmtpConfigurationAsync(Arg.Is<SmtpConfiguration>(
|
||||||
|
c => c.Transport == "Ews" && c.Credentials == @"dom\user:pw:with:colons"));
|
||||||
|
repo.Received().SaveChangesAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void ReadOnlyView_ShowsTransportBadgeAndHidesPortAndTlsForEwsRows()
|
public void ReadOnlyView_ShowsTransportBadgeAndHidesPortAndTlsForEwsRows()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user