Return all supported authentication methods from RealAutoconfigParser

This commit is contained in:
cketti 2023-07-19 19:32:55 +02:00
parent ef4d05f5be
commit f73cd2ffef
15 changed files with 86 additions and 78 deletions

View file

@ -16,14 +16,14 @@ internal class AutoDiscoveryResultFormatter(private val echo: (String) -> Unit)
echo(" Hostname: ${incomingServer.hostname.value}")
echo(" Port: ${incomingServer.port.value}")
echo(" Connection security: ${incomingServer.connectionSecurity}")
echo(" Authentication type: ${incomingServer.authenticationType}")
echo(" Authentication: ${incomingServer.authenticationTypes.joinToString()}")
echo(" Username: ${incomingServer.username}")
echo("")
echo("Outgoing server:")
echo(" Hostname: ${outgoingServer.hostname.value}")
echo(" Port: ${outgoingServer.port.value}")
echo(" Connection security: ${outgoingServer.connectionSecurity}")
echo(" Authentication type: ${outgoingServer.authenticationType}")
echo(" Authentication: ${outgoingServer.authenticationTypes.joinToString()}")
echo(" Username: ${outgoingServer.username}")
echo("------------------------------")
if (settings.isTrusted) {

View file

@ -57,10 +57,10 @@ class FakeAutoDiscoveryService : AutoDiscoveryService {
hostname = "imap.${emailAddress.domain}".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = if (hasIncomingOauth) {
AuthenticationType.OAuth2
authenticationTypes = if (hasIncomingOauth) {
listOf(AuthenticationType.OAuth2)
} else {
AuthenticationType.PasswordEncrypted
listOf(AuthenticationType.PasswordEncrypted)
},
username = "username",
),
@ -68,10 +68,10 @@ class FakeAutoDiscoveryService : AutoDiscoveryService {
hostname = "smtp.${emailAddress.domain}".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = if (hasOutgoingOauth) {
AuthenticationType.OAuth2
authenticationTypes = if (hasOutgoingOauth) {
listOf(AuthenticationType.OAuth2)
} else {
AuthenticationType.PasswordEncrypted
listOf(AuthenticationType.PasswordEncrypted)
},
username = "username",
),

View file

@ -39,35 +39,38 @@ internal class GetAutoDiscovery(
val incomingServerSettings = settings.incomingServerSettings as ImapServerSettings
val outgoingServerSettings = settings.outgoingServerSettings as SmtpServerSettings
val incomingAuthenticationType = updateAuthenticationType(
authenticationType = incomingServerSettings.authenticationType,
val incomingAuthenticationTypes = cleanAuthenticationTypes(
authenticationTypes = incomingServerSettings.authenticationTypes,
hostname = incomingServerSettings.hostname.value,
)
val outgoingAuthenticationType = updateAuthenticationType(
authenticationType = outgoingServerSettings.authenticationType,
val outgoingAuthenticationTypes = cleanAuthenticationTypes(
authenticationTypes = outgoingServerSettings.authenticationTypes,
hostname = outgoingServerSettings.hostname.value,
)
return settings.copy(
return if (incomingAuthenticationTypes.isNotEmpty() && outgoingAuthenticationTypes.isNotEmpty()) {
settings.copy(
incomingServerSettings = incomingServerSettings.copy(
authenticationType = incomingAuthenticationType,
authenticationTypes = incomingAuthenticationTypes,
),
outgoingServerSettings = outgoingServerSettings.copy(
authenticationType = outgoingAuthenticationType,
authenticationTypes = outgoingAuthenticationTypes,
),
)
} else {
AutoDiscoveryResult.NoUsableSettingsFound
}
}
private fun updateAuthenticationType(
authenticationType: AuthenticationType,
private fun cleanAuthenticationTypes(
authenticationTypes: List<AuthenticationType>,
hostname: String,
): AuthenticationType {
return if (authenticationType == AuthenticationType.OAuth2 && !isOAuthSupportedFor(hostname)) {
// OAuth2 is not supported for this hostname, downgrade to password cleartext
// TODO replace with next supported authentication type, once populated by autodiscovery
AuthenticationType.PasswordCleartext
): List<AuthenticationType> {
return if (AuthenticationType.OAuth2 in authenticationTypes && !isOAuthSupportedFor(hostname)) {
// OAuth2 is not supported for this hostname; remove it from the list of supported authentication types
authenticationTypes.filter { it != AuthenticationType.OAuth2 }
} else {
authenticationType
authenticationTypes
}
}

View file

@ -115,14 +115,14 @@ internal fun AutoDiscoveryStatusBodyViewPreview() {
hostname = "imap.example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
isTrusted = true,

View file

@ -77,14 +77,14 @@ internal fun AutoDiscoveryStatusViewTrustedPreview() {
hostname = "imap.example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
isTrusted = true,
@ -105,14 +105,14 @@ internal fun AutoDiscoveryStatusViewUntrustedPreview() {
hostname = "imap.example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "",
),
isTrusted = false,

View file

@ -15,14 +15,14 @@ object AutoDiscoverySettingsFixture {
hostname = "incoming.example.com".toHostname(),
port = 123.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "incoming_username",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "outgoing.example.com".toHostname(),
port = 456.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordEncrypted,
authenticationTypes = listOf(AuthenticationType.PasswordEncrypted),
username = "outgoing_username",
),
isTrusted = true,

View file

@ -16,7 +16,7 @@ class IncomingServerSettingsExtensionKtTest {
hostname = "example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = AutoDiscoveryConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordCleartext,
authenticationTypes = listOf(AuthenticationType.PasswordCleartext),
username = "username",
)

View file

@ -90,7 +90,7 @@ class GetAutoDiscoveryTest {
}
@Test
fun `should check for oauth support and change auth type to password when not supported`() = runTest {
fun `should check for OAuth support and drop OAuth when not supported`() = runTest {
val useCase = GetAutoDiscovery(
service = FakeAutoDiscoveryService(SETTINGS_WITH_OAUTH),
oauthProvider = FakeOAuthConfigurationProvider(),
@ -103,10 +103,10 @@ class GetAutoDiscoveryTest {
.isEqualTo(
SETTINGS_WITH_OAUTH.copy(
incomingServerSettings = (SETTINGS_WITH_OAUTH.incomingServerSettings as ImapServerSettings).copy(
authenticationType = AuthenticationType.PasswordCleartext,
authenticationTypes = listOf(AuthenticationType.PasswordCleartext),
),
outgoingServerSettings = (SETTINGS_WITH_OAUTH.outgoingServerSettings as SmtpServerSettings).copy(
authenticationType = AuthenticationType.PasswordCleartext,
authenticationTypes = listOf(AuthenticationType.PasswordCleartext),
),
),
)
@ -132,14 +132,14 @@ class GetAutoDiscoveryTest {
hostname = "imap.example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.OAuth2,
authenticationTypes = listOf(AuthenticationType.OAuth2, AuthenticationType.PasswordCleartext),
username = "user",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.OAuth2,
authenticationTypes = listOf(AuthenticationType.OAuth2, AuthenticationType.PasswordCleartext),
username = "user",
),
isTrusted = true,
@ -152,7 +152,7 @@ class GetAutoDiscoveryTest {
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.OAuth2,
authenticationTypes = listOf(AuthenticationType.OAuth2),
username = "user",
),
isTrusted = true,
@ -164,14 +164,14 @@ class GetAutoDiscoveryTest {
hostname = "imap.example.com".toHostname(),
port = 993.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordCleartext,
authenticationTypes = listOf(AuthenticationType.PasswordCleartext),
username = "user",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.example.com".toHostname(),
port = 465.toPort(),
connectionSecurity = ConnectionSecurity.TLS,
authenticationType = AuthenticationType.PasswordCleartext,
authenticationTypes = listOf(AuthenticationType.PasswordCleartext),
username = "user",
),
isTrusted = true,

View file

@ -165,14 +165,14 @@ class AccountAutoDiscoveryStateMapperKtTest {
hostname = AUTO_DISCOVERY_HOSTNAME,
port = AUTO_DISCOVERY_PORT_IMAP,
connectionSecurity = AUTO_DISCOVERY_SECURITY,
authenticationType = AUTO_DISCOVERY_AUTHENTICATION,
authenticationTypes = listOf(AUTO_DISCOVERY_AUTHENTICATION),
username = AUTO_DISCOVERY_USERNAME,
),
outgoingServerSettings = SmtpServerSettings(
hostname = AUTO_DISCOVERY_HOSTNAME,
port = AUTO_DISCOVERY_PORT_SMTP,
connectionSecurity = AUTO_DISCOVERY_SECURITY,
authenticationType = AUTO_DISCOVERY_AUTHENTICATION,
authenticationTypes = listOf(AUTO_DISCOVERY_AUTHENTICATION),
username = AUTO_DISCOVERY_USERNAME,
),
isTrusted = true,

View file

@ -7,6 +7,6 @@ data class ImapServerSettings(
val hostname: Hostname,
val port: Port,
val connectionSecurity: ConnectionSecurity,
val authenticationType: AuthenticationType,
val authenticationTypes: List<AuthenticationType>,
val username: String,
) : IncomingServerSettings

View file

@ -7,6 +7,6 @@ data class SmtpServerSettings(
val hostname: Hostname,
val port: Port,
val connectionSecurity: ConnectionSecurity,
val authenticationType: AuthenticationType,
val authenticationTypes: List<AuthenticationType>,
val username: String,
) : OutgoingServerSettings

View file

@ -28,7 +28,7 @@ private typealias ServerSettingsFactory<T> = (
hostname: Hostname,
port: Port,
connectionSecurity: ConnectionSecurity,
authenticationType: AuthenticationType,
authenticationTypes: List<AuthenticationType>,
username: String,
) -> T
@ -161,7 +161,7 @@ private class ClientConfigParser(
var hostname: String? = null
var port: Int? = null
var userName: String? = null
var authenticationType: AuthenticationType? = null
val authenticationTypes = mutableListOf<AuthenticationType>()
var connectionSecurity: ConnectionSecurity? = null
readElement { eventType ->
@ -170,7 +170,7 @@ private class ClientConfigParser(
"hostname" -> hostname = readHostname()
"port" -> port = readPort()
"username" -> userName = readUsername()
"authentication" -> authenticationType = readAuthentication(authenticationType)
"authentication" -> readAuthentication(authenticationTypes)
"socketType" -> connectionSecurity = readSocketType()
}
}
@ -179,14 +179,18 @@ private class ClientConfigParser(
val finalHostname = hostname ?: parserError("Missing 'hostname' element")
val finalPort = port ?: parserError("Missing 'port' element")
val finalUserName = userName ?: parserError("Missing 'username' element")
val finalAuthenticationType = authenticationType ?: parserError("No usable 'authentication' element found")
val finalAuthenticationTypes = if (authenticationTypes.isNotEmpty()) {
authenticationTypes.toList()
} else {
parserError("No usable 'authentication' element found")
}
val finalConnectionSecurity = connectionSecurity ?: parserError("Missing 'socketType' element")
return createServerSettings(
finalHostname.toHostname(),
finalPort.toPort(),
finalConnectionSecurity,
finalAuthenticationType,
finalAuthenticationTypes,
finalUserName,
)
}
@ -206,8 +210,9 @@ private class ClientConfigParser(
private fun readUsername(): String = readText().replaceVariables()
private fun readAuthentication(authenticationType: AuthenticationType?): AuthenticationType? {
return authenticationType ?: readText().toAuthenticationType()
private fun readAuthentication(authenticationTypes: MutableList<AuthenticationType>) {
val authenticationType = readText().toAuthenticationType() ?: return
authenticationTypes.add(authenticationType)
}
private fun readSocketType() = readText().toConnectionSecurity()
@ -295,19 +300,19 @@ private class ClientConfigParser(
hostname: Hostname,
port: Port,
connectionSecurity: ConnectionSecurity,
authenticationType: AuthenticationType,
authenticationTypes: List<AuthenticationType>,
username: String,
): ImapServerSettings {
return ImapServerSettings(hostname, port, connectionSecurity, authenticationType, username)
return ImapServerSettings(hostname, port, connectionSecurity, authenticationTypes, username)
}
private fun createSmtpServerSettings(
hostname: Hostname,
port: Port,
connectionSecurity: ConnectionSecurity,
authenticationType: AuthenticationType,
authenticationTypes: List<AuthenticationType>,
username: String,
): SmtpServerSettings {
return SmtpServerSettings(hostname, port, connectionSecurity, authenticationType, username)
return SmtpServerSettings(hostname, port, connectionSecurity, authenticationTypes, username)
}
}

View file

@ -39,14 +39,14 @@ internal class MockAutoconfigFetcher : AutoconfigFetcher {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "irrelevant@domain.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 465.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "irrelevant@domain.example",
),
isTrusted = true,
@ -57,14 +57,14 @@ internal class MockAutoconfigFetcher : AutoconfigFetcher {
hostname = "imap.company.example".toHostname(),
port = 143.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
authenticationTypes = listOf(PasswordEncrypted),
username = "irrelevant@company.example",
),
outgoingServerSettings = SmtpServerSettings(
hostname = "smtp.company.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordEncrypted,
authenticationTypes = listOf(PasswordEncrypted),
username = "irrelevant@company.example",
),
isTrusted = true,

View file

@ -69,14 +69,14 @@ class RealAutoconfigParserTest {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
),
@ -95,14 +95,14 @@ class RealAutoconfigParserTest {
hostname = "imap.gmail.com".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = OAuth2,
authenticationTypes = listOf(OAuth2, PasswordCleartext),
username = "test@gmail.com",
),
SmtpServerSettings(
hostname = "smtp.gmail.com".toHostname(),
port = 465.toPort(),
connectionSecurity = TLS,
authenticationType = OAuth2,
authenticationTypes = listOf(OAuth2, PasswordCleartext),
username = "test@gmail.com",
),
),
@ -125,14 +125,14 @@ class RealAutoconfigParserTest {
hostname = "user.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
SmtpServerSettings(
hostname = "user.outgoing.domain.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "domain.example",
),
),
@ -157,7 +157,7 @@ class RealAutoconfigParserTest {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
)
@ -177,7 +177,7 @@ class RealAutoconfigParserTest {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
)
@ -197,7 +197,7 @@ class RealAutoconfigParserTest {
hostname = "smtp.domain.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
)
@ -217,7 +217,7 @@ class RealAutoconfigParserTest {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
)

View file

@ -145,14 +145,14 @@ class PriorityParallelRunnerTest {
hostname = "imap.domain.example".toHostname(),
port = 993.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 587.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
isTrusted = true,
@ -164,14 +164,14 @@ class PriorityParallelRunnerTest {
hostname = "imap.domain.example".toHostname(),
port = 143.toPort(),
connectionSecurity = StartTLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
SmtpServerSettings(
hostname = "smtp.domain.example".toHostname(),
port = 465.toPort(),
connectionSecurity = TLS,
authenticationType = PasswordCleartext,
authenticationTypes = listOf(PasswordCleartext),
username = "user@domain.example",
),
isTrusted = true,