Closes #7344: Login storage refactor
The a-c side of this work is in https://github.com/mozilla-mobile/android-components/pull/6128 This switches Fenix to use `SyncableLoginsStorage`, which caches a connection internally on first access, and doesn't expose any lock/unlock APIs at the public boundary.master
parent
6a75d822ca
commit
e6e2dd94c7
|
@ -6,10 +6,9 @@ import android.content.Context
|
|||
import android.os.Bundle
|
||||
import mozilla.components.browser.engine.gecko.autofill.GeckoLoginDelegateWrapper
|
||||
import mozilla.components.browser.engine.gecko.glean.GeckoAdapter
|
||||
import mozilla.components.concept.storage.LoginsStorage
|
||||
import mozilla.components.lib.crash.handler.CrashHandlerService
|
||||
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
|
||||
import mozilla.components.service.experiments.Experiments
|
||||
import mozilla.components.service.sync.logins.AsyncLoginsStorage
|
||||
import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
@ -24,11 +23,10 @@ object GeckoProvider {
|
|||
@Synchronized
|
||||
fun getOrCreateRuntime(
|
||||
context: Context,
|
||||
storage: AsyncLoginsStorage,
|
||||
securePreferences: SecureAbove22Preferences
|
||||
storage: LoginsStorage
|
||||
): GeckoRuntime {
|
||||
if (runtime == null) {
|
||||
runtime = createRuntime(context, storage, securePreferences)
|
||||
runtime = createRuntime(context, storage)
|
||||
}
|
||||
|
||||
return runtime!!
|
||||
|
@ -36,8 +34,7 @@ object GeckoProvider {
|
|||
|
||||
private fun createRuntime(
|
||||
context: Context,
|
||||
storage: AsyncLoginsStorage,
|
||||
securePreferences: SecureAbove22Preferences
|
||||
storage: LoginsStorage
|
||||
): GeckoRuntime {
|
||||
val builder = GeckoRuntimeSettings.Builder()
|
||||
|
||||
|
@ -69,7 +66,6 @@ object GeckoProvider {
|
|||
val geckoRuntime = GeckoRuntime.create(context, runtimeSettings)
|
||||
val loginStorageDelegate = GeckoLoginStorageDelegate(
|
||||
storage,
|
||||
securePreferences,
|
||||
{ context.settings().shouldAutofillLogins && context.settings().shouldPromptToSaveLogins }
|
||||
)
|
||||
geckoRuntime.loginStorageDelegate = GeckoLoginDelegateWrapper(loginStorageDelegate)
|
||||
|
|
|
@ -6,9 +6,8 @@ import android.content.Context
|
|||
import android.os.Bundle
|
||||
import mozilla.components.browser.engine.gecko.autofill.GeckoLoginDelegateWrapper
|
||||
import mozilla.components.browser.engine.gecko.glean.GeckoAdapter
|
||||
import mozilla.components.concept.storage.LoginsStorage
|
||||
import mozilla.components.lib.crash.handler.CrashHandlerService
|
||||
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
|
||||
import mozilla.components.service.sync.logins.AsyncLoginsStorage
|
||||
import mozilla.components.service.sync.logins.GeckoLoginStorageDelegate
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.ext.settings
|
||||
|
@ -23,11 +22,10 @@ object GeckoProvider {
|
|||
@Synchronized
|
||||
fun getOrCreateRuntime(
|
||||
context: Context,
|
||||
storage: AsyncLoginsStorage,
|
||||
securePreferences: SecureAbove22Preferences
|
||||
storage: LoginsStorage
|
||||
): GeckoRuntime {
|
||||
if (runtime == null) {
|
||||
runtime = createRuntime(context, storage, securePreferences)
|
||||
runtime = createRuntime(context, storage)
|
||||
}
|
||||
|
||||
return runtime!!
|
||||
|
@ -35,8 +33,7 @@ object GeckoProvider {
|
|||
|
||||
private fun createRuntime(
|
||||
context: Context,
|
||||
storage: AsyncLoginsStorage,
|
||||
securePreferences: SecureAbove22Preferences
|
||||
storage: LoginsStorage
|
||||
): GeckoRuntime {
|
||||
val builder = GeckoRuntimeSettings.Builder()
|
||||
|
||||
|
@ -61,7 +58,6 @@ object GeckoProvider {
|
|||
val geckoRuntime = GeckoRuntime.create(context, runtimeSettings)
|
||||
val loginStorageDelegate = GeckoLoginStorageDelegate(
|
||||
storage,
|
||||
securePreferences,
|
||||
{ context.settings().shouldAutofillLogins && context.settings().shouldPromptToSaveLogins }
|
||||
)
|
||||
geckoRuntime.loginStorageDelegate = GeckoLoginDelegateWrapper(loginStorageDelegate)
|
||||
|
|
|
@ -328,8 +328,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
customTabId = customTabSessionId,
|
||||
fragmentManager = parentFragmentManager,
|
||||
loginValidationDelegate = DefaultLoginValidationDelegate(
|
||||
context.components.core.asyncPasswordsStorage,
|
||||
context.components.core.getSecureAbove22Preferences()
|
||||
context.components.core.passwordsStorage
|
||||
),
|
||||
isSaveLoginEnabled = {
|
||||
context.settings().shouldPromptToSaveLogins
|
||||
|
|
|
@ -20,7 +20,6 @@ import mozilla.components.feature.accounts.push.SendTabFeature
|
|||
import mozilla.components.feature.push.AutoPushFeature
|
||||
import mozilla.components.feature.push.PushConfig
|
||||
import mozilla.components.lib.crash.CrashReporter
|
||||
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
|
||||
import mozilla.components.service.fxa.DeviceConfig
|
||||
import mozilla.components.service.fxa.ServerConfig
|
||||
import mozilla.components.service.fxa.SyncConfig
|
||||
|
@ -30,7 +29,7 @@ import mozilla.components.service.fxa.manager.SCOPE_SESSION
|
|||
import mozilla.components.service.fxa.manager.SCOPE_SYNC
|
||||
import mozilla.components.service.fxa.manager.SyncEnginesStorage
|
||||
import mozilla.components.service.fxa.sync.GlobalSyncableStoreProvider
|
||||
import mozilla.components.service.sync.logins.SyncableLoginsStore
|
||||
import mozilla.components.service.sync.logins.SyncableLoginsStorage
|
||||
import mozilla.components.support.base.log.logger.Logger
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
|
@ -51,8 +50,7 @@ class BackgroundServices(
|
|||
crashReporter: CrashReporter,
|
||||
historyStorage: PlacesHistoryStorage,
|
||||
bookmarkStorage: PlacesBookmarksStorage,
|
||||
passwordsStorage: SyncableLoginsStore,
|
||||
secureAbove22Preferences: SecureAbove22Preferences
|
||||
passwordsStorage: SyncableLoginsStorage
|
||||
) {
|
||||
fun defaultDeviceName(context: Context): String =
|
||||
context.getString(
|
||||
|
@ -88,7 +86,7 @@ class BackgroundServices(
|
|||
syncPeriodInMinutes = 240L) // four hours
|
||||
}
|
||||
|
||||
val pushService by lazy { FirebasePushService() }
|
||||
private val pushService by lazy { FirebasePushService() }
|
||||
|
||||
val push by lazy { makePushConfig()?.let { makePush(it) } }
|
||||
|
||||
|
@ -97,7 +95,6 @@ class BackgroundServices(
|
|||
GlobalSyncableStoreProvider.configureStore(SyncEngine.History to historyStorage)
|
||||
GlobalSyncableStoreProvider.configureStore(SyncEngine.Bookmarks to bookmarkStorage)
|
||||
GlobalSyncableStoreProvider.configureStore(SyncEngine.Passwords to passwordsStorage)
|
||||
GlobalSyncableStoreProvider.configureKeyStorage(secureAbove22Preferences)
|
||||
}
|
||||
|
||||
private val telemetryAccountObserver = TelemetryAccountObserver(
|
||||
|
|
|
@ -29,8 +29,7 @@ class Components(private val context: Context) {
|
|||
analytics.crashReporter,
|
||||
core.historyStorage,
|
||||
core.bookmarksStorage,
|
||||
core.syncablePasswordsStorage,
|
||||
core.getSecureAbove22Preferences()
|
||||
core.passwordsStorage
|
||||
)
|
||||
}
|
||||
val services by lazy { Services(context, backgroundServices.accountManager) }
|
||||
|
|
|
@ -9,7 +9,6 @@ import android.app.Application
|
|||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import io.sentry.Sentry
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -38,8 +37,7 @@ import mozilla.components.feature.webcompat.WebCompatFeature
|
|||
import mozilla.components.feature.webnotifications.WebNotificationFeature
|
||||
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
|
||||
import mozilla.components.lib.dataprotect.generateEncryptionKey
|
||||
import mozilla.components.service.sync.logins.AsyncLoginsStorageAdapter
|
||||
import mozilla.components.service.sync.logins.SyncableLoginsStore
|
||||
import mozilla.components.service.sync.logins.SyncableLoginsStorage
|
||||
import org.mozilla.fenix.AppRequestInterceptor
|
||||
import org.mozilla.fenix.Config
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
|
@ -47,7 +45,6 @@ import org.mozilla.fenix.HomeActivity
|
|||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
import java.io.File
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
|
@ -76,9 +73,7 @@ class Core(private val context: Context) {
|
|||
GeckoEngine(
|
||||
context,
|
||||
defaultSettings,
|
||||
GeckoProvider.getOrCreateRuntime(
|
||||
context, asyncPasswordsStorage, getSecureAbove22Preferences()
|
||||
)
|
||||
GeckoProvider.getOrCreateRuntime(context, passwordsStorage)
|
||||
).also {
|
||||
WebCompatFeature.install(it)
|
||||
}
|
||||
|
@ -90,11 +85,7 @@ class Core(private val context: Context) {
|
|||
val client: Client by lazy {
|
||||
GeckoViewFetchClient(
|
||||
context,
|
||||
GeckoProvider.getOrCreateRuntime(
|
||||
context,
|
||||
asyncPasswordsStorage,
|
||||
getSecureAbove22Preferences()
|
||||
)
|
||||
GeckoProvider.getOrCreateRuntime(context, passwordsStorage)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -201,6 +192,8 @@ class Core(private val context: Context) {
|
|||
|
||||
val bookmarksStorage by lazy { PlacesBookmarksStorage(context) }
|
||||
|
||||
val passwordsStorage by lazy { SyncableLoginsStorage(context, passwordsEncryptionKey) }
|
||||
|
||||
val tabCollectionStorage by lazy { TabCollectionStorage(context, sessionManager) }
|
||||
|
||||
val topSiteStorage by lazy { TopSiteStorage(context) }
|
||||
|
@ -209,36 +202,19 @@ class Core(private val context: Context) {
|
|||
|
||||
val webAppManifestStorage by lazy { ManifestStorage(context) }
|
||||
|
||||
val asyncPasswordsStorage by lazy {
|
||||
AsyncLoginsStorageAdapter.forDatabase(
|
||||
File(
|
||||
context.filesDir,
|
||||
"logins.sqlite"
|
||||
).canonicalPath
|
||||
)
|
||||
}
|
||||
|
||||
val syncablePasswordsStorage by lazy {
|
||||
SyncableLoginsStore(
|
||||
asyncPasswordsStorage
|
||||
) {
|
||||
CompletableDeferred(passwordsEncryptionKey)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared Preferences that encrypt/decrypt using Android KeyStore and lib-dataprotect for 23+
|
||||
* only on Nightly/Debug for now, otherwise simply stored.
|
||||
* See https://github.com/mozilla-mobile/fenix/issues/8324
|
||||
*/
|
||||
fun getSecureAbove22Preferences() =
|
||||
private fun getSecureAbove22Preferences() =
|
||||
SecureAbove22Preferences(
|
||||
context = context,
|
||||
name = KEY_STORAGE_NAME,
|
||||
forceInsecure = !Config.channel.isNightlyOrDebug
|
||||
)
|
||||
|
||||
val passwordsEncryptionKey: String =
|
||||
private val passwordsEncryptionKey by lazy {
|
||||
getSecureAbove22Preferences().getString(PASSWORDS_KEY)
|
||||
?: generateEncryptionKey(KEY_STRENGTH).also {
|
||||
if (context.settings().passwordsEncryptionKeyGenerated) {
|
||||
|
@ -248,6 +224,7 @@ class Core(private val context: Context) {
|
|||
context.settings().recordPasswordsEncryptionKeyGenerated()
|
||||
getSecureAbove22Preferences().putString(PASSWORDS_KEY, it)
|
||||
}
|
||||
}
|
||||
|
||||
val trackingProtectionPolicyFactory = TrackingProtectionPolicyFactory(context.settings())
|
||||
|
||||
|
|
|
@ -94,9 +94,7 @@ class SavedLoginSiteInfoFragment : Fragment(R.layout.fragment_saved_login_site_i
|
|||
var deleteLoginJob: Deferred<Boolean>? = null
|
||||
val deleteJob = lifecycleScope.launch(IO) {
|
||||
deleteLoginJob = async {
|
||||
requireContext().components.core.syncablePasswordsStorage.withUnlocked {
|
||||
it.delete(args.savedLoginItem.id).await()
|
||||
}
|
||||
requireContext().components.core.passwordsStorage.delete(args.savedLoginItem.id)
|
||||
}
|
||||
deleteLoginJob?.await()
|
||||
withContext(Main) {
|
||||
|
|
|
@ -22,7 +22,7 @@ import kotlinx.coroutines.ObsoleteCoroutinesApi
|
|||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.appservices.logins.ServerPassword
|
||||
import mozilla.components.concept.storage.Login
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
|
@ -103,18 +103,16 @@ class SavedLoginsFragment : Fragment() {
|
|||
}
|
||||
|
||||
private fun loadAndMapLogins() {
|
||||
var deferredLogins: Deferred<List<ServerPassword>>? = null
|
||||
var deferredLogins: Deferred<List<Login>>? = null
|
||||
val fetchLoginsJob = lifecycleScope.launch(IO) {
|
||||
deferredLogins = async {
|
||||
requireContext().components.core.syncablePasswordsStorage.withUnlocked {
|
||||
it.list().await()
|
||||
}
|
||||
requireContext().components.core.passwordsStorage.list()
|
||||
}
|
||||
val logins = deferredLogins?.await()
|
||||
logins?.let {
|
||||
withContext(Main) {
|
||||
savedLoginsStore.dispatch(SavedLoginsFragmentAction.UpdateLogins(logins.map { item ->
|
||||
SavedLoginsItem(item.hostname, item.username, item.password, item.id)
|
||||
SavedLoginsItem(item.origin, item.username, item.password, item.guid!!)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ import org.mozilla.fenix.components.metrics.MetricController
|
|||
class BackgroundServicesTest {
|
||||
class TestableBackgroundServices(
|
||||
val context: Context
|
||||
) : BackgroundServices(context, mockk(), mockk(), mockk(), mockk(), mockk()) {
|
||||
) : BackgroundServices(context, mockk(), mockk(), mockk(), mockk()) {
|
||||
override fun makeAccountManager(
|
||||
context: Context,
|
||||
serverConfig: ServerConfig,
|
||||
|
|
|
@ -11,7 +11,6 @@ import mozilla.components.browser.session.SessionManager
|
|||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.fetch.Client
|
||||
import mozilla.components.feature.pwa.WebAppShortcutManager
|
||||
import mozilla.components.lib.dataprotect.SecureAbove22Preferences
|
||||
|
||||
class TestCore(context: Context) : Core(context) {
|
||||
|
||||
|
@ -20,5 +19,4 @@ class TestCore(context: Context) : Core(context) {
|
|||
override val store = mockk<BrowserStore>()
|
||||
override val client = mockk<Client>()
|
||||
override val webAppShortcutManager = mockk<WebAppShortcutManager>()
|
||||
override fun getSecureAbove22Preferences() = mockk<SecureAbove22Preferences>(relaxed = true)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue