Merge pull request #1902 from vector-im/feature/verify_one_session

Feature/verify one session
This commit is contained in:
Benoit Marty 2020-09-03 11:11:22 +02:00 committed by GitHub
commit e9b3ab91a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 34 deletions

View file

@ -7,6 +7,7 @@ Features ✨:
Improvements 🙌: Improvements 🙌:
- You can now join room through permalink and within room directory search - You can now join room through permalink and within room directory search
- Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774) - Add long click gesture to copy userId, user display name, room name, room topic and room alias (#1774)
- Do not propose to verify session if there is only one session and 4S is not configured (#1901)
Bugfix 🐛: Bugfix 🐛:
- Display name not shown under Settings/General (#1926) - Display name not shown under Settings/General (#1926)

View file

@ -39,7 +39,7 @@ data class DeviceVerificationInfoArgs(
val deviceId: String val deviceId: String
) : Parcelable ) : Parcelable
class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), DeviceVerificationInfoEpoxyController.Callback { class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(), DeviceVerificationInfoBottomSheetController.Callback {
private val viewModel: DeviceVerificationInfoBottomSheetViewModel by fragmentViewModel(DeviceVerificationInfoBottomSheetViewModel::class) private val viewModel: DeviceVerificationInfoBottomSheetViewModel by fragmentViewModel(DeviceVerificationInfoBottomSheetViewModel::class)
@ -54,17 +54,17 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(),
injector.inject(this) injector.inject(this)
} }
@Inject lateinit var epoxyController: DeviceVerificationInfoEpoxyController @Inject lateinit var controller: DeviceVerificationInfoBottomSheetController
override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title override fun getLayoutResId() = R.layout.bottom_sheet_generic_list_with_title
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
recyclerView.configureWith( recyclerView.configureWith(
epoxyController, controller,
showDivider = false, showDivider = false,
hasFixedSize = false) hasFixedSize = false)
epoxyController.callback = this controller.callback = this
bottomSheetTitle.isVisible = false bottomSheetTitle.isVisible = false
} }
@ -74,7 +74,7 @@ class DeviceVerificationInfoBottomSheet : VectorBaseBottomSheetDialogFragment(),
} }
override fun invalidate() = withState(viewModel) { override fun invalidate() = withState(viewModel) {
epoxyController.setData(it) controller.setData(it)
super.invalidate() super.invalidate()
} }

View file

