Merge pull request #6097 from k9mail/oauth_config_provider

Add proper way to retrieve OAuth credentials
This commit is contained in:
cketti 2022-06-06 22:17:20 +02:00 committed by GitHub
commit 402259a834
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 78 additions and 40 deletions

View file

@ -4,5 +4,5 @@ import org.koin.dsl.module
val autodiscoveryProvidersXmlModule = module {
factory { ProvidersXmlProvider(context = get()) }
factory { ProvidersXmlDiscovery(xmlProvider = get()) }
factory { ProvidersXmlDiscovery(xmlProvider = get(), oAuthConfigurationProvider = get()) }
}

View file

@ -9,12 +9,14 @@ import com.fsck.k9.autodiscovery.api.DiscoveryTarget
import com.fsck.k9.helper.EmailHelper
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.fsck.k9.preferences.Protocols
import org.xmlpull.v1.XmlPullParser
import timber.log.Timber
class ProvidersXmlDiscovery(
private val xmlProvider: ProvidersXmlProvider
private val xmlProvider: ProvidersXmlProvider,
private val oAuthConfigurationProvider: OAuthConfigurationProvider
) : ConnectionSettingsDiscovery {
override fun discover(email: String, target: DiscoveryTarget): DiscoveryResults? {
@ -104,8 +106,7 @@ class ProvidersXmlDiscovery(
uri.port
}
// TODO: Remove this hack
val authType = if (host == "imap.gmail.com" || host == "imap.googlemail.com") {
val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
AuthType.XOAUTH2
} else {
AuthType.PLAIN
@ -134,8 +135,7 @@ class ProvidersXmlDiscovery(
uri.port
}
// TODO: Remove this hack
val authType = if (host == "smtp.gmail.com" || host == "smtp.googlemail.com") {
val authType = if (oAuthConfigurationProvider.getConfiguration(host) != null) {
AuthType.XOAUTH2
} else {
AuthType.PLAIN

View file

@ -5,12 +5,15 @@ import com.fsck.k9.RobolectricTest
import com.fsck.k9.autodiscovery.api.DiscoveryTarget
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ConnectionSecurity
import com.fsck.k9.oauth.OAuthConfiguration
import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class ProvidersXmlDiscoveryTest : RobolectricTest() {
private val xmlProvider = ProvidersXmlProvider(ApplicationProvider.getApplicationContext())
private val providersXmlDiscovery = ProvidersXmlDiscovery(xmlProvider)
private val oAuthConfigurationProvider = createOAuthConfigurationProvider()
private val providersXmlDiscovery = ProvidersXmlDiscovery(xmlProvider, oAuthConfigurationProvider)
@Test
fun discover_withGmailDomain_shouldReturnCorrectSettings() {
@ -39,4 +42,20 @@ class ProvidersXmlDiscoveryTest : RobolectricTest() {
assertThat(connectionSettings).isNull()
}
private fun createOAuthConfigurationProvider(): OAuthConfigurationProvider {
val googleConfig = OAuthConfiguration(
clientId = "irrelevant",
scopes = listOf("irrelevant"),
authorizationEndpoint = "irrelevant",
tokenEndpoint = "irrelevant"
)
return OAuthConfigurationProvider(
configurations = mapOf(
listOf("imap.gmail.com", "smtp.gmail.com") to googleConfig,
),
googleConfiguration = googleConfig
)
}
}

View file

@ -1,4 +1,4 @@
package com.fsck.k9.activity.setup
package com.fsck.k9.oauth
data class OAuthConfiguration(
val clientId: String,

View file

@ -0,0 +1,22 @@
package com.fsck.k9.oauth
class OAuthConfigurationProvider(
private val configurations: Map<List<String>, OAuthConfiguration>,
private val googleConfiguration: OAuthConfiguration
) {
private val hostnameMapping: Map<String, OAuthConfiguration> = buildMap {
for ((hostnames, configuration) in configurations) {
for (hostname in hostnames) {
put(hostname.lowercase(), configuration)
}
}
}
fun getConfiguration(hostname: String): OAuthConfiguration? {
return hostnameMapping[hostname.lowercase()]
}
fun isGoogle(hostname: String): Boolean {
return getConfiguration(hostname) == googleConfiguration
}
}

View file

@ -1,7 +1,6 @@
package com.fsck.k9
import com.fsck.k9.activity.setup.OAuthCredentials
import com.fsck.k9.auth.K9OAuthCredentials
import com.fsck.k9.auth.createOAuthConfigurationProvider
import com.fsck.k9.backends.backendsModule
import com.fsck.k9.controller.ControllerExtension
import com.fsck.k9.crypto.EncryptionExtractor
@ -31,7 +30,7 @@ private val mainAppModule = module {
single(named("controllerExtensions")) { emptyList<ControllerExtension>() }
single<EncryptionExtractor> { OpenPgpEncryptionExtractor.newInstance() }
single<StoragePersister> { K9StoragePersister(get()) }
factory<OAuthCredentials> { K9OAuthCredentials() }
single { createOAuthConfigurationProvider() }
}
val appModules = listOf(

View file

@ -1,9 +0,0 @@
package com.fsck.k9.auth
import com.fsck.k9.BuildConfig
import com.fsck.k9.activity.setup.OAuthCredentials
class K9OAuthCredentials : OAuthCredentials {
override val gmailClientId: String
get() = BuildConfig.OAUTH_GMAIL_CLIENT_ID
}

View file

@ -0,0 +1,21 @@
package com.fsck.k9.auth
import com.fsck.k9.BuildConfig
import com.fsck.k9.oauth.OAuthConfiguration
import com.fsck.k9.oauth.OAuthConfigurationProvider
fun createOAuthConfigurationProvider(): OAuthConfigurationProvider {
val googleConfig = OAuthConfiguration(
clientId = BuildConfig.OAUTH_GMAIL_CLIENT_ID,
scopes = listOf("https://mail.google.com/"),
authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth",
tokenEndpoint = "https://oauth2.googleapis.com/token"
)
return OAuthConfigurationProvider(
configurations = mapOf(
listOf("imap.gmail.com", "imap.googlemail.com", "smtp.gmail.com", "smtp.googlemail.com") to googleConfig,
),
googleConfiguration = googleConfig
)
}

View file

@ -6,5 +6,5 @@ import org.koin.dsl.module
val activityModule = module {
single { MessageLoaderHelperFactory(messageViewInfoExtractorFactory = get(), htmlSettingsProvider = get()) }
viewModel { AuthViewModel(application = get(), accountManager = get(), oauthCredentials = get()) }
viewModel { AuthViewModel(application = get(), accountManager = get(), oAuthConfigurationProvider = get()) }
}

View file

@ -16,6 +16,8 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.fsck.k9.Account
import com.fsck.k9.oauth.OAuthConfiguration
import com.fsck.k9.oauth.OAuthConfigurationProvider
import com.fsck.k9.preferences.AccountManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@ -38,7 +40,7 @@ private const val KEY_AUTHORIZATION = "app.k9mail_auth"
class AuthViewModel(
application: Application,
private val accountManager: AccountManager,
private val oauthCredentials: OAuthCredentials
private val oAuthConfigurationProvider: OAuthConfigurationProvider
) : AndroidViewModel(application) {
private var authService: AuthorizationService? = null
private val authState = AuthState()
@ -70,8 +72,7 @@ class AuthViewModel(
}
fun isUsingGoogle(account: Account): Boolean {
val config = findOAuthConfiguration(account)
return config?.authorizationEndpoint == "https://accounts.google.com/o/oauth2/v2/auth"
return oAuthConfigurationProvider.isGoogle(account.incomingServerSettings.host!!)
}
private fun getOrCreateAuthState(account: Account): AuthState {
@ -137,17 +138,7 @@ class AuthViewModel(
}
private fun findOAuthConfiguration(account: Account): OAuthConfiguration? {
return when (account.incomingServerSettings.host) {
"imap.gmail.com", "imap.googlemail.com" -> {
OAuthConfiguration(
clientId = oauthCredentials.gmailClientId,
scopes = listOf("https://mail.google.com/"),
authorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth",
tokenEndpoint = "https://oauth2.googleapis.com/token"
)
}
else -> null
}
return oAuthConfigurationProvider.getConfiguration(account.incomingServerSettings.host!!)
}
private fun onLoginResult(authorizationResult: AuthorizationResult?) {

View file

@ -1,5 +0,0 @@
package com.fsck.k9.activity.setup
interface OAuthCredentials {
val gmailClientId: String
}