1
0
Fork 0

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
Grisha Kruglov 2020-02-28 16:21:12 -08:00 committed by Sebastian Kaspari
parent 6a75d822ca
commit e6e2dd94c7
10 changed files with 27 additions and 69 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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(

View File

@ -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) }

View File

@ -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())

View File

@ -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) {

View File

@ -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!!)
}))
}
}

View File

@ -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,

View File

@ -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)
}