warn on cross signing reset

This commit is contained in:
Valere 2022-08-01 10:41:56 +02:00
parent dc9451aeba
commit c8f0792997
16 changed files with 208 additions and 44 deletions

1
changelog.d/6702.bugfix Normal file
View File

@ -0,0 +1 @@
Add Warning shield when a user previously verified rotated his cross signing keys

View File

@ -18,7 +18,8 @@ package org.matrix.android.sdk.api.session.crypto.crosssigning
data class MXCrossSigningInfo( data class MXCrossSigningInfo(
val userId: String, val userId: String,
val crossSigningKeys: List<CryptoCrossSigningKey> val crossSigningKeys: List<CryptoCrossSigningKey>,
val wasTrustedOnce: Boolean
) { ) {
fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true && fun isTrusted(): Boolean = masterKey()?.trustLevel?.isVerified() == true &&

View File

@ -0,0 +1,28 @@
/*
* Copyright 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.api.session.crypto.model
enum class UserVerificationLevel {
VERIFIED_ALL_DEVICES_TRUSTED,
VERIFIED_WITH_DEVICES_UNTRUSTED,
UNVERIFIED_BUT_WAS_PREVIOUSLY,
WAS_NEVER_VERIFIED,
}

View File

@ -167,7 +167,11 @@ internal class DefaultCrossSigningService @Inject constructor(
} }
override fun onSuccess(data: InitializeCrossSigningTask.Result) { override fun onSuccess(data: InitializeCrossSigningTask.Result) {
val crossSigningInfo = MXCrossSigningInfo(userId, listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo)) val crossSigningInfo = MXCrossSigningInfo(
userId,
listOf(data.masterKeyInfo, data.userKeyInfo, data.selfSignedKeyInfo),
true
)
cryptoStore.setMyCrossSigningInfo(crossSigningInfo) cryptoStore.setMyCrossSigningInfo(crossSigningInfo)
setUserKeysAsTrusted(userId, true) setUserKeysAsTrusted(userId, true)
cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK) cryptoStore.storePrivateKeysInfo(data.masterKeyPK, data.userKeyPK, data.selfSigningKeyPK)

View File

@ -259,21 +259,27 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
cryptoRealm.where(CrossSigningInfoEntity::class.java) cryptoRealm.where(CrossSigningInfoEntity::class.java)
.equalTo(CrossSigningInfoEntityFields.USER_ID, userId) .equalTo(CrossSigningInfoEntityFields.USER_ID, userId)
.findFirst() .findFirst()
?.crossSigningKeys ?.let { userKeyInfo ->
?.forEach { info -> userKeyInfo
// optimization to avoid trigger updates when there is no change.. .crossSigningKeys
if (info.trustLevelEntity?.isVerified() != verified) { .forEach { key ->
Timber.d("## CrossSigning - Trust change for $userId : $verified") // optimization to avoid trigger updates when there is no change..
val level = info.trustLevelEntity if (key.trustLevelEntity?.isVerified() != verified) {
if (level == null) { Timber.d("## CrossSigning - Trust change for $userId : $verified")
info.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also { val level = key.trustLevelEntity
it.locallyVerified = verified if (level == null) {
it.crossSignedVerified = verified key.trustLevelEntity = cryptoRealm.createObject(TrustLevelEntity::class.java).also {
it.locallyVerified = verified
it.crossSignedVerified = verified
}
} else {
level.locallyVerified = verified
level.crossSignedVerified = verified
}
}
} }
} else { if (verified) {
level.locallyVerified = verified userKeyInfo.wasUserVerifiedOnce = true
level.crossSignedVerified = verified
}
} }
} }
} }
@ -299,8 +305,18 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
getCrossSigningInfo(cryptoRealm, userId)?.isTrusted() == true getCrossSigningInfo(cryptoRealm, userId)?.isTrusted() == true
} }
val resetTrust = listToCheck
.filter { userId ->
val crossSigningInfo = getCrossSigningInfo(cryptoRealm, userId)
crossSigningInfo?.isTrusted() != true && crossSigningInfo?.wasTrustedOnce == true
}
return if (allTrustedUserIds.isEmpty()) { return if (allTrustedUserIds.isEmpty()) {
RoomEncryptionTrustLevel.Default if (resetTrust.isEmpty()) {
RoomEncryptionTrustLevel.Default
} else {
RoomEncryptionTrustLevel.Warning
}
} else { } else {
// If one of the verified user as an untrusted device -> warning // If one of the verified user as an untrusted device -> warning
// If all devices of all verified users are trusted -> green // If all devices of all verified users are trusted -> green
@ -327,11 +343,15 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
if (hasWarning) { if (hasWarning) {
RoomEncryptionTrustLevel.Warning RoomEncryptionTrustLevel.Warning
} else { } else {
if (listToCheck.size == allTrustedUserIds.size) { if (resetTrust.isEmpty()) {
// all users are trusted and all devices are verified if (listToCheck.size == allTrustedUserIds.size) {
RoomEncryptionTrustLevel.Trusted // all users are trusted and all devices are verified
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Default
}
} else { } else {
RoomEncryptionTrustLevel.Default RoomEncryptionTrustLevel.Warning
} }
} }
} }
@ -344,7 +364,8 @@ internal class UpdateTrustWorker(context: Context, params: WorkerParameters, ses
userId = userId, userId = userId,
crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull { crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
crossSigningKeysMapper.map(userId, it) crossSigningKeysMapper.map(userId, it)
} },
wasTrustedOnce = xsignInfo.wasUserVerifiedOnce
) )
} }

View File

@ -1611,7 +1611,8 @@ internal class RealmCryptoStore @Inject constructor(
userId = userId, userId = userId,
crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull { crossSigningKeys = xsignInfo.crossSigningKeys.mapNotNull {
crossSigningKeysMapper.map(userId, it) crossSigningKeysMapper.map(userId, it)
} },
wasTrustedOnce = xsignInfo.wasUserVerifiedOnce
) )
} }

View File

@ -35,6 +35,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018 import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo019
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
import javax.inject.Inject import javax.inject.Inject
@ -49,7 +50,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(
private val clock: Clock, private val clock: Clock,
) : MatrixRealmMigration( ) : MatrixRealmMigration(
dbName = "Crypto", dbName = "Crypto",
schemaVersion = 18L, schemaVersion = 19L,
) { ) {
/** /**
* Forces all RealmCryptoStoreMigration instances to be equal. * Forces all RealmCryptoStoreMigration instances to be equal.
@ -77,5 +78,6 @@ internal class RealmCryptoStoreMigration @Inject constructor(
if (oldVersion < 16) MigrateCryptoTo016(realm).perform() if (oldVersion < 16) MigrateCryptoTo016(realm).perform()
if (oldVersion < 17) MigrateCryptoTo017(realm).perform() if (oldVersion < 17) MigrateCryptoTo017(realm).perform()
if (oldVersion < 18) MigrateCryptoTo018(realm).perform() if (oldVersion < 18) MigrateCryptoTo018(realm).perform()
if (oldVersion < 19) MigrateCryptoTo019(realm).perform()
} }
} }

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.crypto.store.db.migration
import io.realm.DynamicRealm
import io.realm.DynamicRealmObject
import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntityFields
import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntityFields
import org.matrix.android.sdk.internal.util.database.RealmMigrator
/**
* This migration is adding support for trusted flags on megolm sessions.
* We can't really assert the trust of existing keys, so for the sake of simplicity we are going to
* mark existing keys as safe.
* This migration can take long depending on the account
*/
internal class MigrateCryptoTo019(realm: DynamicRealm) : RealmMigrator(realm, 18) {
override fun doMigrate(realm: DynamicRealm) {
realm.schema.get("CrossSigningInfoEntity")
?.addField(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, Boolean::class.java)
?.transform { dynamicObject ->
val knowKeys = dynamicObject.getList(CrossSigningInfoEntityFields.CROSS_SIGNING_KEYS.`$`)
val msk = knowKeys.firstOrNull {
it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.MASTER.value)
}
val ssk = knowKeys.firstOrNull {
it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.SELF_SIGNING.value)
}
val isTrusted = isDynamicKeyInfoTrusted(msk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`)) &&
isDynamicKeyInfoTrusted(ssk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`))
dynamicObject.setBoolean(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, isTrusted)
}
}
private fun isDynamicKeyInfoTrusted(keyInfo: DynamicRealmObject?): Boolean {
if (keyInfo == null) return false
return !keyInfo.isNull(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) &&
!keyInfo.isNull(TrustLevelEntityFields.LOCALLY_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED)
}
}

