Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat(config): Add auth types config #1431

Merged
merged 1 commit into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Rnwood.Smtp4dev/ApiModel/Server.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ public class Server
public bool DesktopMinimiseToTrayIcon { get; set; }
public bool IsDesktopApp { get; internal set; }
public bool SmtpAllowAnyCredentials { get; set; }

public string[] SmtpEnabledAuthTypesWhenSecureConnection { get; set; }
public string[] SmtpEnabledAuthTypesWhenNotSecureConnection { get; set; }
}

}
9 changes: 8 additions & 1 deletion Rnwood.Smtp4dev/ClientApp/src/ApiClient/Server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export default class Server {
smtpAllowAnyCredentials: boolean;
minimiseToTrayIcon: boolean;
isDesktopApp: boolean;
smtpEnabledAuthTypesWhenNotSecureConnection: string[];
smtpEnabledAuthTypesWhenSecureConnection: string[];

constructor(isRunning: boolean, exception: string, portNumber: number, hostName: string, allowRemoteConnections: boolean, numberOfMessagesToKeep: number, numberOfSessionsToKeep: number, imapPortNumber: number, settingsAreEditable: boolean, disableMessageSanitisation: boolean, automaticRelayExpression: string, tlsMode: string, credentialsValidationExpression: string,
authenticationRequired: boolean,
Expand All @@ -20,7 +22,9 @@ export default class Server {
smtpAllowAnyCredentials: boolean,
lockedSettings: { [key: string]: string },
minimiseToTrayIcon: boolean,
isDesktopApp: boolean
isDesktopApp: boolean,
smtpEnabledAuthTypesWhenNotSecureConnection: string[],
smtpEnabledAuthTypesWhenSecureConnection: string[]
)
{

Expand Down Expand Up @@ -55,6 +59,9 @@ export default class Server {
this.smtpAllowAnyCredentials = smtpAllowAnyCredentials;
this.minimiseToTrayIcon = minimiseToTrayIcon;
this.isDesktopApp = isDesktopApp;
this.smtpEnabledAuthTypesWhenNotSecureConnection = smtpEnabledAuthTypesWhenNotSecureConnection;
this.smtpEnabledAuthTypesWhenSecureConnection = smtpEnabledAuthTypesWhenSecureConnection;

}


Expand Down
32 changes: 32 additions & 0 deletions Rnwood.Smtp4dev/ClientApp/src/components/settingsdialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,38 @@
<el-switch v-model="server.secureConnectionRequired" :disabled="server.lockedSettings.secureConnectionRequired" />
</el-form-item>

<el-form-item label="Auth Types when not secure connection" prop="server.smtpEnabledAuthTypesWhenNotSecureConnection">

<el-select v-model="server.smtpEnabledAuthTypesWhenNotSecureConnection"
multiple
style="width: 100%" :disabled="server.lockedSettings.smtpEnabledAuthTypesWhenNotSecureConnection">
<el-option v-for="item in ['ANONYMOUS', 'PLAIN', 'LOGIN', 'CRAM-MD5']"
:key="item"
:label="item"
:value="item" />
<template #prefix>
<el-icon v-if="server.lockedSettings.smtpEnabledAuthTypesWhenNotSecureConnection" :title="`Locked: ${server.lockedSettings.smtpEnabledAuthTypesWhenNotSecureConnection}`"><Lock /></el-icon>
</template>
</el-select>
</el-form-item>

<el-form-item label="Auth Types when secure connection" prop="server.smtpEnabledAuthTypesWhenSecureConnection">

<el-select v-model="server.smtpEnabledAuthTypesWhenSecureConnection"
multiple
style="width: 100%" :disabled="server.lockedSettings.smtpEnabledAuthTypesWhenSecureConnection">
<el-option v-for="item in ['ANONYMOUS', 'PLAIN', 'LOGIN', 'CRAM-MD5']"
:key="item"
:label="item"
:value="item" />
<template #prefix>
<el-icon v-if="server.lockedSettings.smtpEnabledAuthTypesWhenSecureConnection" :title="`Locked: ${server.lockedSettings.smtpEnabledAuthTypesWhenSecureConnection}`"><Lock /></el-icon>
</template>
</el-select>
</el-form-item>



<el-form-item label="Allow Any Credentials (off = see 'Users')" prop="server.smtpAllowAnyCredentials">
<el-icon v-if="server.lockedSettings.smtpAllowAnyCredentials" :title="`Locked: ${server.lockedSettings.smtpAllowAnyCredentials}`"><Lock /></el-icon>

Expand Down
40 changes: 22 additions & 18 deletions Rnwood.Smtp4dev/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ public static MapOptions<CommandLineOptions> TryParseCommandLine(IEnumerable<str
{ "smtpallowanycredentials", "True if the SMTP server will allow any credentials to be used without checking them again the 'Users'", data => map.Add((data != null).ToString(), x => x.ServerOptions.SmtpAllowAnyCredentials) },
{ "webauthenticationrequired", "Require authentication for web interface", data => map.Add((data != null).ToString(), x => x.ServerOptions.WebAuthenticationRequired) },
{ "user=", "Adds a user and password combination for web, SMTP and IMAP. Use format username=password. This option can be repeated to add multiple users.", data =>
map.Add(data, x => x.ServerOptions.Users)
}
map.Add(data, x => x.ServerOptions.Users)},
{ "SmtpAuthTypesNotSecure=", "SMTP auth type enabled when not using secure connection (choices: ANONYMOUS, PLAIN, LOGIN, CRAM-MD5). Separate values with comma.", data =>
map.Add(data, x => x.ServerOptions.SmtpEnabledAuthTypesWhenNotSecureConnection) },
{ "SmtpAuthTypesSecure=", "SMTP auth type enabled when using secure connection (choices: ANONYMOUS, PLAIN, LOGIN, CRAM-MD5). Separate values with comma.", data =>
map.Add(data, x => x.ServerOptions.SmtpEnabledAuthTypesWhenSecureConnection) }


};
};

if (!isDesktopApp)
{
Expand Down Expand Up @@ -82,25 +86,25 @@ public static MapOptions<CommandLineOptions> TryParseCommandLine(IEnumerable<str
catch (OptionException e)
{
errorStream.WriteLine("Invalid command line: " + e.Message);
hadBadArgs = true;
hadBadArgs = true;
}

if (help || hadBadArgs)
{
errorStream.WriteLine();
errorStream.WriteLine(" > For information about default values see documentation in appsettings.json.");
errorStream.WriteLine();
options.WriteOptionDescriptions(errorStream);
throw new CommandLineOptionsException(errorStream.ToString()) { IsHelpRequest = help };
}
else
{
Console.WriteLine();
Console.WriteLine(" > For help use argument --help");
Console.WriteLine();
}
{
errorStream.WriteLine();
errorStream.WriteLine(" > For information about default values see documentation in appsettings.json.");
errorStream.WriteLine();
options.WriteOptionDescriptions(errorStream);
throw new CommandLineOptionsException(errorStream.ToString()) { IsHelpRequest = help };
}
else
{
Console.WriteLine();
Console.WriteLine(" > For help use argument --help");
Console.WriteLine();
}

return map;
return map;
}
}
}
62 changes: 34 additions & 28 deletions Rnwood.Smtp4dev/Controllers/ServerController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,40 +52,44 @@ public ApiModel.Server GetServer()
{
var lockedSettings = GetLockedSettings(true);

var serverOptionsCurrentValue = serverOptions.CurrentValue;
var relayOptionsCurrentValue = relayOptions.CurrentValue;
return new ApiModel.Server()
{
IsRunning = server.IsRunning,
LockedSettings = lockedSettings,
Port = serverOptions.CurrentValue.Port,
ImapPort = serverOptions.CurrentValue.ImapPort,
HostName = serverOptions.CurrentValue.HostName,
AllowRemoteConnections = serverOptions.CurrentValue.AllowRemoteConnections,
NumberOfMessagesToKeep = serverOptions.CurrentValue.NumberOfMessagesToKeep,
NumberOfSessionsToKeep = serverOptions.CurrentValue.NumberOfSessionsToKeep,
Port = serverOptionsCurrentValue.Port,
ImapPort = serverOptionsCurrentValue.ImapPort,
HostName = serverOptionsCurrentValue.HostName,
AllowRemoteConnections = serverOptionsCurrentValue.AllowRemoteConnections,
NumberOfMessagesToKeep = serverOptionsCurrentValue.NumberOfMessagesToKeep,
NumberOfSessionsToKeep = serverOptionsCurrentValue.NumberOfSessionsToKeep,
Exception = server.Exception?.Message,
RelaySmtpServer = relayOptions.CurrentValue.SmtpServer,
RelayTlsMode = relayOptions.CurrentValue.TlsMode.ToString(),
RelaySmtpPort = relayOptions.CurrentValue.SmtpPort,
RelayLogin = relayOptions.CurrentValue.Login,
RelayPassword = relayOptions.CurrentValue.Password,
RelayAutomaticEmails = relayOptions.CurrentValue.AutomaticEmails.Where(s => !String.IsNullOrWhiteSpace(s)).ToArray(),
RelaySenderAddress = relayOptions.CurrentValue.SenderAddress,
RelayAutomaticRelayExpression = relayOptions.CurrentValue.AutomaticRelayExpression,
RelaySmtpServer = relayOptionsCurrentValue.SmtpServer,
RelayTlsMode = relayOptionsCurrentValue.TlsMode.ToString(),
RelaySmtpPort = relayOptionsCurrentValue.SmtpPort,
RelayLogin = relayOptionsCurrentValue.Login,
RelayPassword = relayOptionsCurrentValue.Password,
RelayAutomaticEmails = relayOptionsCurrentValue.AutomaticEmails.Where(s => !string.IsNullOrWhiteSpace(s)).ToArray(),
RelaySenderAddress = relayOptionsCurrentValue.SenderAddress,
RelayAutomaticRelayExpression = relayOptionsCurrentValue.AutomaticRelayExpression,
SettingsAreEditable = hostingEnvironmentHelper.SettingsAreEditable,
DisableMessageSanitisation = serverOptions.CurrentValue.DisableMessageSanitisation,
TlsMode = serverOptions.CurrentValue.TlsMode.ToString(),
AuthenticationRequired = serverOptions.CurrentValue.AuthenticationRequired,
SmtpAllowAnyCredentials = serverOptions.CurrentValue.SmtpAllowAnyCredentials,
SecureConnectionRequired = serverOptions.CurrentValue.SecureConnectionRequired,
CredentialsValidationExpression = serverOptions.CurrentValue.CredentialsValidationExpression,
RecipientValidationExpression = serverOptions.CurrentValue.RecipientValidationExpression,
MessageValidationExpression = serverOptions.CurrentValue.MessageValidationExpression,
DisableIPv6 = serverOptions.CurrentValue.DisableIPv6,
WebAuthenticationRequired = serverOptions.CurrentValue.WebAuthenticationRequired,
Users = serverOptions.CurrentValue.Users,
DisableMessageSanitisation = serverOptionsCurrentValue.DisableMessageSanitisation,
TlsMode = serverOptionsCurrentValue.TlsMode.ToString(),
AuthenticationRequired = serverOptionsCurrentValue.AuthenticationRequired,
SmtpAllowAnyCredentials = serverOptionsCurrentValue.SmtpAllowAnyCredentials,
SecureConnectionRequired = serverOptionsCurrentValue.SecureConnectionRequired,
CredentialsValidationExpression = serverOptionsCurrentValue.CredentialsValidationExpression,
RecipientValidationExpression = serverOptionsCurrentValue.RecipientValidationExpression,
MessageValidationExpression = serverOptionsCurrentValue.MessageValidationExpression,
DisableIPv6 = serverOptionsCurrentValue.DisableIPv6,
WebAuthenticationRequired = serverOptionsCurrentValue.WebAuthenticationRequired,
Users = serverOptionsCurrentValue.Users,
DesktopMinimiseToTrayIcon = desktopOptions.CurrentValue.MinimiseToTrayIcon,
IsDesktopApp = cmdLineOptions.IsDesktopApp
};
IsDesktopApp = cmdLineOptions.IsDesktopApp,
SmtpEnabledAuthTypesWhenNotSecureConnection = serverOptionsCurrentValue.SmtpEnabledAuthTypesWhenNotSecureConnection.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries),
SmtpEnabledAuthTypesWhenSecureConnection = serverOptionsCurrentValue.SmtpEnabledAuthTypesWhenSecureConnection.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)
};
}

