mirror of
https://github.com/thunderbird/thunderbird-android.git
synced 2024-09-20 04:02:14 +02:00
Migrate accounts using AuthType.AUTOMATIC
or AuthType.LOGIN
This commit is contained in:
parent
3a379646a0
commit
f2acdcbcef
@ -19,7 +19,7 @@ import timber.log.Timber;
|
||||
|
||||
|
||||
public class K9StoragePersister implements StoragePersister {
|
||||
private static final int DB_VERSION = 23;
|
||||
private static final int DB_VERSION = 24;
|
||||
private static final String DB_NAME = "preferences_storage";
|
||||
|
||||
private final Context context;
|
||||
|
@ -0,0 +1,80 @@
|
||||
package com.fsck.k9.preferences.migration
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import com.squareup.moshi.JsonAdapter
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
|
||||
/**
|
||||
* Clean up the authentication type in outgoing server settings.
|
||||
*
|
||||
* Replaces the authentication value "AUTOMATIC" with "PLAIN" when TLS is used, "CRAM_MD5" otherwise.
|
||||
* Replaces the authentication value "LOGIN" with "PLAIN".
|
||||
*/
|
||||
class StorageMigrationTo24(
|
||||
private val db: SQLiteDatabase,
|
||||
private val migrationsHelper: StorageMigrationHelper,
|
||||
) {
|
||||
fun removeLegacyAuthenticationModes() {
|
||||
val accountUuidsListValue = migrationsHelper.readValue(db, "accountUuids")
|
||||
if (accountUuidsListValue.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val accountUuids = accountUuidsListValue.split(",")
|
||||
for (accountUuid in accountUuids) {
|
||||
removeLegacyAuthenticationModesForAccount(accountUuid)
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeLegacyAuthenticationModesForAccount(accountUuid: String) {
|
||||
val outgoingServerSettingsJson = migrationsHelper.readValue(db, "$accountUuid.outgoingServerSettings") ?: return
|
||||
|
||||
val adapter = createJsonAdapter()
|
||||
|
||||
adapter.fromJson(outgoingServerSettingsJson)?.let { settings ->
|
||||
createUpdatedServerSettings(settings)?.let { newSettings ->
|
||||
val json = adapter.toJson(newSettings)
|
||||
migrationsHelper.writeValue(db, "$accountUuid.outgoingServerSettings", json)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createUpdatedServerSettings(serverSettings: Map<String, Any?>): Map<String, Any?>? {
|
||||
val isSecure = serverSettings["connectionSecurity"] == "STARTTLS_REQUIRED" ||
|
||||
serverSettings["connectionSecurity"] == "SSL_TLS_REQUIRED"
|
||||
|
||||
return when (serverSettings["authenticationType"]) {
|
||||
"AUTOMATIC" -> {
|
||||
serverSettings.toMutableMap().apply {
|
||||
fixPortType()
|
||||
this["authenticationType"] = if (isSecure) "PLAIN" else "CRAM_MD5"
|
||||
}
|
||||
}
|
||||
|
||||
"LOGIN" -> {
|
||||
serverSettings.toMutableMap().apply {
|
||||
fixPortType()
|
||||
this["authenticationType"] = "PLAIN"
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableMap<String, Any?>.fixPortType() {
|
||||
// This is so we don't end up with a port value of e.g. "993.0". It would still work, but it looks odd.
|
||||
this["port"] = (this["port"] as? Double)?.toInt()
|
||||
}
|
||||
|
||||
private fun createJsonAdapter(): JsonAdapter<Map<String, Any?>> {
|
||||
val moshi = Moshi.Builder().build()
|
||||
|
||||
return moshi.adapter<Map<String, Any?>>(
|
||||
Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java),
|
||||
).serializeNulls()
|
||||
}
|
||||
}
|
@ -30,5 +30,6 @@ internal object StorageMigrations {
|
||||
if (oldVersion < 21) StorageMigrationTo21(db, migrationsHelper).createPostRemoveNavigationSetting()
|
||||
if (oldVersion < 22) StorageMigrationTo22(db, migrationsHelper).fixServerSettings()
|
||||
if (oldVersion < 23) StorageMigrationTo23(db, migrationsHelper).renameSendClientId()
|
||||
if (oldVersion < 24) StorageMigrationTo24(db, migrationsHelper).removeLegacyAuthenticationModes()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,223 @@
|
||||
package com.fsck.k9.preferences.migration
|
||||
|
||||
import assertk.all
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.key
|
||||
import com.fsck.k9.preferences.createPreferencesDatabase
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.Types
|
||||
import java.util.UUID
|
||||
import kotlin.test.Test
|
||||
import org.junit.After
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class StorageMigrationTo24Test {
|
||||
private val database = createPreferencesDatabase()
|
||||
private val migrationHelper = DefaultStorageMigrationHelper()
|
||||
private val migration = StorageMigrationTo24(database, migrationHelper)
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
database.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AUTOMATIC with SSL_TLS_REQUIRED should be migrated to PLAIN`() {
|
||||
val account = createAccount(
|
||||
"outgoingServerSettings" to toJson(
|
||||
"type" to "smtp",
|
||||
"host" to "irrelevant.invalid",
|
||||
"port" to 465,
|
||||
"connectionSecurity" to "SSL_TLS_REQUIRED",
|
||||
"authenticationType" to "AUTOMATIC",
|
||||
"username" to "username",
|
||||
"password" to null,
|
||||
"clientCertificateAlias" to null,
|
||||
),
|
||||
)
|
||||
writeAccountUuids(account)
|
||||
|
||||
migration.removeLegacyAuthenticationModes()
|
||||
|
||||
assertThat(migrationHelper.readAllValues(database))
|
||||
.key("$account.outgoingServerSettings")
|
||||
.isEqualTo(
|
||||
"""
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "irrelevant.invalid",
|
||||
"port": 465,
|
||||
"connectionSecurity": "SSL_TLS_REQUIRED",
|
||||
"authenticationType": "PLAIN",
|
||||
"username": "username",
|
||||
"password": null,
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".toCompactJson(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AUTOMATIC with STARTTLS_REQUIRED should be migrated to PLAIN`() {
|
||||
val account = createAccount(
|
||||
"outgoingServerSettings" to toJson(
|
||||
"type" to "smtp",
|
||||
"host" to "irrelevant.invalid",
|
||||
"port" to 465,
|
||||
"connectionSecurity" to "STARTTLS_REQUIRED",
|
||||
"authenticationType" to "AUTOMATIC",
|
||||
"username" to "username",
|
||||
"password" to null,
|
||||
"clientCertificateAlias" to null,
|
||||
),
|
||||
)
|
||||
writeAccountUuids(account)
|
||||
|
||||
migration.removeLegacyAuthenticationModes()
|
||||
|
||||
assertThat(migrationHelper.readAllValues(database))
|
||||
.key("$account.outgoingServerSettings")
|
||||
.isEqualTo(
|
||||
"""
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "irrelevant.invalid",
|
||||
"port": 465,
|
||||
"connectionSecurity": "STARTTLS_REQUIRED",
|
||||
"authenticationType": "PLAIN",
|
||||
"username": "username",
|
||||
"password": null,
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".toCompactJson(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `AUTOMATIC with NONE should be migrated to CRAM_MD5`() {
|
||||
val account = createAccount(
|
||||
"outgoingServerSettings" to toJson(
|
||||
"type" to "smtp",
|
||||
"host" to "irrelevant.invalid",
|
||||
"port" to 465,
|
||||
"connectionSecurity" to "NONE",
|
||||
"authenticationType" to "AUTOMATIC",
|
||||
"username" to "username",
|
||||
"password" to null,
|
||||
"clientCertificateAlias" to null,
|
||||
),
|
||||
)
|
||||
writeAccountUuids(account)
|
||||
|
||||
migration.removeLegacyAuthenticationModes()
|
||||
|
||||
assertThat(migrationHelper.readAllValues(database))
|
||||
.key("$account.outgoingServerSettings")
|
||||
.isEqualTo(
|
||||
"""
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "irrelevant.invalid",
|
||||
"port": 465,
|
||||
"connectionSecurity": "NONE",
|
||||
"authenticationType": "CRAM_MD5",
|
||||
"username": "username",
|
||||
"password": null,
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".toCompactJson(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `LOGIN should be migrated to PLAIN`() {
|
||||
val accountOne = createAccount(
|
||||
"outgoingServerSettings" to toJson(
|
||||
"type" to "smtp",
|
||||
"host" to "irrelevant.invalid",
|
||||
"port" to 465,
|
||||
"connectionSecurity" to "SSL_TLS_REQUIRED",
|
||||
"authenticationType" to "LOGIN",
|
||||
"username" to "username",
|
||||
"password" to null,
|
||||
"clientCertificateAlias" to null,
|
||||
),
|
||||
)
|
||||
val accountTwo = createAccount(
|
||||
"outgoingServerSettings" to toJson(
|
||||
"type" to "smtp",
|
||||
"host" to "another.irrelevant.invalid",
|
||||
"port" to 465,
|
||||
"connectionSecurity" to "STARTTLS_REQUIRED",
|
||||
"authenticationType" to "LOGIN",
|
||||
"username" to "user",
|
||||
"password" to "pass",
|
||||
"clientCertificateAlias" to null,
|
||||
),
|
||||
)
|
||||
writeAccountUuids(accountOne, accountTwo)
|
||||
|
||||
migration.removeLegacyAuthenticationModes()
|
||||
|
||||
assertThat(migrationHelper.readAllValues(database)).all {
|
||||
key("$accountOne.outgoingServerSettings").isEqualTo(
|
||||
"""
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "irrelevant.invalid",
|
||||
"port": 465,
|
||||
"connectionSecurity": "SSL_TLS_REQUIRED",
|
||||
"authenticationType": "PLAIN",
|
||||
"username": "username",
|
||||
"password": null,
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".toCompactJson(),
|
||||
)
|
||||
key("$accountTwo.outgoingServerSettings").isEqualTo(
|
||||
"""
|
||||
{
|
||||
"type": "smtp",
|
||||
"host": "another.irrelevant.invalid",
|
||||
"port": 465,
|
||||
"connectionSecurity": "STARTTLS_REQUIRED",
|
||||
"authenticationType": "PLAIN",
|
||||
"username": "user",
|
||||
"password": "pass",
|
||||
"clientCertificateAlias": null
|
||||
}
|
||||
""".toCompactJson(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeAccountUuids(vararg accounts: String) {
|
||||
val accountUuids = accounts.joinToString(separator = ",")
|
||||
migrationHelper.insertValue(database, "accountUuids", accountUuids)
|
||||
}
|
||||
|
||||
private fun createAccount(vararg pairs: Pair<String, String>): String {
|
||||
val accountUuid = UUID.randomUUID().toString()
|
||||
|
||||
for ((key, value) in pairs) {
|
||||
migrationHelper.insertValue(database, "$accountUuid.$key", value)
|
||||
}
|
||||
|
||||
return accountUuid
|
||||
}
|
||||
|
||||
private fun toJson(vararg pairs: Pair<String, Any?>): String {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val adapter = moshi.adapter<Map<String, Any?>>(
|
||||
Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java),
|
||||
).serializeNulls()
|
||||
|
||||
return adapter.toJson(pairs.toMap()) ?: error("Failed to create JSON")
|
||||
}
|
||||
|
||||
// Note: This only works for JSON strings where keys and values don't contain any spaces
|
||||
private fun String.toCompactJson(): String = replace(" ", "").replace("\n", "")
|
||||
}
|
Loading…
Reference in New Issue
Block a user