View File

@ -25,6 +25,7 @@ import org.matrix.android.sdk.internal.extensions.clearWith
internal open class CrossSigningInfoEntity( internal open class CrossSigningInfoEntity(
@PrimaryKey @PrimaryKey
var userId: String? = null, var userId: String? = null,
var wasUserVerifiedOnce: Boolean = false,
var crossSigningKeys: RealmList<KeyInfoEntity> = RealmList() var crossSigningKeys: RealmList<KeyInfoEntity> = RealmList()
) : RealmObject() { ) : RealmObject() {

View File

@ -26,7 +26,7 @@ import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.setTextOrHide import im.vector.app.core.extensions.setTextOrHide
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes layoutId: Int) : VectorEpoxyModel<T>(layoutId) { abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes layoutId: Int) : VectorEpoxyModel<T>(layoutId) {
@ -35,7 +35,7 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes la
@EpoxyAttribute var editable: Boolean = true @EpoxyAttribute var editable: Boolean = true
@EpoxyAttribute @EpoxyAttribute
var userEncryptionTrustLevel: RoomEncryptionTrustLevel? = null var userVerificationLevel: UserVerificationLevel? = null
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
var clickListener: ClickListener? = null var clickListener: ClickListener? = null
@ -53,6 +53,6 @@ abstract class BaseProfileMatrixItem<T : ProfileMatrixItem.Holder>(@LayoutRes la
holder.subtitleView.setTextOrHide(matrixId) holder.subtitleView.setTextOrHide(matrixId)
holder.editableView.isVisible = editable holder.editableView.isVisible = editable
avatarRenderer.render(matrixItem, holder.avatarImageView) avatarRenderer.render(matrixItem, holder.avatarImageView)
holder.avatarDecorationImageView.render(userEncryptionTrustLevel) holder.avatarDecorationImageView.renderUser(userVerificationLevel)
} }
} }