private IDictionary<string, string> GetLockedSettings(bool toJsonCasing)
Expand Down Expand Up @@ -206,8 +210,10 @@ public ActionResult UpdateServer(ApiModel.Server serverUpdate)
newSettings.Users = serverUpdate.Users;
newSettings.WebAuthenticationRequired = serverUpdate.WebAuthenticationRequired;
newSettings.SmtpAllowAnyCredentials = serverUpdate.SmtpAllowAnyCredentials;
newSettings.SmtpEnabledAuthTypesWhenNotSecureConnection = string.Join(",", serverUpdate.SmtpEnabledAuthTypesWhenNotSecureConnection);
newSettings.SmtpEnabledAuthTypesWhenSecureConnection = string.Join(",", serverUpdate.SmtpEnabledAuthTypesWhenSecureConnection);

newRelaySettings.SmtpServer = serverUpdate.RelaySmtpServer;
newRelaySettings.SmtpServer = serverUpdate.RelaySmtpServer;
newRelaySettings.SmtpPort = serverUpdate.RelaySmtpPort;
newRelaySettings.TlsMode = Enum.Parse<SecureSocketOptions>(serverUpdate.RelayTlsMode);
newRelaySettings.SenderAddress = serverUpdate.RelaySenderAddress;
Expand Down
4 changes: 4 additions & 0 deletions Rnwood.Smtp4dev/Server/Settings/ServerOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,9 @@ public record ServerOptions

