Key Req Dev tool initial commit

This commit is contained in:
Valere 2020-02-26 11:44:49 +01:00
parent 06fc5c2dd9
commit 757e90986e
13 changed files with 356 additions and 1 deletions

View file

@ -28,6 +28,7 @@ import im.vector.matrix.android.api.session.events.model.Content
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.crypto.NewSessionListener
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
@ -129,4 +130,6 @@ interface CryptoService {
fun addNewSessionListener(newSessionListener: NewSessionListener)
fun removeSessionListener(listener: NewSessionListener)
fun getOutgoingRoomKeyRequest(): List<OutgoingRoomKeyRequest>
}

View file

@ -115,6 +115,7 @@ internal class DefaultCryptoService @Inject constructor(
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
// the crypto store
private val cryptoStore: IMXCryptoStore,
// Olm device
private val olmDevice: MXOlmDevice,
// Set of parameters used to configure/customize the end-to-end crypto.
@ -1091,4 +1092,8 @@ internal class DefaultCryptoService @Inject constructor(
override fun toString(): String {
return "DefaultCryptoService of " + credentials.userId + " (" + credentials.deviceId + ")"
}
override fun getOutgoingRoomKeyRequest(): List<OutgoingRoomKeyRequest> {
return cryptoStore.getOutgoingRoomKeyRequests()
}
}

View file

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.util.Optional
import im.vector.matrix.android.internal.crypto.DeviceListManager
import im.vector.matrix.android.internal.crypto.MXOlmDevice
import im.vector.matrix.android.internal.crypto.MyDeviceInfoHolder
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequestManager
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
import im.vector.matrix.android.internal.crypto.model.KeyUsage
import im.vector.matrix.android.internal.crypto.model.rest.UploadSignatureQueryBuilder
@ -61,6 +62,7 @@ internal class DefaultCrossSigningService @Inject constructor(
private val taskExecutor: TaskExecutor,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope,
private val outgoingRoomKeyRequestManager: OutgoingRoomKeyRequestManager,
private val eventBus: EventBus) : CrossSigningService, DeviceListManager.UserDevicesUpdateListener {
private var olmUtility: OlmUtility? = null
@ -736,6 +738,7 @@ internal class DefaultCrossSigningService @Inject constructor(
// If it's me, recheck trust of all users and devices?
val users = ArrayList<String>()
if (otherUserId == userId && currentTrust != trusted) {
reRequestAllPendingRoomKeyRequest()
cryptoStore.updateUsersTrust {
users.add(it)
checkUserTrust(it).isVerified()
@ -751,4 +754,18 @@ internal class DefaultCrossSigningService @Inject constructor(
}
}
}
private fun reRequestAllPendingRoomKeyRequest() {
Timber.d("## CrossSigning - reRequest pending outgoing room key requests")
cryptoStore.getOutgoingRoomKeyRequests().forEach {
it.requestBody?.let {requestBody ->
if (cryptoStore.getInboundGroupSession(requestBody.sessionId ?: "", requestBody.senderKey ?: "") == null) {
outgoingRoomKeyRequestManager.resendRoomKeyRequest(requestBody)
} else {
outgoingRoomKeyRequestManager.cancelRoomKeyRequest(requestBody)
}
}
}
}
}

View file

@ -424,4 +424,8 @@ internal interface IMXCryptoStore {
fun clearOtherUserTrust()
fun updateUsersTrust(check: (String) -> Boolean)
// Dev tools
fun getOutgoingRoomKeyRequests() : List<OutgoingRoomKeyRequest>
}

View file

@ -1024,6 +1024,14 @@ internal class RealmCryptoStore @Inject constructor(
}
}
override fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest> {
return monarchy.fetchAllMappedSync( { realm ->
realm.where(OutgoingRoomKeyRequestEntity::class.java)
}, {
it.toOutgoingRoomKeyRequest()
})
}
override fun getCrossSigningInfo(userId: String): MXCrossSigningInfo? {
return doRealmQueryAndCopy(realmConfiguration) { realm ->
realm.where(CrossSigningInfoEntity::class.java)

View file

@ -74,6 +74,8 @@ import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
import im.vector.riotx.features.settings.crosssigning.CrossSigningSettingsFragment
import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
import im.vector.riotx.features.settings.devtools.AccountDataFragment
import im.vector.riotx.features.settings.devtools.KeyRequestListFragment
import im.vector.riotx.features.settings.devtools.KeyRequestsFragment
import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
import im.vector.riotx.features.settings.push.PushGatewaysFragment
import im.vector.riotx.features.share.IncomingShareFragment
@ -366,4 +368,14 @@ interface FragmentModule {
@IntoMap
@FragmentKey(AccountDataFragment::class)
fun bindAccountDataFragment(fragment: AccountDataFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KeyRequestListFragment::class)
fun bindKeyRequestListFragment(fragment: KeyRequestListFragment): Fragment
@Binds
@IntoMap
@FragmentKey(KeyRequestsFragment::class)
fun bindKeyRequestsFragment(fragment: KeyRequestsFragment): Fragment
}

View file

@ -0,0 +1,92 @@
/*
* 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.devtools
import com.airbnb.epoxy.TypedEpoxyController
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.loadingItem
import im.vector.riotx.core.extensions.exhaustive
import im.vector.riotx.core.resources.StringProvider
import im.vector.riotx.core.ui.list.genericItem
import im.vector.riotx.core.ui.list.genericItemHeader
import me.gujun.android.span.span
import javax.inject.Inject
class KeyRequestEpoxyController @Inject constructor(
private val stringProvider: StringProvider
) : TypedEpoxyController<KeyRequestListViewState>() {
interface InteractionListener {
//fun didTap(data: UserAccountData)
}
var interactionListener: InteractionListener? = null
override fun buildModels(data: KeyRequestListViewState?) {
data?.outgoingRoomKeyRequest?.let { async ->
when (async) {
is Uninitialized,
is Loading -> {
loadingItem {
id("loadingOutgoing")
loadingText(stringProvider.getString(R.string.loading))
}
}
is Fail -> {
genericItem {
id("failOutgoing")
title(async.error.localizedMessage)
}
}
is Success -> {
val requestList = async.invoke().groupBy { it.roomId }
requestList.forEach {
genericItemHeader {
id(it.key)
text("roomId: ${it.key}")
}
it.value.forEach { roomKeyRequest ->
genericItem {
id(roomKeyRequest.requestId)
title(roomKeyRequest.requestId)
description(
span {
span("sessionId:") {
textStyle = "bold"
}
+"${roomKeyRequest.sessionId}"
span("\nstate:") {
textStyle = "bold"
}
+"\n${roomKeyRequest.state.name}"
}
)
}
}
}
}
}.exhaustive
}
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.devtools
import android.os.Bundle
import android.view.View
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import im.vector.riotx.R
import im.vector.riotx.core.extensions.cleanup
import im.vector.riotx.core.extensions.configureWith
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.resources.ColorProvider
import kotlinx.android.synthetic.main.fragment_generic_recycler.*
import javax.inject.Inject
class KeyRequestListFragment @Inject constructor(
val viewModelFactory: KeyRequestListViewModel.Factory,
private val epoxyController: KeyRequestEpoxyController,
private val colorProvider: ColorProvider
) : VectorBaseFragment() {
override fun getLayoutResId() = R.layout.fragment_generic_recycler
private val viewModel: KeyRequestListViewModel by fragmentViewModel(KeyRequestListViewModel::class)
override fun invalidate() = withState(viewModel) { state ->
epoxyController.setData(state)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
recyclerView.configureWith(epoxyController, showDivider = true)
// epoxyController.interactionListener = this
}
override fun onDestroyView() {
super.onDestroyView()
recyclerView.cleanup()
// epoxyController.interactionListener = null
}
}

View file

@ -0,0 +1,69 @@
/*
* 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.devtools
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxState
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.crypto.IncomingRoomKeyRequest
import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
import im.vector.riotx.core.platform.EmptyAction
import im.vector.riotx.core.platform.EmptyViewEvents
import im.vector.riotx.core.platform.VectorViewModel
data class KeyRequestListViewState(
val incomingRequests: Async<List<IncomingRoomKeyRequest>> = Uninitialized,
val outgoingRoomKeyRequest: Async<List<OutgoingRoomKeyRequest>> = Uninitialized
) : MvRxState
class KeyRequestListViewModel @AssistedInject constructor(@Assisted initialState: KeyRequestListViewState,
private val session: Session)
: VectorViewModel<KeyRequestListViewState, EmptyAction, EmptyViewEvents>(initialState) {
init {
session.cryptoService().getOutgoingRoomKeyRequest().let {
setState {
copy(
outgoingRoomKeyRequest = Success(it)
)
}
}
}
override fun handle(action: EmptyAction) {}
@AssistedInject.Factory
interface Factory {
fun create(initialState: KeyRequestListViewState): KeyRequestListViewModel
}
companion object : MvRxViewModelFactory<KeyRequestListViewModel, KeyRequestListViewState> {
@JvmStatic
override fun create(viewModelContext: ViewModelContext, state: KeyRequestListViewState): KeyRequestListViewModel? {
val fragment: KeyRequestListFragment = (viewModelContext as FragmentViewModelContext).fragment()
return fragment.viewModelFactory.create(state)
}
}
}

View file

@ -0,0 +1,57 @@
/*
* 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.devtools
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator
import im.vector.riotx.R
import im.vector.riotx.core.platform.VectorBaseActivity
import im.vector.riotx.core.platform.VectorBaseFragment
import kotlinx.android.synthetic.main.fragment_devtool_keyrequests.*
import javax.inject.Inject
class KeyRequestsFragment @Inject constructor() : VectorBaseFragment() {
override fun getLayoutResId(): Int = R.layout.fragment_devtool_keyrequests
override fun onResume() {
super.onResume()
(activity as? VectorBaseActivity)?.supportActionBar?.setTitle(R.string.key_share_request)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
devToolKeyRequestPager.adapter = KeyReqPagerAdapter(requireActivity())
TabLayoutMediator(devToolKeyRequestTabs, devToolKeyRequestPager) { tab, _ ->
tab.text = "Outgoing"
}.attach()
}
private inner class KeyReqPagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = 1
override fun createFragment(position: Int): Fragment {
return childFragmentManager.fragmentFactory.instantiate(requireContext().classLoader, KeyRequestListFragment::class.java.name)
}
}
}

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:background="#FFFFFF">
<com.google.android.material.tabs.TabLayout
android:id="@+id/devToolKeyRequestTabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabMode="scrollable" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/devToolKeyRequestPager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>

View file

@ -7,6 +7,8 @@
<!-- BEGIN Strings added by Valere -->
<string name="settings_key_requests">Key Requests</string>
<!-- END Strings added by Valere -->

View file

@ -65,13 +65,20 @@
app:fragment="im.vector.riotx.features.settings.push.PushRulesFragment" />
</im.vector.riotx.core.preference.VectorPreferenceCategory>
<im.vector.riotx.core.preference.VectorPreferenceCategory android:title="@string/settings_dev_tools">
<im.vector.riotx.core.preference.VectorPreferenceCategory
android:dependency="SETTINGS_DEVELOPER_MODE_PREFERENCE_KEY"
android:title="@string/settings_dev_tools">
<im.vector.riotx.core.preference.VectorPreference
android:persistent="false"
android:title="@string/settings_account_data"
app:fragment="im.vector.riotx.features.settings.devtools.AccountDataFragment" />
<im.vector.riotx.core.preference.VectorPreference
android:persistent="false"
android:title="@string/settings_key_requests"
app:fragment="im.vector.riotx.features.settings.devtools.KeyRequestsFragment" />
</im.vector.riotx.core.preference.VectorPreferenceCategory>
</androidx.preference.PreferenceScreen>