View File

@ -24,6 +24,7 @@ import androidx.core.view.isVisible
import im.vector.app.R import im.vector.app.R
import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
class ShieldImageView @JvmOverloads constructor( class ShieldImageView @JvmOverloads constructor(
context: Context, context: Context,
@ -102,6 +103,35 @@ class ShieldImageView @JvmOverloads constructor(
} }
} }
} }
fun renderUser(userVerificationLevel: UserVerificationLevel?, borderLess: Boolean = false) {
isVisible = userVerificationLevel != null
when (userVerificationLevel) {
UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED -> {
contentDescription = context.getString(R.string.a11y_trust_level_trusted)
setImageResource(
if (borderLess) R.drawable.ic_shield_trusted_no_border
else R.drawable.ic_shield_trusted
)
}
UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY,
UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED -> {
contentDescription = context.getString(R.string.a11y_trust_level_warning)
setImageResource(
if (borderLess) R.drawable.ic_shield_warning_no_border
else R.drawable.ic_shield_warning
)
}
UserVerificationLevel.WAS_NEVER_VERIFIED -> {
contentDescription = context.getString(R.string.a11y_trust_level_default)
setImageResource(
if (borderLess) R.drawable.ic_shield_black_no_border
else R.drawable.ic_shield_black
)
}
null -> Unit
}
}
} }
@DrawableRes @DrawableRes

View File

@ -59,7 +59,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.MatrixItemColorPr
import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet import im.vector.app.features.roommemberprofile.devices.DeviceListBottomSheet
import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs import im.vector.app.features.roommemberprofile.powerlevel.EditPowerLevelDialogs
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
import org.matrix.android.sdk.api.session.room.powerlevels.Role import org.matrix.android.sdk.api.session.room.powerlevels.Role
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
import javax.inject.Inject import javax.inject.Inject
@ -235,23 +235,27 @@ class RoomMemberProfileFragment :
if (state.userMXCrossSigningInfo.isTrusted()) { if (state.userMXCrossSigningInfo.isTrusted()) {
// User is trusted // User is trusted
if (state.allDevicesAreCrossSignedTrusted) { if (state.allDevicesAreCrossSignedTrusted) {
RoomEncryptionTrustLevel.Trusted UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
} else { } else {
RoomEncryptionTrustLevel.Warning UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
} }
} else { } else {
RoomEncryptionTrustLevel.Default if (state.userMXCrossSigningInfo.wasTrustedOnce) {
UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
} else {
UserVerificationLevel.WAS_NEVER_VERIFIED
}
} }
} else { } else {
// Legacy // Legacy
if (state.allDevicesAreTrusted) { if (state.allDevicesAreTrusted) {
RoomEncryptionTrustLevel.Trusted UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
} else { } else {
RoomEncryptionTrustLevel.Warning UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
} }
} }
headerViews.memberProfileDecorationImageView.render(trustLevel) headerViews.memberProfileDecorationImageView.renderUser(trustLevel)
views.matrixProfileDecorationToolbarAvatarImageView.render(trustLevel) views.matrixProfileDecorationToolbarAvatarImageView.renderUser(trustLevel)
} else { } else {
headerViews.memberProfileDecorationImageView.isVisible = false headerViews.memberProfileDecorationImageView.isVisible = false
} }