public User[] Users { get; set; } = new User[0];
public bool WebAuthenticationRequired { get; set; } = false;

public string SmtpEnabledAuthTypesWhenNotSecureConnection { get; set; } = "PLAIN,LOGIN,CRAM-MD5";

public string SmtpEnabledAuthTypesWhenSecureConnection { get; set; } = "PLAIN,LOGIN,CRAM-MD5";
}
}
5 changes: 5 additions & 0 deletions Rnwood.Smtp4dev/Server/Settings/ServerOptionsSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,10 @@ public record ServerOptionsSource

public User[] Users { get; set; }
public bool? WebAuthenticationRequired { get; internal set; }

public string[] SmtpEnabledAuthTypesWhenNotSecureConnection { get; set; } = [];

public string[] SmtpEnabledAuthTypesWhenSecureConnection { get; set; } = [];

}
}
1 change: 1 addition & 0 deletions Rnwood.Smtp4dev/Server/Smtp4devServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ private void CreateSmtpServer()

Settings.ServerOptions serverOptionsValue = serverOptions.CurrentValue;
this.smtpServer = new Rnwood.SmtpServer.SmtpServer(new SmtpServer.ServerOptions(serverOptionsValue.AllowRemoteConnections, !serverOptionsValue.DisableIPv6, serverOptionsValue.HostName, serverOptionsValue.Port, serverOptionsValue.AuthenticationRequired,
serverOptionsValue.SmtpEnabledAuthTypesWhenNotSecureConnection.Split(',', StringSplitOptions.TrimEntries|StringSplitOptions.RemoveEmptyEntries), serverOptionsValue.SmtpEnabledAuthTypesWhenSecureConnection.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries),
serverOptionsValue.TlsMode == TlsMode.ImplicitTls ? cert : null,
serverOptionsValue.TlsMode == TlsMode.StartTls ? cert : null
));
Expand Down
11 changes: 11 additions & 0 deletions Rnwood.Smtp4dev/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,17 @@
// 'AuthenticationRequired' must be on or the server will not enforce that authentication takes place.
"SmtpAllowAnyCredentials": true,

