From 83e44ac96e2fc4907a05be91953822440c988298 Mon Sep 17 00:00:00 2001 From: Valere Date: Tue, 28 Jan 2020 11:02:12 +0100 Subject: [PATCH] Fix / cross signing info live data not always updated --- .../crypto/store/db/RealmCryptoStore.kt | 63 +++--- .../RoomMemberProfileViewModel.kt | 9 +- .../RoomMemberProfileViewState.kt | 2 +- .../devices/DeviceListBottomSheetViewModel.kt | 5 +- .../VectorSettingsSecurityPrivacyFragment.kt | 5 + .../CrossSigningEpoxyController.kt | 183 +++++++++--------- .../CrossSigningSettingsViewModel.kt | 25 ++- 7 files changed, 151 insertions(+), 141 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt index d7b801a343..c80035fe7e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/db/RealmCryptoStore.kt @@ -877,10 +877,10 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati } override fun setMyCrossSigningInfo(info: MXCrossSigningInfo?) { - doRealmQueryAndCopy(realmConfiguration) { - it.where().findFirst() - }?.userId?.let { - setCrossSigningInfo(it, info) + doRealmTransaction(realmConfiguration) { realm -> + realm.where().findFirst()?.userId?.let { userId -> + addOrUpdateCrossSigningInfo(realm, userId, info) + } } } @@ -998,31 +998,36 @@ internal class RealmCryptoStore(private val realmConfiguration: RealmConfigurati override fun setCrossSigningInfo(userId: String, info: MXCrossSigningInfo?) { doRealmTransaction(realmConfiguration) { realm -> - - var existing = CrossSigningInfoEntity.get(realm, userId) - if (info == null) { - // Delete known if needed - existing?.deleteFromRealm() - // TODO notify, we might need to untrust things? - } else { - // Just override existing, caller should check and untrust id needed - existing = CrossSigningInfoEntity.getOrCreate(realm, userId) - val xkeys = RealmList() - info.crossSigningKeys.forEach { info -> - xkeys.add( - realm.createObject(KeyInfoEntity::class.java).also { keyInfoEntity -> - keyInfoEntity.publicKeyBase64 = info.unpaddedBase64PublicKey - keyInfoEntity.usages = info.usages?.let { RealmList(*it.toTypedArray()) } - ?: RealmList() - keyInfoEntity.putSignatures(info.signatures) - // TODO how to handle better, check if same keys? - // reset trust - keyInfoEntity.trustLevelEntity = null - } - ) - } - existing.crossSigningKeys = xkeys - } + addOrUpdateCrossSigningInfo(realm, userId, info) } } + + private fun addOrUpdateCrossSigningInfo(realm: Realm, userId: String, info: MXCrossSigningInfo?): CrossSigningInfoEntity? { + var existing = CrossSigningInfoEntity.get(realm, userId) + if (info == null) { + // Delete known if needed + existing?.deleteFromRealm() + // TODO notify, we might need to untrust things? + } else { + // Just override existing, caller should check and untrust id needed + existing = CrossSigningInfoEntity.getOrCreate(realm, userId) + // existing.crossSigningKeys.forEach { it.deleteFromRealm() } + val xkeys = RealmList() + info.crossSigningKeys.forEach { cryptoCrossSigningKey -> + xkeys.add( + realm.createObject(KeyInfoEntity::class.java).also { keyInfoEntity -> + keyInfoEntity.publicKeyBase64 = cryptoCrossSigningKey.unpaddedBase64PublicKey + keyInfoEntity.usages = cryptoCrossSigningKey.usages?.let { RealmList(*it.toTypedArray()) } + ?: RealmList() + keyInfoEntity.putSignatures(cryptoCrossSigningKey.signatures) + // TODO how to handle better, check if same keys? + // reset trust + keyInfoEntity.trustLevelEntity = null + } + ) + } + existing.crossSigningKeys = xkeys + } + return existing + } } diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt index 419278b486..314a719ce9 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewModel.kt @@ -46,7 +46,6 @@ import im.vector.matrix.rx.mapOptional import im.vector.matrix.rx.rx import im.vector.matrix.rx.unwrap import im.vector.riotx.R -import im.vector.riotx.core.di.HasScreenInjector import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.DataSource @@ -75,7 +74,6 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v val fragment: RoomMemberProfileFragment = (viewModelContext as FragmentViewModelContext).fragment() return fragment.viewModelFactory.create(state) } - } private val _viewEvents = PublishDataSource() @@ -118,11 +116,8 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v } session.rx().liveCrossSigningInfo(initialState.userId) - .map { - it.getOrNull() - } .execute { - copy(userMXCrossSigningInfo = it) + copy(userMXCrossSigningInfo = it.invoke()?.getOrNull()) } } } @@ -150,7 +145,7 @@ class RoomMemberProfileViewModel @AssistedInject constructor(@Assisted private v private fun prepareVerification(action: RoomMemberProfileAction.VerifyUser) = withState { state -> // Sanity if (state.isRoomEncrypted) { - if (!state.isMine && state.userMXCrossSigningInfo.invoke()?.isTrusted() == false) { + if (!state.isMine && state.userMXCrossSigningInfo?.isTrusted() == false) { // ok, let's find or create the DM room _actionResultLiveData.postValue( LiveEvent(Success(action.copy(userId = state.userId))) diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt index 7a773d1228..2cf96441dc 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/RoomMemberProfileViewState.kt @@ -35,7 +35,7 @@ data class RoomMemberProfileViewState( val powerLevelsContent: Async = Uninitialized, val userPowerLevelString: Async = Uninitialized, val userMatrixItem: Async = Uninitialized, - val userMXCrossSigningInfo: Async = Uninitialized, + val userMXCrossSigningInfo: MXCrossSigningInfo? = null, val allDevicesAreTrusted: Async = Uninitialized ) : MvRxState { diff --git a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt index 6970298513..99eeae93da 100644 --- a/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/roommemberprofile/devices/DeviceListBottomSheetViewModel.kt @@ -74,11 +74,8 @@ class DeviceListBottomSheetViewModel @AssistedInject constructor(@Assisted priva } session.rx().liveCrossSigningInfo(userId) - .map { - it.getOrNull() - } .execute { - copy(memberCrossSigningKey = it.invoke()) + copy(memberCrossSigningKey = it.invoke()?.getOrNull()) } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt index 833e368b35..bc56351106 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/VectorSettingsSecurityPrivacyFragment.kt @@ -105,6 +105,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( super.onResume() // My device name may have been updated refreshMyDevice() + refreshXSigningStatus() mCryptographyCategory.isVisible = vectorPreferences.developerMode() } @@ -128,6 +129,10 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor( } } + refreshXSigningStatus() + } + + private fun refreshXSigningStatus() { if (vectorPreferences.developerMode()) { val crossSigningKeys = session.getCrossSigningService().getMyCrossSigningKeys() val xSigningIsEnableInAccount = crossSigningKeys != null diff --git a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningEpoxyController.kt b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningEpoxyController.kt index 60a4a6a114..87cb74ce80 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningEpoxyController.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningEpoxyController.kt @@ -16,8 +16,6 @@ package im.vector.riotx.features.settings.crosssigning import com.airbnb.epoxy.TypedEpoxyController -import com.airbnb.mvrx.Loading -import com.airbnb.mvrx.Success import im.vector.riotx.R import im.vector.riotx.core.epoxy.loadingItem import im.vector.riotx.core.resources.ColorProvider @@ -50,14 +48,16 @@ class CrossSigningEpoxyController @Inject constructor( titleIconResourceId(R.drawable.ic_shield_trusted) title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete)) } - bottomSheetVerificationActionItem { - id("resetkeys") - title("Reset keys") - titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - iconRes(R.drawable.ic_arrow_right) - iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - listener { - interactionListener?.onResetCrossSigningKeys() + if (!data.isUploadingKeys) { + bottomSheetVerificationActionItem { + id("resetkeys") + title("Reset keys") + titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + iconRes(R.drawable.ic_arrow_right) + iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + listener { + interactionListener?.onResetCrossSigningKeys() + } } } } else if (data.xSigningKeysAreTrusted) { @@ -66,30 +66,32 @@ class CrossSigningEpoxyController @Inject constructor( titleIconResourceId(R.drawable.ic_shield_warning) title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted)) } - bottomSheetVerificationActionItem { - id("resetkeys") - title("Reset keys") - titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - iconRes(R.drawable.ic_arrow_right) - iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - listener { - interactionListener?.onResetCrossSigningKeys() + if (!data.isUploadingKeys) { + bottomSheetVerificationActionItem { + id("resetkeys") + title("Reset keys") + titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + iconRes(R.drawable.ic_arrow_right) + iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + listener { + interactionListener?.onResetCrossSigningKeys() + } } - } - } else if (data.xSigningIsEnableInAccount) { - genericItem { - id("enable") - titleIconResourceId(R.drawable.ic_shield_black) - title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted)) - } - bottomSheetVerificationActionItem { - id("resetkeys") - title("Reset keys") - titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - iconRes(R.drawable.ic_arrow_right) - iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) - listener { - interactionListener?.onResetCrossSigningKeys() + } else if (data.xSigningIsEnableInAccount) { + genericItem { + id("enable") + titleIconResourceId(R.drawable.ic_shield_black) + title(stringProvider.getString(R.string.encryption_information_dg_xsigning_not_trusted)) + } + bottomSheetVerificationActionItem { + id("resetkeys") + title("Reset keys") + titleColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + iconRes(R.drawable.ic_arrow_right) + iconColor(colorProvider.getColor(R.color.riotx_destructive_accent)) + listener { + interactionListener?.onResetCrossSigningKeys() + } } } } else { @@ -97,75 +99,74 @@ class CrossSigningEpoxyController @Inject constructor( id("not") title(stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled)) } - - bottomSheetVerificationActionItem { - id("initKeys") - title("Initialize keys") - titleColor(colorProvider.getColor(R.color.riotx_positive_accent)) - iconRes(R.drawable.ic_arrow_right) - iconColor(colorProvider.getColor(R.color.riotx_positive_accent)) - listener { - interactionListener?.onInitializeCrossSigningKeys() + if (!data.isUploadingKeys) { + bottomSheetVerificationActionItem { + id("initKeys") + title("Initialize keys") + titleColor(colorProvider.getColor(R.color.riotx_positive_accent)) + iconRes(R.drawable.ic_arrow_right) + iconColor(colorProvider.getColor(R.color.riotx_positive_accent)) + listener { + interactionListener?.onInitializeCrossSigningKeys() + } } } } - when (data.crossSigningInfo) { - is Loading -> { - loadingItem { - id("loading") + if (data.isUploadingKeys) { + loadingItem { + id("loading") + } + } else { + + val crossSigningKeys = data.crossSigningInfo + + crossSigningKeys?.masterKey()?.let { + genericItemWithValue { + id("msk") + titleIconResourceId(R.drawable.key_small) + title( + span { + +"Master Key:\n" + span { + text = it.unpaddedBase64PublicKey ?: "" + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + textSize = dimensionConverter.spToPx(12) + } + } + ) } } - is Success -> { - val crossSigningKeys = data.crossSigningInfo.invoke() - - crossSigningKeys?.masterKey()?.let { - genericItemWithValue { - id("msk") - titleIconResourceId(R.drawable.key_small) - title( + crossSigningKeys?.userKey()?.let { + genericItemWithValue { + id("usk") + titleIconResourceId(R.drawable.key_small) + title( + span { + +"User Key:\n" span { - +"Master Key:\n" - span { - text = it.unpaddedBase64PublicKey ?: "" - textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) - textSize = dimensionConverter.spToPx(12) - } + text = it.unpaddedBase64PublicKey ?: "" + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + textSize = dimensionConverter.spToPx(12) } - ) - } + } + ) } - crossSigningKeys?.userKey()?.let { - genericItemWithValue { - id("usk") - titleIconResourceId(R.drawable.key_small) - title( + } + crossSigningKeys?.selfSigningKey()?.let { + genericItemWithValue { + id("ssk") + titleIconResourceId(R.drawable.key_small) + title( + span { + +"Self Signed Key:\n" span { - +"User Key:\n" - span { - text = it.unpaddedBase64PublicKey ?: "" - textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) - textSize = dimensionConverter.spToPx(12) - } + text = it.unpaddedBase64PublicKey ?: "" + textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) + textSize = dimensionConverter.spToPx(12) } - ) - } - } - crossSigningKeys?.selfSigningKey()?.let { - genericItemWithValue { - id("ssk") - titleIconResourceId(R.drawable.key_small) - title( - span { - +"Self Signed Key:\n" - span { - text = it.unpaddedBase64PublicKey ?: "" - textColor = colorProvider.getColorFromAttribute(R.attr.riotx_text_secondary) - textSize = dimensionConverter.spToPx(12) - } - } - ) - } + } + ) } } } diff --git a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt index 44da3ce5c9..44297b8010 100644 --- a/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/settings/crosssigning/CrossSigningSettingsViewModel.kt @@ -20,7 +20,6 @@ import androidx.lifecycle.MutableLiveData import com.airbnb.mvrx.Async import com.airbnb.mvrx.Fail import com.airbnb.mvrx.FragmentViewModelContext -import com.airbnb.mvrx.Loading import com.airbnb.mvrx.MvRxState import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.Success @@ -44,10 +43,11 @@ import im.vector.riotx.core.resources.StringProvider import im.vector.riotx.core.utils.LiveEvent data class CrossSigningSettingsViewState( - val crossSigningInfo: Async = Uninitialized, + val crossSigningInfo: MXCrossSigningInfo? = null, val xSigningIsEnableInAccount: Boolean = false, val xSigningKeysAreTrusted: Boolean = false, - val xSigningKeyCanSign: Boolean = true + val xSigningKeyCanSign: Boolean = true, + val isUploadingKeys: Boolean = false ) : MvRxState sealed class CrossSigningAction : VectorViewModelAction { @@ -67,16 +67,13 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat init { session.rx().liveCrossSigningInfo(session.myUserId) - .map { - it.getOrNull() - } .execute { - val crossSigningKeys = it.invoke() + val crossSigningKeys = it.invoke()?.getOrNull() val xSigningIsEnableInAccount = crossSigningKeys != null val xSigningKeysAreTrusted = session.getCrossSigningService().checkUserTrust(session.myUserId).isVerified() val xSigningKeyCanSign = session.getCrossSigningService().canCrossSign() copy( - crossSigningInfo = it, + crossSigningInfo = crossSigningKeys, xSigningIsEnableInAccount = xSigningIsEnableInAccount, xSigningKeysAreTrusted = xSigningKeysAreTrusted, xSigningKeyCanSign = xSigningKeyCanSign @@ -99,9 +96,16 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat private fun initializeCrossSigning(auth: UserPasswordAuth?) { setState { - copy(crossSigningInfo = Loading()) + copy(isUploadingKeys = true) } session.getCrossSigningService().initializeCrossSigning(auth, object : MatrixCallback { + + override fun onSuccess(data: Unit) { + setState { + copy(isUploadingKeys = false) + } + } + override fun onFailure(failure: Throwable) { if (failure is Failure.OtherServerError && failure.httpCode == 401 @@ -125,6 +129,9 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat } } _requestLiveData.postValue(LiveEvent(Fail(Throwable("You cannot do that from mobile")))) + setState { + copy(isUploadingKeys = false) + } } }) }