Remove developer mode option to set up cross-signing

This commit is contained in:
Benoit Marty 2020-06-16 16:09:27 +02:00 committed by Valere
parent bddd7f4005
commit 369f40c804
7 changed files with 202 additions and 270 deletions

View File

@ -1,163 +0,0 @@
/*
* Copyright 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.riotx.features.settings.crosssigning
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.loadingItem
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.ui.list.genericItem
import im.vector.riotx.core.ui.list.genericItemWithValue
import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.riotx.features.settings.VectorPreferences
import me.gujun.android.span.span
import javax.inject.Inject
class CrossSigningEpoxyController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
private val dimensionConverter: DimensionConverter,
private val vectorPreferences: VectorPreferences
) : TypedEpoxyController<CrossSigningSettingsViewState>() {
interface InteractionListener {
fun onInitializeCrossSigningKeys()
fun verifySession()
}
var interactionListener: InteractionListener? = null
override fun buildModels(data: CrossSigningSettingsViewState?) {
if (data == null) return
if (data.xSigningKeyCanSign) {
genericItem {
id("can")
titleIconResourceId(R.drawable.ic_shield_trusted)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
}
} else if (data.xSigningKeysAreTrusted) {
genericItem {
id("trusted")
titleIconResourceId(R.drawable.ic_shield_custom)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
}
if (!data.isUploadingKeys) {
bottomSheetVerificationActionItem {
id("verify")
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
listener {
interactionListener?.verifySession()
}
}
}
} 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("verify")
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
listener {
interactionListener?.verifySession()
}
}
} else {
genericItem {
id("not")
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled))
}
if (vectorPreferences.developerMode() && !data.isUploadingKeys) {
bottomSheetVerificationActionItem {
id("initKeys")
title(stringProvider.getString(R.string.initialize_cross_signing))
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) {
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)
}
}
)
}
}
crossSigningKeys?.userKey()?.let {
genericItemWithValue {
id("usk")
titleIconResourceId(R.drawable.key_small)
title(
span {
+"User Key:\n"
span {
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)
}
}
)
}
}
}
}
}

View File

@ -0,0 +1,23 @@
/*
* 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.riotx.features.settings.crosssigning
import im.vector.riotx.core.platform.VectorViewModelAction
sealed class CrossSigningSettingsAction : VectorViewModelAction {
object VerifySession : CrossSigningSettingsAction()
}

View File

@ -0,0 +1,139 @@
/*
* Copyright 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.riotx.features.settings.crosssigning
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.riotx.R
import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.ui.list.genericItem
import im.vector.riotx.core.ui.list.genericItemWithValue
import im.vector.riotx.core.utils.DimensionConverter
import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import me.gujun.android.span.span
import javax.inject.Inject
class CrossSigningSettingsController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
private val dimensionConverter: DimensionConverter
) : TypedEpoxyController<CrossSigningSettingsViewState>() {
interface InteractionListener {
fun verifySession()
}
var interactionListener: InteractionListener? = null
override fun buildModels(data: CrossSigningSettingsViewState?) {
if (data == null) return
if (data.xSigningKeyCanSign) {
genericItem {
id("can")
titleIconResourceId(R.drawable.ic_shield_trusted)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_complete))
}
} else if (data.xSigningKeysAreTrusted) {
genericItem {
id("trusted")
titleIconResourceId(R.drawable.ic_shield_custom)
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_trusted))
}
bottomSheetVerificationActionItem {
id("verify")
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
listener {
interactionListener?.verifySession()
}
}
} 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("verify")
title(stringProvider.getString(R.string.crosssigning_verify_this_session))
titleColor(colorProvider.getColor(R.color.riotx_positive_accent))
iconRes(R.drawable.ic_arrow_right)
iconColor(colorProvider.getColor(R.color.riotx_positive_accent))
listener {
interactionListener?.verifySession()
}
}
} else {
genericItem {
id("not")
title(stringProvider.getString(R.string.encryption_information_dg_xsigning_disabled))
}
}
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)
}
}
)
}
}
crossSigningKeys?.userKey()?.let {
genericItemWithValue {
id("usk")
titleIconResourceId(R.drawable.key_small)
title(
span {
+"User Key:\n"
span {
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)
}
}
)
}
}
}
}

View File

@ -21,7 +21,6 @@ import androidx.appcompat.app.AlertDialog
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
import im.vector.riotx.core.dialogs.PromptPasswordDialog
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.extensions.exhaustive
@ -31,9 +30,9 @@ import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import javax.inject.Inject
class CrossSigningSettingsFragment @Inject constructor(
private val epoxyController: CrossSigningEpoxyController,
private val controller: CrossSigningSettingsController,
val viewModelFactory: CrossSigningSettingsViewModel.Factory
) : VectorBaseFragment(), CrossSigningEpoxyController.InteractionListener {
) : VectorBaseFragment(), CrossSigningSettingsController.InteractionListener {
override fun getLayoutResId() = R.layout.fragment_generic_recycler
@ -43,7 +42,7 @@ class CrossSigningSettingsFragment @Inject constructor(
super.onActivityCreated(savedInstanceState)
viewModel.observeViewEvents {
when (it) {
is CrossSigningSettingsViewEvents.Failure -> {
is CrossSigningSettingsViewEvents.Failure -> {
AlertDialog.Builder(requireContext())
.setTitle(R.string.dialog_title_error)
.setMessage(errorFormatter.toHumanReadable(it.throwable))
@ -51,13 +50,8 @@ class CrossSigningSettingsFragment @Inject constructor(
.show()
Unit
}
is CrossSigningSettingsViewEvents.RequestPassword -> {
requestPassword()
}
CrossSigningSettingsViewEvents.VerifySession -> {
(requireActivity() as? VectorBaseActivity)?.let { activity ->
activity.navigator.waitSessionVerification(activity)
}
CrossSigningSettingsViewEvents.VerifySession -> {
navigator.waitSessionVerification(requireActivity())
}
}.exhaustive
}
@ -74,31 +68,21 @@ class CrossSigningSettingsFragment @Inject constructor(
}
override fun invalidate() = withState(viewModel) { state ->
epoxyController.setData(state)
controller.setData(state)
}
private fun setupRecyclerView() {
recyclerView.configureWith(epoxyController, hasFixedSize = false, disableItemAnimation = true)
epoxyController.interactionListener = this
recyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.interactionListener = this
}
override fun onDestroyView() {
recyclerView.cleanup()
epoxyController.interactionListener = null
controller.interactionListener = null
super.onDestroyView()
}
private fun requestPassword() {
PromptPasswordDialog().show(requireActivity()) { password ->
viewModel.handle(CrossSigningAction.PasswordEntered(password))
}
}
override fun onInitializeCrossSigningKeys() {
viewModel.handle(CrossSigningAction.InitializeCrossSigning)
}
override fun verifySession() {
viewModel.handle(CrossSigningAction.VerifySession)
viewModel.handle(CrossSigningSettingsAction.VerifySession)
}
}

View File

@ -24,6 +24,5 @@ import im.vector.riotx.core.platform.VectorViewEvents
sealed class CrossSigningSettingsViewEvents : VectorViewEvents {
data class Failure(val throwable: Throwable) : CrossSigningSettingsViewEvents()
object RequestPassword : CrossSigningSettingsViewEvents()
object VerifySession : CrossSigningSettingsViewEvents()
}

View File

@ -16,40 +16,19 @@
package im.vector.riotx.features.settings.crosssigning
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.LoginFlowTypes
import im.vector.matrix.android.api.failure.toRegistrationFlowResponse
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
import im.vector.matrix.android.internal.crypto.crosssigning.isVerified
import im.vector.matrix.android.internal.crypto.model.rest.UserPasswordAuth
import im.vector.matrix.rx.rx
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.platform.VectorViewModel
import im.vector.riotx.core.platform.VectorViewModelAction
data class CrossSigningSettingsViewState(
val crossSigningInfo: MXCrossSigningInfo? = null,
val xSigningIsEnableInAccount: Boolean = false,
val xSigningKeysAreTrusted: Boolean = false,
val xSigningKeyCanSign: Boolean = true,
val isUploadingKeys: Boolean = false
) : MvRxState
sealed class CrossSigningAction : VectorViewModelAction {
object InitializeCrossSigning : CrossSigningAction()
object VerifySession : CrossSigningAction()
data class PasswordEntered(val password: String) : CrossSigningAction()
}
class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted private val initialState: CrossSigningSettingsViewState,
private val session: Session)
: VectorViewModel<CrossSigningSettingsViewState, CrossSigningAction, CrossSigningSettingsViewEvents>(initialState) {
: VectorViewModel<CrossSigningSettingsViewState, CrossSigningSettingsAction, CrossSigningSettingsViewEvents>(initialState) {
init {
session.rx().liveCrossSigningInfo(session.myUserId)
@ -67,75 +46,19 @@ class CrossSigningSettingsViewModel @AssistedInject constructor(@Assisted privat
}
}
// Storage when password is required
private var _pendingSession: String? = null
@AssistedInject.Factory
interface Factory {
fun create(initialState: CrossSigningSettingsViewState): CrossSigningSettingsViewModel
}
override fun handle(action: CrossSigningAction) {
override fun handle(action: CrossSigningSettingsAction) {
when (action) {
is CrossSigningAction.InitializeCrossSigning -> {
initializeCrossSigning(null)
}
is CrossSigningAction.PasswordEntered -> {
initializeCrossSigning(UserPasswordAuth(
session = _pendingSession,
user = session.myUserId,
password = action.password
))
}
CrossSigningAction.VerifySession -> {
CrossSigningSettingsAction.VerifySession -> {
_viewEvents.post(CrossSigningSettingsViewEvents.VerifySession)
}
}.exhaustive
}
private fun initializeCrossSigning(auth: UserPasswordAuth?) {
_pendingSession = null
setState {
copy(isUploadingKeys = true)
}
session.cryptoService().crossSigningService().initializeCrossSigning(auth, object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) {
_pendingSession = null
setState {
copy(isUploadingKeys = false)
}
}
override fun onFailure(failure: Throwable) {
_pendingSession = null
val registrationFlowResponse = failure.toRegistrationFlowResponse()
if (registrationFlowResponse != null) {
// Retry with authentication
if (registrationFlowResponse.flows?.any { it.stages?.contains(LoginFlowTypes.PASSWORD) == true } == true) {
_pendingSession = registrationFlowResponse.session ?: ""
_viewEvents.post(CrossSigningSettingsViewEvents.RequestPassword)
} else {
// can't do this from here
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(Throwable("You cannot do that from mobile")))
setState {
copy(isUploadingKeys = false)
}
}
} else {
_viewEvents.post(CrossSigningSettingsViewEvents.Failure(failure))
setState {
copy(isUploadingKeys = false)
}
}
}
})
}
companion object : MvRxViewModelFactory<CrossSigningSettingsViewModel, CrossSigningSettingsViewState> {
@JvmStatic

View File

@ -0,0 +1,27 @@
/*
* 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.riotx.features.settings.crosssigning
import com.airbnb.mvrx.MvRxState
import im.vector.matrix.android.api.session.crypto.crosssigning.MXCrossSigningInfo
data class CrossSigningSettingsViewState(
val crossSigningInfo: MXCrossSigningInfo? = null,
val xSigningIsEnableInAccount: Boolean = false,
val xSigningKeysAreTrusted: Boolean = false,
val xSigningKeyCanSign: Boolean = true
) : MvRxState