Merge pull request #1902 from vector-im/feature/verify_one_session
Feature/verify one session
This commit is contained in:
commit
e9b3ab91a0
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
|
@ -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())
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in New Issue