// Controls which auth mechanisms are allowed for SMTP when connection is not using SSL/TLS
// Note that real SMTP servers will likely allow zero auth mechanisms until the connection is upgraded using STARTTLS.
// Values are:
// ANONYMOUS
// PLAIN
// LOGIN
"SmtpEnabledAuthTypesWhenNotSecureConnection": "PLAIN,LOGIN,CRAM-MD5",

// Controls which auth mechanisms are allowed for SMTP when connection is using SSL/TLS
"SmtpEnabledAuthTypesWhenSecureConnection": "PLAIN,LOGIN,CRAM-MD5",

// True if the SMTP session will require a secure connection.
// The client will recieve an error if a message is attempted without TLS - this must be enabled separately.
"SecureConnectionRequired": false,
Expand Down
10 changes: 5 additions & 5 deletions smtpserver/Rnwood.SmtpServer.Tests/ClientTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public partial class ClientTests
[Fact]
public async Task MailKit_SmtpUtf8()
{
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions(false, false, (int)StandardSmtpPort.AssignAutomatically, false)))
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions(false, false, "test", (int)StandardSmtpPort.AssignAutomatically, false, [], [], null, null)))
{
ConcurrentBag<IMessage> messages = new ConcurrentBag<IMessage>();

Expand Down Expand Up @@ -82,7 +82,7 @@ await SendMessage_MailKit_Async(server, "ظػؿقط@to.com", "ظػؿقط@from.co
[Fact]
public async Task MailKit_NonSSL()
{
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions(false, false, (int)StandardSmtpPort.AssignAutomatically, false)))
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions(false, false, "test",(int)StandardSmtpPort.AssignAutomatically, false, [], [], null, null)))
{
ConcurrentBag<IMessage> messages = new ConcurrentBag<IMessage>();

Expand All @@ -109,7 +109,7 @@ await SendMessage_MailKit_Async(server, "to@to.com").WithTimeout("sending messag
public async Task MailKit_StartTLS()
{
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions( false,false, Dns.GetHostName(),
(int)StandardSmtpPort.AssignAutomatically, false,
(int)StandardSmtpPort.AssignAutomatically, false, [], [],
null, CreateSelfSignedCertificate())))
{
ConcurrentBag<IMessage> messages = new ConcurrentBag<IMessage>();
Expand Down Expand Up @@ -137,7 +137,7 @@ await SendMessage_MailKit_Async(server, "to@to.com", secureSocketOptions: Secure
public async Task MailKit_ImplicitTLS()
{
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions( false,false, Dns.GetHostName(),
(int)StandardSmtpPort.AssignAutomatically, false,
(int)StandardSmtpPort.AssignAutomatically, false, [], [],
CreateSelfSignedCertificate(), null)))
{
ConcurrentBag<IMessage> messages = new ConcurrentBag<IMessage>();
Expand Down Expand Up @@ -165,7 +165,7 @@ await SendMessage_MailKit_Async(server, "to@to.com",
[Fact]
public async Task MailKit_NonSSL_StressTest()
{
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions( false, false, (int) StandardSmtpPort.AssignAutomatically, false)))
using (SmtpServer server = new SmtpServer(new Rnwood.SmtpServer.ServerOptions( false, false, "test", (int) StandardSmtpPort.AssignAutomatically, false, [], [], null, null)))
{
ConcurrentBag<IMessage> messages = new ConcurrentBag<IMessage>();

Expand Down
Loading