Logs the user out when username not found for user id
If we detect an inconsistent state in the app, in which a username for the primary id is not present in the shared prefs, log the user out and log the error. The correct username for that user id will be put there on the next log in. Additionally, removes the code setting up alarm and fetching events from the navigation activity when a new id arrives- it's no longer needed there and was a leftover from a merge conflict from a previous MR. MAILAND-2644
This commit is contained in:
parent
60544120f1
commit
497ccd0709
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
package ch.protonmail.android.activities.dialogs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
|
@ -44,7 +43,7 @@ import ch.protonmail.android.settings.presentation.SnoozeNotificationsActivity;
|
|||
import ch.protonmail.android.views.CustomQuickSnoozeDialog;
|
||||
import dagger.hilt.android.AndroidEntryPoint;
|
||||
|
||||
import static ch.protonmail.android.activities.NavigationActivityKt.REQUEST_CODE_SNOOZED_NOTIFICATIONS;
|
||||
import static ch.protonmail.android.navigation.presentation.NavigationActivityKt.REQUEST_CODE_SNOOZED_NOTIFICATIONS;
|
||||
|
||||
@AndroidEntryPoint
|
||||
public class QuickSnoozeDialogFragment
|
||||
|
|
|
@ -44,9 +44,9 @@ open class DatabaseFactory<T : RoomDatabase>(
|
|||
|
||||
@Synchronized
|
||||
fun deleteDatabase(context: Context, userId: UserId) {
|
||||
val username = CounterDatabase.usernameForUserId(context, userId)
|
||||
val username = usernameForUserId(context, userId)
|
||||
|
||||
val baseFileName = CounterDatabase.databaseName(username)
|
||||
val baseFileName = databaseName(username)
|
||||
val databaseFile = context.getDatabasePath(baseFileName)
|
||||
val databaseFileShm = context.getDatabasePath("$baseFileName-shm")
|
||||
val databaseFileWal = context.getDatabasePath("$baseFileName-wal")
|
||||
|
@ -72,12 +72,12 @@ open class DatabaseFactory<T : RoomDatabase>(
|
|||
.build()
|
||||
}
|
||||
|
||||
protected fun usernameForUserId(context: Context, userId: UserId): String {
|
||||
private fun usernameForUserId(context: Context, userId: UserId): String {
|
||||
val prefs = SecureSharedPreferences.getPrefsForUser(context, userId)
|
||||
return checkNotNull(prefs.getString(Constants.Prefs.PREF_USER_NAME, null))
|
||||
}
|
||||
|
||||
protected fun databaseName(username: String) =
|
||||
private fun databaseName(username: String) =
|
||||
"${Base64.encodeToString(username.toByteArray(), Base64.NO_WRAP)}-$baseDatabaseName"
|
||||
|
||||
@VisibleForTesting
|
||||
|
|
|
@ -57,11 +57,11 @@ import androidx.recyclerview.widget.RecyclerView
|
|||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.activities.EXTRA_FIRST_LOGIN
|
||||
import ch.protonmail.android.navigation.presentation.EXTRA_FIRST_LOGIN
|
||||
import ch.protonmail.android.activities.EXTRA_SETTINGS_ITEM_TYPE
|
||||
import ch.protonmail.android.activities.EditSettingsItemActivity
|
||||
import ch.protonmail.android.activities.EngagementActivity
|
||||
import ch.protonmail.android.activities.NavigationActivity
|
||||
import ch.protonmail.android.navigation.presentation.NavigationActivity
|
||||
import ch.protonmail.android.activities.SettingsItem
|
||||
import ch.protonmail.android.activities.StartCompose
|
||||
import ch.protonmail.android.activities.StartMessageDetails
|
||||
|
@ -86,8 +86,6 @@ import ch.protonmail.android.core.Constants.Prefs.PREF_DONT_SHOW_PLAY_SERVICES
|
|||
import ch.protonmail.android.core.Constants.Prefs.PREF_SWIPE_GESTURES_DIALOG_SHOWN
|
||||
import ch.protonmail.android.core.Constants.Prefs.PREF_USED_SPACE
|
||||
import ch.protonmail.android.core.Constants.SWIPE_GESTURES_CHANGED_VERSION
|
||||
import ch.protonmail.android.data.local.CounterDao
|
||||
import ch.protonmail.android.data.local.CounterDatabase
|
||||
import ch.protonmail.android.data.local.PendingActionDao
|
||||
import ch.protonmail.android.data.local.PendingActionDatabase
|
||||
import ch.protonmail.android.data.local.model.Message
|
||||
|
@ -130,7 +128,6 @@ import com.squareup.otto.Subscribe
|
|||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.android.synthetic.main.activity_mailbox.*
|
||||
import kotlinx.android.synthetic.main.activity_mailbox.screenShotPreventerView
|
||||
import kotlinx.android.synthetic.main.activity_message_details.*
|
||||
import kotlinx.android.synthetic.main.navigation_drawer.*
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
@ -167,7 +164,6 @@ internal class MailboxActivity :
|
|||
ActionMode.Callback,
|
||||
OnRefreshListener {
|
||||
|
||||
private lateinit var counterDao: CounterDao
|
||||
private lateinit var pendingActionDao: PendingActionDao
|
||||
|
||||
@Inject
|
||||
|
@ -521,10 +517,7 @@ internal class MailboxActivity :
|
|||
private var firstLogin: Boolean? = null
|
||||
|
||||
override fun onPrimaryUserId(userId: UserId) {
|
||||
super.onPrimaryUserId(userId)
|
||||
|
||||
mJobManager.start()
|
||||
counterDao = CounterDatabase.getInstance(this, userId).getDao()
|
||||
pendingActionDao = PendingActionDatabase.getInstance(this, userId).getDao()
|
||||
|
||||
setUpDrawer()
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
package ch.protonmail.android.activities
|
||||
package ch.protonmail.android.navigation.presentation
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
|
||||
|
@ -35,10 +35,13 @@ import androidx.lifecycle.flowWithLifecycle
|
|||
import androidx.lifecycle.lifecycleScope
|
||||
import ch.protonmail.android.BuildConfig
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.activities.BaseActivity
|
||||
import ch.protonmail.android.activities.StartContacts
|
||||
import ch.protonmail.android.activities.StartReportBugs
|
||||
import ch.protonmail.android.activities.StartSettings
|
||||
import ch.protonmail.android.api.AccountManager
|
||||
import ch.protonmail.android.api.models.DatabaseProvider
|
||||
import ch.protonmail.android.api.segments.event.AlarmReceiver
|
||||
import ch.protonmail.android.api.segments.event.FetchUpdatesJob
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.core.UserManager
|
||||
import ch.protonmail.android.drawer.presentation.mapper.DrawerLabelItemUiModelMapper
|
||||
|
@ -61,6 +64,7 @@ import ch.protonmail.android.utils.extensions.setDrawBehindSystemBars
|
|||
import ch.protonmail.android.utils.ui.dialogs.DialogUtils.Companion.showTwoButtonInfoDialog
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
@ -115,6 +119,7 @@ internal abstract class NavigationActivity : BaseActivity() {
|
|||
lateinit var drawerLabelMapper: DrawerLabelItemUiModelMapper
|
||||
|
||||
private val accountSwitcherViewModel by viewModels<AccountSwitcherViewModel>()
|
||||
private val navigationViewModel by viewModels<NavigationViewModel>()
|
||||
|
||||
private val startSettingsLauncher = registerForActivityResult(StartSettings()) {}
|
||||
private val startContactsLauncher = registerForActivityResult(StartContacts()) {}
|
||||
|
@ -143,12 +148,7 @@ internal abstract class NavigationActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
protected open fun onPrimaryUserId(userId: UserId) {
|
||||
app.startJobManager()
|
||||
mJobManager.addJobInBackground(FetchUpdatesJob())
|
||||
val alarmReceiver = AlarmReceiver()
|
||||
alarmReceiver.setAlarm(this)
|
||||
}
|
||||
protected abstract fun onPrimaryUserId(userId: UserId)
|
||||
|
||||
protected abstract fun onInbox(type: Constants.DrawerOptionType)
|
||||
|
||||
|
@ -200,6 +200,7 @@ internal abstract class NavigationActivity : BaseActivity() {
|
|||
|
||||
getPrimaryUserId().filterNotNull()
|
||||
.flowWithLifecycle(lifecycle, Lifecycle.State.CREATED)
|
||||
.filter { userId -> navigationViewModel.verifyPrimaryUserId(userId) }
|
||||
.onEach { userId -> onPrimaryUserId(userId) }
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonMail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.navigation.presentation
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.feature.account.AccountStateManager
|
||||
import ch.protonmail.android.prefs.SecureSharedPreferences
|
||||
import ch.protonmail.android.utils.notifier.UserNotifier
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.util.kotlin.DispatcherProvider
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
internal class NavigationViewModel @Inject constructor(
|
||||
private val secureSharedPreferencesFactory: SecureSharedPreferences.Factory,
|
||||
private val accountStateManager: AccountStateManager,
|
||||
private val userNotifier: UserNotifier,
|
||||
private val dispatchers: DispatcherProvider
|
||||
) : ViewModel() {
|
||||
|
||||
suspend fun verifyPrimaryUserId(userId: UserId): Boolean = withContext(dispatchers.Io) {
|
||||
val prefs = secureSharedPreferencesFactory.userPreferences(userId)
|
||||
return@withContext if (prefs.getString(Constants.Prefs.PREF_USER_NAME, null) == null) {
|
||||
Timber.e("Did not find username for the current primary user id. Logging the user out.")
|
||||
accountStateManager.signOut(userId)
|
||||
userNotifier.showError(R.string.logged_out_description)
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ package ch.protonmail.android.utils
|
|||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import ch.protonmail.android.activities.EXTRA_FIRST_LOGIN
|
||||
import ch.protonmail.android.navigation.presentation.EXTRA_FIRST_LOGIN
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.core.ProtonMailApplication
|
||||
import ch.protonmail.android.mailbox.presentation.MailboxActivity
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Proton Technologies AG
|
||||
*
|
||||
* This file is part of ProtonMail.
|
||||
*
|
||||
* ProtonMail is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonMail is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
|
||||
package ch.protonmail.android.navigation.presentation
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import ch.protonmail.android.R
|
||||
import ch.protonmail.android.core.Constants
|
||||
import ch.protonmail.android.feature.account.AccountStateManager
|
||||
import ch.protonmail.android.prefs.SecureSharedPreferences
|
||||
import ch.protonmail.android.testdata.UserIdTestData
|
||||
import ch.protonmail.android.utils.notifier.UserNotifier
|
||||
import io.mockk.called
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockk
|
||||
import io.mockk.runs
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import me.proton.core.test.android.ArchTest
|
||||
import me.proton.core.test.kotlin.CoroutinesTest
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class NavigationViewModelTest : ArchTest, CoroutinesTest {
|
||||
|
||||
private val sharedPrefsMock = mockk<SharedPreferences>()
|
||||
private val sharedPreferencesFactoryMock = mockk<SecureSharedPreferences.Factory> {
|
||||
every { userPreferences(UserIdTestData.userId) } returns sharedPrefsMock
|
||||
}
|
||||
private val accountStateManagerMock = mockk<AccountStateManager> {
|
||||
every { signOut(UserIdTestData.userId) } returns mockk()
|
||||
}
|
||||
private val userNotifierMock = mockk<UserNotifier> {
|
||||
every { showError(R.string.logged_out_description) } just runs
|
||||
}
|
||||
private val navigationViewModel = NavigationViewModel(
|
||||
sharedPreferencesFactoryMock,
|
||||
accountStateManagerMock,
|
||||
userNotifierMock,
|
||||
dispatchers
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `should not log out and return true if user id verified correctly`() = runBlockingTest {
|
||||
// given
|
||||
every { sharedPrefsMock.getString(Constants.Prefs.PREF_USER_NAME, null) } returns USERNAME
|
||||
|
||||
// when
|
||||
val userIdVerified = navigationViewModel.verifyPrimaryUserId(UserIdTestData.userId)
|
||||
|
||||
// then
|
||||
assertTrue(userIdVerified)
|
||||
verify { accountStateManagerMock wasNot called }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should log out, show error, and return false if user id not verified correctly`() = runBlockingTest {
|
||||
// given
|
||||
every { sharedPrefsMock.getString(Constants.Prefs.PREF_USER_NAME, null) } returns null
|
||||
|
||||
// when
|
||||
val userIdVerified = navigationViewModel.verifyPrimaryUserId(UserIdTestData.userId)
|
||||
|
||||
// then
|
||||
assertFalse(userIdVerified)
|
||||
verify { accountStateManagerMock.signOut(UserIdTestData.userId) }
|
||||
verify { userNotifierMock.showError(R.string.logged_out_description) }
|
||||
}
|
||||
|
||||
private companion object TestData {
|
||||
const val USERNAME = "username"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue