Migration from old to new preferences

pull/263/head
maxmoney21m 2023-03-12 13:24:51 +08:00
rodzic 992796a7bf
commit 269197a59d
5 zmienionych plików z 92 dodań i 26 usunięć

Wyświetl plik

@ -6,13 +6,17 @@ import androidx.security.crypto.MasterKey
object EncryptedStorage {
private const val PREFERENCES_NAME = "secret_keeper"
fun prefsFileName(npub: String? = null): String {
return if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
}
fun preferences(npub: String? = null): EncryptedSharedPreferences {
val context = Amethyst.instance
val masterKey: MasterKey = MasterKey.Builder(context, MasterKey.DEFAULT_MASTER_KEY_ALIAS)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val preferencesName = if (npub == null) PREFERENCES_NAME else "${PREFERENCES_NAME}_$npub"
val preferencesName = prefsFileName(npub)
return EncryptedSharedPreferences.create(
context,

Wyświetl plik

@ -11,17 +11,25 @@ import com.vitorpamplona.amethyst.model.toByteArray
import com.vitorpamplona.amethyst.service.model.ContactListEvent
import com.vitorpamplona.amethyst.service.model.Event
import com.vitorpamplona.amethyst.service.model.Event.Companion.getRefinedEvent
import fr.acinq.secp256k1.Hex
import nostr.postr.Persona
import nostr.postr.toHex
import nostr.postr.toNpub
import java.io.File
import java.util.Locale
// Release mode (!BuildConfig.DEBUG) always uses encrypted preferences
// To use plaintext SharedPreferences for debugging, set this to true
// It will only apply in Debug builds
const val DEBUG_PLAINTEXT_PREFERENCES = true
private const val DEBUG_PLAINTEXT_PREFERENCES = false
private const val OLD_PREFS_FILENAME = "secret_keeper"
data class AccountInfo(val npub: String, val current: Boolean, val displayName: String?, val profilePicture: String?)
data class AccountInfo(
val npub: String,
val current: Boolean,
val displayName: String?,
val profilePicture: String?
)
private object PrefKeys {
const val CURRENT_ACCOUNT = "currently_logged_in_account"
@ -57,6 +65,9 @@ object LocalPreferences {
private val savedAccounts: Set<String>
get() = encryptedPreferences().getStringSet(PrefKeys.SAVED_ACCOUNTS, null) ?: setOf()
private val prefsDirPath: String
get() = "${Amethyst.instance.filesDir.parent}/shared_prefs/"
private fun addAccount(npub: String) {
val accounts = savedAccounts.toMutableSet()
accounts.add(npub)
@ -66,6 +77,12 @@ object LocalPreferences {
}.apply()
}
private fun setCurrentAccount(account: Account) {
val npub = account.userProfile().pubkeyNpub()
currentAccount = npub
addAccount(npub)
}
/**
* Removes the account from the app level shared preferences
*/
@ -82,8 +99,7 @@ object LocalPreferences {
* Deletes the npub-specific shared preference file
*/
private fun deleteUserPreferenceFile(npub: String) {
val context = Amethyst.instance
val prefsDir = File("${context.filesDir.parent}/shared_prefs/")
val prefsDir = File(prefsDirPath)
prefsDir.list()?.forEach {
if (it.contains(npub)) {
File(prefsDir, it).delete()
@ -93,7 +109,7 @@ object LocalPreferences {
private fun encryptedPreferences(npub: String? = null): SharedPreferences {
return if (BuildConfig.DEBUG && DEBUG_PLAINTEXT_PREFERENCES) {
val preferenceFile = if (npub == null) "testing_only" else "testing_only_$npub"
val preferenceFile = if (npub == null) "debug_prefs" else "debug_prefs_$npub"
Amethyst.instance.getSharedPreferences(preferenceFile, Context.MODE_PRIVATE)
} else {
return EncryptedStorage.preferences(npub)
@ -103,9 +119,13 @@ object LocalPreferences {
/**
* Clears the preferences for a given npub, deletes the preferences xml file,
* and switches the user to the first account in the list if it exists
*
* We need to use `commit()` to write changes to disk and release the file
* lock so that it can be deleted. If we use `apply()` there is a race
* condition and the file will probably not be deleted
*/
@SuppressLint("ApplySharedPref")
fun clearEncryptedStorage(npub: String) {
fun updatePrefsForLogout(npub: String) {
val userPrefs = encryptedPreferences(npub)
userPrefs.edit().clear().commit()
removeAccount(npub)
@ -119,7 +139,12 @@ object LocalPreferences {
}
}
fun findAllLocalAccounts(): List<AccountInfo> {
fun updatePrefsForLogin(account: Account) {
setCurrentAccount(account)
saveToEncryptedStorage(account)
}
fun allSavedAccounts(): List<AccountInfo> {
return savedAccounts.map { npub ->
val prefs = encryptedPreferences(npub)
@ -132,12 +157,6 @@ object LocalPreferences {
}
}
fun setCurrentAccount(account: Account) {
val npub = account.userProfile().pubkeyNpub()
currentAccount = npub
addAccount(npub)
}
fun saveToEncryptedStorage(account: Account) {
val prefs = encryptedPreferences(account.userProfile().pubkeyNpub())
prefs.edit().apply {
@ -157,11 +176,6 @@ object LocalPreferences {
}.apply()
}
fun login(account: Account) {
setCurrentAccount(account)
saveToEncryptedStorage(account)
}
fun loadFromEncryptedStorage(): Account? {
encryptedPreferences(currentAccount).apply {
val pubKey = getString(PrefKeys.NOSTR_PUBKEY, null) ?: return null
@ -183,7 +197,8 @@ object LocalPreferences {
val latestContactList = try {
getString(PrefKeys.LATEST_CONTACT_LIST, null)?.let {
Event.gson.fromJson(it, Event::class.java).getRefinedEvent(true) as ContactListEvent
Event.gson.fromJson(it, Event::class.java)
.getRefinedEvent(true) as ContactListEvent
}
} catch (e: Throwable) {
e.printStackTrace()
@ -192,7 +207,10 @@ object LocalPreferences {
val languagePreferences = try {
getString(PrefKeys.LANGUAGE_PREFS, null)?.let {
gson.fromJson(it, object : TypeToken<Map<String, String>>() {}.type) as Map<String, String>
gson.fromJson(
it,
object : TypeToken<Map<String, String>>() {}.type
) as Map<String, String>
} ?: mapOf()
} catch (e: Throwable) {
e.printStackTrace()
@ -227,4 +245,45 @@ object LocalPreferences {
return getLong(PrefKeys.LAST_READ(route), 0)
}
}
fun migrateSingleUserPrefs() {
if (currentAccount != null) return
val pubkey = encryptedPreferences().getString(PrefKeys.NOSTR_PUBKEY, null) ?: return
val npub = Hex.decode(pubkey).toNpub()
val stringPrefs = listOf(
PrefKeys.NOSTR_PRIVKEY,
PrefKeys.NOSTR_PUBKEY,
PrefKeys.RELAYS,
PrefKeys.LANGUAGE_PREFS,
PrefKeys.TRANSLATE_TO,
PrefKeys.ZAP_AMOUNTS,
PrefKeys.LATEST_CONTACT_LIST
)
val stringSetPrefs = listOf(
PrefKeys.FOLLOWING_CHANNELS,
PrefKeys.HIDDEN_USERS,
PrefKeys.DONT_TRANSLATE_FROM
)
encryptedPreferences().apply {
val appPrefs = this
encryptedPreferences(npub).edit().apply {
val userPrefs = this
stringPrefs.forEach { userPrefs.putString(it, appPrefs.getString(it, null)) }
stringSetPrefs.forEach { userPrefs.putStringSet(it, appPrefs.getStringSet(it, null)) }
userPrefs.putBoolean(
PrefKeys.HIDE_DELETE_REQUEST_INFO,
appPrefs.getBoolean(PrefKeys.HIDE_DELETE_REQUEST_INFO, false)
)
}.apply()
}
encryptedPreferences().edit().clear().apply()
addAccount(npub)
currentAccount = npub
}
}

Wyświetl plik

@ -14,6 +14,7 @@ import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.decode.SvgDecoder
import com.vitorpamplona.amethyst.LocalPreferences
import com.vitorpamplona.amethyst.ServiceManager
import com.vitorpamplona.amethyst.service.nip19.Nip19
import com.vitorpamplona.amethyst.service.relays.Client
@ -48,6 +49,8 @@ class MainActivity : FragmentActivity() {
.build()
}
LocalPreferences.migrateSingleUserPrefs()
setContent {
AmethystTheme {
// A surface container using the 'background' color from the theme

Wyświetl plik

@ -55,7 +55,7 @@ fun AccountSwitchBottomSheet(
accountStateViewModel: AccountStateViewModel
) {
val context = LocalContext.current
val accounts = LocalPreferences.findAllLocalAccounts()
val accounts = LocalPreferences.allSavedAccounts()
val accountState by accountViewModel.accountLiveData.observeAsState()
val account = accountState?.account ?: return

Wyświetl plik

@ -51,18 +51,18 @@ class AccountStateViewModel() : ViewModel() {
Account(Persona(Hex.decode(key)))
}
LocalPreferences.login(account)
LocalPreferences.updatePrefsForLogin(account)
login(account)
}
fun newKey() {
val account = Account(Persona())
LocalPreferences.login(account)
LocalPreferences.updatePrefsForLogin(account)
login(account)
}
fun login(account: Account) {
LocalPreferences.login(account)
LocalPreferences.updatePrefsForLogin(account)
if (account.loggedIn.privKey != null) {
_accountContent.update { AccountState.LoggedIn(account) }
@ -105,7 +105,7 @@ class AccountStateViewModel() : ViewModel() {
_accountContent.update { AccountState.LoggedOff }
LocalPreferences.clearEncryptedStorage(npub)
LocalPreferences.updatePrefsForLogout(npub)
tryLoginExistingAccount()
}
}