Skip to content

Commit

Permalink
SIANXSVC-1230: migrate old realm accounts to new authenticator app
Browse files Browse the repository at this point in the history
  • Loading branch information
anx-bkelhar committed Jan 17, 2025
1 parent 2831038 commit cc1b2d2
Show file tree
Hide file tree
Showing 34 changed files with 3,649 additions and 98 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ dependencies {
exclude("org.apache.httpcomponents", "guava-jdk5")
exclude("com.google.http-client", "google-http-client")
}
implementation(libs.realm.gradle.plugin)

// ObjectBox - legacy
debugImplementation("io.objectbox:objectbox-android-objectbrowser:2.9.1")
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/twofasapp/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import com.twofasapp.data.services.remote.CloudSyncWorkDispatcher
import com.twofasapp.di.Modules
import com.twofasapp.parsers.SupportedServices
import com.twofasapp.prefs.usecase.SendCrashLogsPreference
import io.realm.Realm
import net.sqlcipher.database.SQLiteDatabase
import org.koin.android.ext.android.inject
import org.koin.android.ext.koin.androidContext
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/twofasapp/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.twofasapp.prefs.ScopedNavigator
import com.twofasapp.prefs.model.CheckLockStatus
import com.twofasapp.time.TimeProviderImpl
import com.twofasapp.migration.MigrateBoxToRoom
import com.twofasapp.migration.MigrateRealmToRoom
import kotlinx.serialization.json.Json
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.dsl.bind
Expand Down Expand Up @@ -43,6 +44,7 @@ class AppModule : KoinModule {
single { AuthTracker(Provider { get() }) }

singleOf(::MigrateBoxToRoom)
single { MigrateRealmToRoom( get(), get(), get(), get()) }

singleOf(::ShowBrowserExtRequestNotificationImpl) { bind<ShowBrowserExtRequestNotification>() }

Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/twofasapp/di/Modules.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.twofasapp.feature.security.di.SecurityModule
import com.twofasapp.feature.startup.di.StartupModule
import com.twofasapp.feature.trash.di.TrashModule
import com.twofasapp.feature.widget.di.WidgetModule
import com.twofasapp.migration.MigrateRealmToRoom
import com.twofasapp.network.di.NetworkModule
import com.twofasapp.prefs.PreferencesEncryptedModule
import com.twofasapp.prefs.PreferencesPlainModule
Expand Down
144 changes: 144 additions & 0 deletions app/src/main/java/com/twofasapp/migration/Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package com.twofasapp.migration;

import android.net.Uri;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

import io.realm.Realm;
import io.realm.RealmObject;
import io.realm.RealmQuery;
import io.realm.RealmResults;
import io.realm.Sort;
import io.realm.annotations.Ignore;
import io.realm.annotations.PrimaryKey;

/**
* Created by mrauter on 28.02.18.
*/

public class Account extends RealmObject {

@PrimaryKey
private String id = UUID.randomUUID().toString();
private String secret;
private String issuer;
private String username;
private int digits = 6;
private int period = 30;
private Boolean engine = false;
private Date created = new Date();

@Ignore
private Boolean selected = false;

public Account() {

}

public Account(String secret, String issuer, String username, int digits, int period, Boolean engine) {
this.secret = secret;
this.issuer = issuer;
this.username = username;
this.digits = digits;
this.period = period;
this.engine = engine;
}

public static List<Account> readAllFromRealm() {
Realm realm = Realm.getDefaultInstance();
RealmQuery<Account> query = realm.where(Account.class);
RealmResults<Account> results = query.findAll().sort("created", Sort.ASCENDING);
if (results != null) {
return realm.copyFromRealm(results);
}
return null;
}

public static void writeToRealm(Account account) {
if (account != null) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
realm.copyToRealm(account);
realm.commitTransaction();
}
}

public static void deleteFromRealm(Account account) {
if (account != null) {
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
Account result = realm.where(Account.class).equalTo("id", account.getId()).findFirst();
if(result != null) {
result.deleteFromRealm();
}
realm.commitTransaction();
}
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public String getSecret() {
return secret;
}

public void setSecret(String secret) {
this.secret = secret;
}

public String getIssuer() {
return issuer;
}

public void setIssuer(String issuer) {
this.issuer = issuer;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public Boolean getSelected() {
return selected;
}

public void setSelected(Boolean selected) {
this.selected = selected;
}

public int getDigits() {
return digits;
}

public void setDigits(int digits) {
this.digits = digits;
}

public int getPeriod() {
return period;
}

public void setPeriod(int period) {
this.period = period;
}

public Boolean getEngine() {
return engine;
}

public void setEngine(Boolean engine) {
this.engine = engine;
}
}
87 changes: 87 additions & 0 deletions app/src/main/java/com/twofasapp/migration/MigrateRealmToRoom.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.twofasapp.migration

import android.content.Context
import com.twofasapp.common.domain.BackupSyncStatus
import com.twofasapp.common.domain.Service
import com.twofasapp.common.domain.Service.Algorithm
import com.twofasapp.common.time.TimeProvider
import com.twofasapp.data.services.ServicesRepository
import com.twofasapp.parsers.ServiceIcons
import com.twofasapp.prefs.usecase.MigratedRealmToRoomPreference
import io.realm.Realm
import io.realm.RealmConfiguration

class MigrateRealmToRoom(
private val context: Context,
private val servicesRepository: ServicesRepository,
private val migratedRealmToRoomPreference: MigratedRealmToRoomPreference,
private val timeProvider: TimeProvider,
) {

suspend fun invoke() {
try {
if (migratedRealmToRoomPreference.get()) {
return
}

if (context.filesDir?.list()?.contains("default.realm") == false) {
return
}

val oldAccounts = getDataFromRealmDb()
val now = timeProvider.systemCurrentTime()

servicesRepository.addServices(
oldAccounts.map {
Service(
id = 0,
name = it.issuer,
secret = it.secret,
authType = com.twofasapp.common.domain.Service.AuthType.TOTP,
backupSyncStatus = BackupSyncStatus.NOT_SYNCED,
updatedAt = now,
assignedDomains = emptyList(),
serviceTypeId = null,
iconCollectionId = ServiceIcons.defaultCollectionId,
imageType = Service.ImageType.Label,
labelText = it.issuer.take(2).uppercase(),
source = com.twofasapp.common.domain.Service.Source.Manual,
info = it.username,
link = null,
issuer = it.issuer,
period = it.period,
digits = it.digits,
algorithm = Algorithm.SHA1,
iconLight = "",
iconDark = "",
badgeColor = null,
tags = listOf(),
isDeleted = false,
)
}
)

val roomServices = servicesRepository.getServices()
val isSuccess = roomServices.map { it.secret.lowercase().trim() }.distinct()
.containsAll(oldAccounts.map { it.secret.lowercase().trim() }.distinct())
migratedRealmToRoomPreference.put(isSuccess)
} catch (e: Exception) {
e.printStackTrace()
}
}

private fun getDataFromRealmDb(): List<Account> {
Realm.init(context)
val config = RealmConfiguration.Builder().schemaVersion(1).build()
Realm.setDefaultConfiguration(config)

val accounts = Account.readAllFromRealm()
accounts.forEach { account ->
if (account.issuer == null) {
account.issuer = account.username
account.username = ""
}
}
return accounts
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.twofasapp.common.environment.AppBuild
import com.twofasapp.migration.ClearObsoletePrefs
import com.twofasapp.migration.MigrateBoxToRoom
import com.twofasapp.migration.MigratePin
import com.twofasapp.migration.MigrateRealmToRoom
import com.twofasapp.migration.MigrateUnknownServices
import com.twofasapp.prefs.usecase.CurrentAppVersionPreference
import kotlinx.coroutines.withContext
Expand All @@ -27,6 +28,7 @@ class OnAppUpdatedWork(
private val migratePin: MigratePin by inject()
private val migrateUnknownServices: MigrateUnknownServices by inject()
private val migrateBoxToRoom: MigrateBoxToRoom by inject()
private val migrateRealmToRoom: MigrateRealmToRoom by inject()

override suspend fun doWork(): Result {
return withContext(dispatchers.io) {
Expand All @@ -44,6 +46,9 @@ class OnAppUpdatedWork(
Timber.d("Migrate: Box to Room")
migrateBoxToRoom.invoke()

Timber.d("Migrate: Realm to Room")
migrateRealmToRoom.invoke()

Timber.d("Migrate: Unknown services")
migrateUnknownServices.invoke()

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ buildscript {
classpath("org.jetbrains.kotlin:kotlin-serialization:${libs.versions.kotlin.get()}")
classpath("com.google.gms:google-services:4.4.0")
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
classpath (libs.realm.gradle.plugin)
}
}

@Suppress("DSL_SCOPE_VIOLATION")
plugins {
alias(libs.plugins.agpApplication) apply false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ class TwoFasAndroidApplicationPlugin : Plugin<Project> {
with(pluginManager) {
apply("com.android.application")
apply("org.jetbrains.kotlin.android")
apply("kotlin-kapt")
apply("realm-android")
}

extensions.configure<ApplicationExtension> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ThemeColorsLight : ThemeColors() {
override val onSurfacePrimary: Color = Color(0xFF000000)
override val onSurfaceSecondary: Color = Color(0xFF9E9E9E)
override val onSurfaceTertiary: Color = Color(0xFF4C4C4C)
override val primaryIndicator: Color = Color(0xFFC3E187)
override val primaryIndicator: Color = Color(0xFFD5EAAB)
override val serviceBackgroundWithGroups: Color = Color(0xFFFCFCFC)
override val switchTrack: Color = Color(0xFFEEEEEE)
override val switchThumb: Color = Color(0xFFBBBBBB)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.twofasapp.designsystem.R
import com.twofasapp.designsystem.TwTheme
import com.twofasapp.designsystem.ktx.assetAsBitmap
import com.twofasapp.designsystem.service.ServiceImageType
Expand All @@ -36,7 +38,7 @@ internal fun ServiceImage(
when (type) {
ServiceImageType.Icon -> {
Image(
bitmap = assetAsBitmap(if (TwTheme.isDark) iconDark else iconLight),
painter = painterResource(R.drawable.logo_2fas),
contentDescription = null,
modifier = Modifier.size(dimens.imageSize)
)
Expand Down
8 changes: 4 additions & 4 deletions core/designsystem/src/main/res/drawable/ic_change.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M477.74,819.22q-141.35,0 -240.28,-98.93Q138.52,621.35 138.52,480q0,-141.35 98.93,-240.28 98.93,-98.93 240.28,-98.93 74.09,0 140.76,30.19 66.67,30.2 113.93,87.15v-72.83q0,-18.92 12.8,-31.72 12.8,-12.8 31.72,-12.8 18.92,0 31.72,12.8t12.8,31.72v210.17q0,22.09 -15.46,37.54 -15.46,15.46 -37.54,15.46L557.17,448.48q-18.68,0 -31.32,-12.8 -12.64,-12.8 -12.64,-31.72 0,-18.68 12.8,-31.32Q538.82,360 557.74,360h120.09q-32,-52.61 -85,-82.91 -53.01,-30.3 -115.08,-30.3 -97.17,0 -165.2,68.02Q244.52,382.83 244.52,480q0,97.17 68.02,165.2 68.02,68.02 165.2,68.02 61.78,0 114.04,-30.26 52.26,-30.26 83.83,-80.91 11.18,-17.48 31.44,-24.35 20.26,-6.87 39.63,0.39 20.67,7.26 29.93,26.65 9.26,19.39 -1,36.78 -44.39,80.57 -123.5,129.13 -79.11,48.56 -174.37,48.56Z"/>
android:pathData="M16.028,20.505C15.95,20.505 15.882,20.495 15.823,20.476C15.667,20.397 15.589,20.261 15.589,20.065V17.487H10.96C10.843,17.487 10.735,17.448 10.638,17.37C10.56,17.292 10.521,17.194 10.521,17.077C10.521,16.94 10.56,16.833 10.638,16.755C10.735,16.657 10.843,16.608 10.96,16.608H15.999C16.136,16.608 16.243,16.657 16.321,16.755C16.399,16.833 16.448,16.931 16.468,17.048V19.187L22.532,14.528L16.468,9.87V12.009C16.448,12.126 16.399,12.233 16.321,12.331C16.243,12.409 16.146,12.448 16.028,12.448H8.44V15.026C8.44,15.202 8.362,15.329 8.206,15.407C8.05,15.485 7.903,15.476 7.767,15.378L0.501,9.841C0.403,9.743 0.345,9.626 0.325,9.489C0.325,9.333 0.384,9.216 0.501,9.138L7.767,3.601C7.903,3.483 8.05,3.464 8.206,3.542C8.362,3.62 8.44,3.757 8.44,3.952V6.501H13.069C13.186,6.521 13.284,6.569 13.362,6.647C13.46,6.726 13.509,6.833 13.509,6.97C13.509,7.087 13.46,7.185 13.362,7.263C13.284,7.341 13.186,7.39 13.069,7.409H8.001C7.884,7.39 7.776,7.341 7.679,7.263C7.601,7.185 7.562,7.077 7.562,6.94V4.831L1.468,9.489L7.562,14.148V12.009C7.562,11.892 7.601,11.794 7.679,11.716C7.776,11.618 7.884,11.569 8.001,11.569H15.56V8.991C15.579,8.815 15.667,8.688 15.823,8.61C15.979,8.532 16.126,8.542 16.263,8.64L23.499,14.177C23.616,14.274 23.675,14.401 23.675,14.558C23.675,14.694 23.616,14.802 23.499,14.88L16.263,20.417C16.204,20.476 16.126,20.505 16.028,20.505Z"
android:fillColor="#77BC1F"/>
</vector>
4 changes: 2 additions & 2 deletions core/designsystem/src/main/res/drawable/ic_cloud.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M6.55,20.475q-2.475,0 -4.238,-1.7 -1.762,-1.7 -1.762,-4.15 0,-2.1 1.2,-3.763Q2.95,9.2 4.95,8.75q0.725,-2.35 2.663,-3.788Q9.55,3.525 12,3.525q3,0 5.125,2.05T19.4,10.6q1.775,0.35 2.912,1.737 1.138,1.388 1.138,3.213 0,2.05 -1.45,3.488 -1.45,1.437 -3.5,1.437ZM6.55,17.825h11.9q0.975,0 1.663,-0.675 0.687,-0.675 0.687,-1.65 0,-0.975 -0.687,-1.65 -0.688,-0.675 -1.663,-0.675L16.8,13.175L16.8,10.95q0,-1.975 -1.412,-3.375 -1.413,-1.4 -3.388,-1.4 -2.025,0 -3.412,1.475Q7.2,9.125 7.2,11.175h-0.65q-1.375,0 -2.362,0.975 -0.988,0.975 -0.988,2.35t0.988,2.35q0.987,0.975 2.362,0.975ZM12,12Z"/>
android:pathData="M18.563,10.359C19.305,10.652 19.891,11.121 20.32,11.766C20.75,12.391 20.975,13.094 20.994,13.875C20.975,14.949 20.604,15.838 19.881,16.541C19.178,17.244 18.299,17.615 17.244,17.654H6.609C5.594,17.615 4.744,17.264 4.061,16.6C3.377,15.916 3.025,15.066 3.006,14.051C3.025,13.191 3.299,12.439 3.826,11.795C4.354,11.15 5.037,10.731 5.877,10.535C6.131,9.813 6.561,9.236 7.166,8.807C7.771,8.377 8.465,8.162 9.246,8.162C9.539,8.162 9.822,8.191 10.096,8.25C10.545,7.645 11.102,7.176 11.766,6.844C12.43,6.512 13.133,6.346 13.875,6.346C15.086,6.365 16.121,6.746 16.98,7.488C17.84,8.23 18.367,9.188 18.563,10.359ZM17.244,16.775C18.045,16.756 18.719,16.473 19.266,15.926C19.813,15.379 20.096,14.705 20.115,13.904C20.115,13.24 19.93,12.664 19.559,12.176C19.188,11.668 18.69,11.316 18.065,11.121L17.771,11.033L17.742,10.74C17.625,9.725 17.215,8.895 16.512,8.25C15.809,7.605 14.939,7.264 13.904,7.225C13.24,7.244 12.625,7.41 12.059,7.723C11.492,8.035 11.023,8.465 10.652,9.012L10.477,9.305L10.125,9.188C9.852,9.09 9.559,9.041 9.246,9.041C8.621,9.061 8.064,9.256 7.576,9.627C7.107,9.979 6.795,10.457 6.639,11.063L6.551,11.326L6.258,11.385C5.574,11.482 5.008,11.785 4.559,12.293C4.129,12.781 3.904,13.367 3.885,14.051C3.904,14.832 4.168,15.477 4.676,15.984C5.184,16.492 5.828,16.756 6.609,16.775H11.912C15.018,16.775 16.795,16.775 17.244,16.775Z"
android:fillColor="#77BC1F"/>
</vector>
9 changes: 7 additions & 2 deletions core/designsystem/src/main/res/drawable/ic_cloud_off.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M21.9,19.1 L20,17.225q0.4,-0.35 0.6,-0.763 0.2,-0.412 0.2,-0.937 0,-0.925 -0.675,-1.6T18.5,13.25h-1.7v-2.2q0,-2 -1.412,-3.4 -1.413,-1.4 -3.388,-1.4 -0.675,0 -1.25,0.175t-1.075,0.45l-1.925,-1.9q0.875,-0.65 1.988,-1.013Q10.85,3.6 12,3.6q3,0 5.125,2.062Q19.25,7.725 19.4,10.7q1.775,0.35 2.912,1.725 1.138,1.375 1.138,3.15 0,1.075 -0.45,2.012 -0.45,0.938 -1.1,1.513ZM19.225,22.375 L17.3,20.475L6.5,20.475q-2.475,0 -4.212,-1.725Q0.55,17.025 0.55,14.55q0,-2 1.225,-3.625T4.95,8.85q0.05,-0.1 0.087,-0.275 0.038,-0.175 0.063,-0.3l-3.35,-3.35q-0.3,-0.325 -0.3,-0.788 0,-0.462 0.3,-0.787 0.325,-0.3 0.788,-0.3 0.462,0 0.787,0.3l17.45,17.45q0.3,0.325 0.3,0.787 0,0.463 -0.3,0.788 -0.325,0.3 -0.775,0.3t-0.775,-0.3ZM6.5,17.825h8.15l-7.35,-7.35q-0.025,0.2 -0.037,0.387 -0.013,0.188 -0.013,0.388L6.5,11.25q-1.35,0 -2.325,0.962 -0.975,0.963 -0.975,2.338 0,1.35 0.975,2.313 0.975,0.962 2.325,0.962ZM14.85,12.075ZM10.975,14.15Z"/>
android:pathData="M18.563,10.359C19.305,10.652 19.891,11.121 20.32,11.766C20.75,12.391 20.975,13.094 20.994,13.875C20.975,14.949 20.604,15.838 19.881,16.541C19.178,17.244 18.299,17.615 17.244,17.654H6.609C5.594,17.615 4.744,17.264 4.061,16.6C3.377,15.916 3.025,15.066 3.006,14.051C3.025,13.191 3.299,12.439 3.826,11.795C4.354,11.15 5.037,10.731 5.877,10.535C6.131,9.813 6.561,9.236 7.166,8.807C7.771,8.377 8.465,8.162 9.246,8.162C9.539,8.162 9.822,8.191 10.096,8.25C10.545,7.645 11.102,7.176 11.766,6.844C12.43,6.512 13.133,6.346 13.875,6.346C15.086,6.365 16.121,6.746 16.98,7.488C17.84,8.23 18.367,9.188 18.563,10.359ZM17.244,16.775C18.045,16.756 18.719,16.473 19.266,15.926C19.813,15.379 20.096,14.705 20.115,13.904C20.115,13.24 19.93,12.664 19.559,12.176C19.188,11.668 18.69,11.316 18.065,11.121L17.771,11.033L17.742,10.74C17.625,9.725 17.215,8.895 16.512,8.25C15.809,7.605 14.939,7.264 13.904,7.225C13.24,7.244 12.625,7.41 12.059,7.723C11.492,8.035 11.023,8.465 10.652,9.012L10.477,9.305L10.125,9.188C9.852,9.09 9.559,9.041 9.246,9.041C8.621,9.061 8.064,9.256 7.576,9.627C7.107,9.979 6.795,10.457 6.639,11.063L6.551,11.326L6.258,11.385C5.574,11.482 5.008,11.785 4.559,12.293C4.129,12.781 3.904,13.367 3.885,14.051C3.904,14.832 4.168,15.477 4.676,15.984C5.184,16.492 5.828,16.756 6.609,16.775H11.912C15.018,16.775 16.795,16.775 17.244,16.775Z"
android:fillColor="#77BC1F"/>
<path
android:strokeWidth="1"
android:pathData="M6,7.017L17.502,18.911"
android:fillColor="#00000000"
android:strokeColor="#77BC1F"/>
</vector>
Loading

0 comments on commit cc1b2d2

Please sign in to comment.