View File

@ -129,7 +129,7 @@ class RoomMemberListController @Inject constructor(
id(roomMember.userId) id(roomMember.userId)
matrixItem(roomMember.toMatrixItem()) matrixItem(roomMember.toMatrixItem())
avatarRenderer(host.avatarRenderer) avatarRenderer(host.avatarRenderer)
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
clickListener { clickListener {
host.callback?.onRoomMemberClicked(roomMember) host.callback?.onRoomMemberClicked(roomMember)
} }

View File

@ -37,7 +37,7 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.query.QueryStringValue import org.matrix.android.sdk.api.query.QueryStringValue
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom
@ -119,10 +119,22 @@ class RoomMemberListViewModel @AssistedInject constructor(
val allDeviceTrusted = it.value.fold(it.value.isNotEmpty()) { prev, next -> val allDeviceTrusted = it.value.fold(it.value.isNotEmpty()) { prev, next ->
prev && next.trustLevel?.isCrossSigningVerified().orFalse() prev && next.trustLevel?.isCrossSigningVerified().orFalse()
} }
if (session.cryptoService().crossSigningService().getUserCrossSigningKeys(it.key)?.isTrusted().orFalse()) { val mxCrossSigningInfo = session.cryptoService().crossSigningService().getUserCrossSigningKeys(it.key)
if (allDeviceTrusted) RoomEncryptionTrustLevel.Trusted else RoomEncryptionTrustLevel.Warning when {
} else { mxCrossSigningInfo == null -> {
RoomEncryptionTrustLevel.Default UserVerificationLevel.WAS_NEVER_VERIFIED
}
mxCrossSigningInfo.isTrusted() -> {
if (allDeviceTrusted) UserVerificationLevel.VERIFIED_ALL_DEVICES_TRUSTED
else UserVerificationLevel.VERIFIED_WITH_DEVICES_UNTRUSTED
}
else -> {
if (mxCrossSigningInfo.wasTrustedOnce) {
UserVerificationLevel.UNVERIFIED_BUT_WAS_PREVIOUSLY
} else {
UserVerificationLevel.WAS_NEVER_VERIFIED
}
}
} }
} }
} }

View File

@ -23,7 +23,7 @@ import com.airbnb.mvrx.Uninitialized
import im.vector.app.R import im.vector.app.R
import im.vector.app.core.platform.GenericIdArgs import im.vector.app.core.platform.GenericIdArgs
import im.vector.app.features.roomprofile.RoomProfileArgs import im.vector.app.features.roomprofile.RoomProfileArgs
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.UserVerificationLevel
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary
import org.matrix.android.sdk.api.session.room.model.RoomSummary import org.matrix.android.sdk.api.session.room.model.RoomSummary
@ -36,7 +36,7 @@ data class RoomMemberListViewState(
val ignoredUserIds: List<String> = emptyList(), val ignoredUserIds: List<String> = emptyList(),
val filter: String = "", val filter: String = "",
val threePidInvites: Async<List<Event>> = Uninitialized, val threePidInvites: Async<List<Event>> = Uninitialized,
val trustLevelMap: Async<Map<String, RoomEncryptionTrustLevel?>> = Uninitialized, val trustLevelMap: Async<Map<String, UserVerificationLevel>> = Uninitialized,
val actionsPermissions: ActionPermissions = ActionPermissions() val actionsPermissions: ActionPermissions = ActionPermissions()
) : MavericksState { ) : MavericksState {

View File

@ -77,7 +77,7 @@ class SpacePeopleListController @Inject constructor(
id(roomMember.userId) id(roomMember.userId)
matrixItem(roomMember.toMatrixItem()) matrixItem(roomMember.toMatrixItem())
avatarRenderer(host.avatarRenderer) avatarRenderer(host.avatarRenderer)
userEncryptionTrustLevel(data.trustLevelMap.invoke()?.get(roomMember.userId)) userVerificationLevel(data.trustLevelMap.invoke()?.get(roomMember.userId))
.apply { .apply {
val pl = host.toPowerLevelLabel(memberEntry.first) val pl = host.toPowerLevelLabel(memberEntry.first)
if (memberEntry.first == RoomMemberListCategories.INVITE) { if (memberEntry.first == RoomMemberListCategories.INVITE) {