@ -16,9 +16,6 @@
package im.vector.app.features.settings.devices package im.vector.app.features.settings.devices
import com.airbnb.epoxy.TypedEpoxyController import com.airbnb.epoxy.TypedEpoxyController
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.epoxy.dividerItem import im.vector.app.core.epoxy.dividerItem
import im.vector.app.core.epoxy.loadingItem import im.vector.app.core.epoxy.loadingItem
@ -28,12 +25,14 @@ import im.vector.app.core.ui.list.GenericItem
import im.vector.app.core.ui.list.genericFooterItem import im.vector.app.core.ui.list.genericFooterItem
import im.vector.app.core.ui.list.genericItem import im.vector.app.core.ui.list.genericItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
class DeviceVerificationInfoEpoxyController @Inject constructor(private val stringProvider: StringProvider, class DeviceVerificationInfoBottomSheetController @Inject constructor(
private val colorProvider: ColorProvider, private val stringProvider: StringProvider,
private val session: Session) private val colorProvider: ColorProvider)
: TypedEpoxyController<DeviceVerificationInfoBottomSheetViewState>() { : TypedEpoxyController<DeviceVerificationInfoBottomSheetViewState>() {
var callback: Callback? = null var callback: Callback? = null
@ -67,16 +66,18 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri
if (data.hasAccountCrossSigning) { if (data.hasAccountCrossSigning) {
// Cross Signing is enabled // Cross Signing is enabled
handleE2EWithCrossSigning(data.isMine, data.accountCrossSigningIsTrusted, cryptoDeviceInfo, shield) handleE2EWithCrossSigning(data, cryptoDeviceInfo, shield)
} else { } else {
handleE2EInLegacy(data.isMine, cryptoDeviceInfo, shield) handleE2EInLegacy(data, cryptoDeviceInfo, shield)
} }
// COMMON ACTIONS (Rename / signout) // COMMON ACTIONS (Rename / signout)
addGenericDeviceManageActions(data, cryptoDeviceInfo.deviceId) addGenericDeviceManageActions(data, cryptoDeviceInfo.deviceId)
} }
private fun handleE2EWithCrossSigning(isMine: Boolean, currentSessionIsTrusted: Boolean, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { private fun handleE2EWithCrossSigning(data: DeviceVerificationInfoBottomSheetViewState, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) {
val isMine = data.isMine
val currentSessionIsTrusted = data.accountCrossSigningIsTrusted
Timber.v("handleE2EWithCrossSigning $isMine, $cryptoDeviceInfo, $shield") Timber.v("handleE2EWithCrossSigning $isMine, $cryptoDeviceInfo, $shield")
if (isMine) { if (isMine) {
@ -88,14 +89,18 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri
title(stringProvider.getString(R.string.encryption_information_verified)) title(stringProvider.getString(R.string.encryption_information_verified))
description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc)) description(stringProvider.getString(R.string.settings_active_sessions_verified_device_desc))
} }
} else { } else if (data.canVerifySession) {
// You need to complete security // You need to complete security, only if there are other session(s) available, or if 4S contains secrets
genericItem { genericItem {
id("trust${cryptoDeviceInfo.deviceId}") id("trust${cryptoDeviceInfo.deviceId}")
style(GenericItem.STYLE.BIG_TEXT) style(GenericItem.STYLE.BIG_TEXT)
titleIconResourceId(shield) titleIconResourceId(shield)
title(stringProvider.getString(R.string.crosssigning_verify_this_session)) title(stringProvider.getString(R.string.crosssigning_verify_this_session))
if (data.hasOtherSessions) {
description(stringProvider.getString(R.string.confirm_your_identity)) description(stringProvider.getString(R.string.confirm_your_identity))
} else {
description(stringProvider.getString(R.string.confirm_your_identity_quad_s))
}
} }
} }
} else { } else {
@ -132,7 +137,7 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri
description("(${cryptoDeviceInfo.deviceId})") description("(${cryptoDeviceInfo.deviceId})")
} }
if (isMine && !currentSessionIsTrusted) { if (isMine && !currentSessionIsTrusted && data.canVerifySession) {
// Add complete security // Add complete security
dividerItem { dividerItem {
id("completeSecurityDiv") id("completeSecurityDiv")
@ -158,8 +163,9 @@ class DeviceVerificationInfoEpoxyController @Inject constructor(private val stri
} }
} }
private fun handleE2EInLegacy(isMine: Boolean, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) { private fun handleE2EInLegacy(data: DeviceVerificationInfoBottomSheetViewState, cryptoDeviceInfo: CryptoDeviceInfo, shield: Int) {
// ==== Legacy // ==== Legacy
val isMine = data.isMine
// TRUST INFO SECTION // TRUST INFO SECTION
if (cryptoDeviceInfo.trustLevel?.isLocallyVerified() == true) { if (cryptoDeviceInfo.trustLevel?.isLocallyVerified() == true) {

View file

@ -15,31 +15,19 @@
*/ */
package im.vector.app.features.settings.devices package im.vector.app.features.settings.devices
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import im.vector.app.core.platform.EmptyAction import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.rx.rx import org.matrix.android.sdk.rx.rx
data class DeviceVerificationInfoBottomSheetViewState(
val cryptoDeviceInfo: Async<CryptoDeviceInfo?> = Uninitialized,
val deviceInfo: Async<DeviceInfo> = Uninitialized,
val hasAccountCrossSigning: Boolean = false,
val accountCrossSigningIsTrusted: Boolean = false,
val isMine: Boolean = false
) : MvRxState
class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState, class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: DeviceVerificationInfoBottomSheetViewState,
@Assisted val deviceId: String, @Assisted val deviceId: String,
val session: Session val session: Session
@ -55,7 +43,8 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As
setState { setState {
copy( copy(
hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(), hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized(),
accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified() accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified(),
isRecoverySetup = session.sharedSecretStorageService.isRecoverySetup()
) )
} }
session.rx().liveCrossSigningInfo(session.myUserId) session.rx().liveCrossSigningInfo(session.myUserId)
@ -77,6 +66,14 @@ class DeviceVerificationInfoBottomSheetViewModel @AssistedInject constructor(@As
) )
} }
session.rx().liveUserCryptoDevices(session.myUserId)
.map { it.size }
.execute {
copy(
hasOtherSessions = it.invoke() ?: 0 > 1
)
}
setState { setState {
copy(deviceInfo = Loading()) copy(deviceInfo = Loading())
} }

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2020 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.settings.devices
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.Uninitialized
import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
data class DeviceVerificationInfoBottomSheetViewState(
val cryptoDeviceInfo: Async<CryptoDeviceInfo?> = Uninitialized,
val deviceInfo: Async<DeviceInfo> = Uninitialized,
val hasAccountCrossSigning: Boolean = false,
val accountCrossSigningIsTrusted: Boolean = false,
val isMine: Boolean = false,
val hasOtherSessions: Boolean = false,
val isRecoverySetup: Boolean = false
) : MvRxState {
val canVerifySession: Boolean
get() = hasOtherSessions || isRecoverySetup
}

View file

@ -2413,6 +2413,7 @@
<string name="crosssigning_verify_session">Verify login</string> <string name="crosssigning_verify_session">Verify login</string>
<string name="cross_signing_verify_by_emoji">Interactively Verify by Emoji</string> <string name="cross_signing_verify_by_emoji">Interactively Verify by Emoji</string>
<string name="confirm_your_identity">Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.</string> <string name="confirm_your_identity">Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.</string>
<string name="confirm_your_identity_quad_s">Confirm your identity by verifying this login, granting it access to encrypted messages.</string>
<string name="mark_as_verified">Mark as Trusted</string> <string name="mark_as_verified">Mark as Trusted</string>
<string name="error_empty_field_choose_user_name">Please choose a username.</string> <string name="error_empty_field_choose_user_name">Please choose a username.</string>