Show untrusted conclusions

This commit is contained in:
Valere 2019-12-11 18:19:32 +01:00
parent 0776a301ea
commit a673bf092d
8 changed files with 104 additions and 13 deletions

View file

@ -24,7 +24,7 @@ import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultC
import im.vector.matrix.android.internal.crypto.verification.VerificationInfoCancel import im.vector.matrix.android.internal.crypto.verification.VerificationInfoCancel
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
internal data class MessageVerificationCancelContent( data class MessageVerificationCancelContent(
@Json(name = "code") override val code: String? = null, @Json(name = "code") override val code: String? = null,
@Json(name = "reason") override val reason: String? = null, @Json(name = "reason") override val reason: String? = null,
@Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? @Json(name = "m.relates_to") val relatesTo: RelationDefaultContent?

View file

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.session.room
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.crypto.MXCryptoError import im.vector.matrix.android.api.session.crypto.MXCryptoError
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.events.model.* import im.vector.matrix.android.api.session.events.model.*
import im.vector.matrix.android.api.session.room.model.ReferencesAggregatedContent import im.vector.matrix.android.api.session.room.model.ReferencesAggregatedContent
import im.vector.matrix.android.api.session.room.model.message.MessageContent import im.vector.matrix.android.api.session.room.model.message.MessageContent
@ -52,6 +53,9 @@ enum class VerificationState {
DONE DONE
} }
fun VerificationState.isCanceled() : Boolean {
return this == VerificationState.CANCELED_BY_ME || this == VerificationState.CANCELED_BY_OTHER
}
/** /**
* Called by EventRelationAggregationUpdater, when new events that can affect relations are inserted in base. * Called by EventRelationAggregationUpdater, when new events that can affect relations are inserted in base.
*/ */
@ -438,26 +442,27 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
?: ReferencesAggregatedContent(VerificationState.REQUEST.name) ?: ReferencesAggregatedContent(VerificationState.REQUEST.name)
// TODO ignore invalid messages? e.g a START after a CANCEL? // TODO ignore invalid messages? e.g a START after a CANCEL?
// i.e. never change state if already canceled/done // i.e. never change state if already canceled/done
val currentState = VerificationState.values().firstOrNull { data.verificationSummary == it.name }
val newState = when (event.getClearType()) { val newState = when (event.getClearType()) {
EventType.KEY_VERIFICATION_START -> { EventType.KEY_VERIFICATION_START -> {
VerificationState.WAITING updateVerificationState(currentState, VerificationState.WAITING)
} }
EventType.KEY_VERIFICATION_ACCEPT -> { EventType.KEY_VERIFICATION_ACCEPT -> {
VerificationState.WAITING updateVerificationState(currentState, VerificationState.WAITING)
} }
EventType.KEY_VERIFICATION_KEY -> { EventType.KEY_VERIFICATION_KEY -> {
VerificationState.WAITING updateVerificationState(currentState, VerificationState.WAITING)
} }
EventType.KEY_VERIFICATION_MAC -> { EventType.KEY_VERIFICATION_MAC -> {
VerificationState.WAITING updateVerificationState(currentState, VerificationState.WAITING)
} }
EventType.KEY_VERIFICATION_CANCEL -> { EventType.KEY_VERIFICATION_CANCEL -> {
if (event.senderId == userId) { updateVerificationState(currentState, if (event.senderId == userId) {
VerificationState.CANCELED_BY_ME VerificationState.CANCELED_BY_ME
} else VerificationState.CANCELED_BY_OTHER } else VerificationState.CANCELED_BY_OTHER)
} }
EventType.KEY_VERIFICATION_DONE -> { EventType.KEY_VERIFICATION_DONE -> {
VerificationState.DONE updateVerificationState(currentState, VerificationState.DONE)
} }
else -> VerificationState.REQUEST else -> VerificationState.REQUEST
} }
@ -475,4 +480,18 @@ internal class DefaultEventRelationsAggregationTask @Inject constructor(
verifSummary.sourceEvents.add(event.eventId) verifSummary.sourceEvents.add(event.eventId)
} }
} }
private fun updateVerificationState(oldState: VerificationState?, newState: VerificationState) : VerificationState{
// Cancel is always prioritary ?
// Eg id i found that mac or keys mismatch and send a cancel and the other send a done, i have to
// consider as canceled
if (newState == VerificationState.CANCELED_BY_OTHER || newState == VerificationState.CANCELED_BY_ME) {
return newState
}
//never move out of cancel
if (oldState == VerificationState.CANCELED_BY_OTHER || oldState == VerificationState.CANCELED_BY_ME) {
return oldState
}
return newState
}
} }

View file

@ -67,13 +67,13 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me
} }
EventType.KEY_VERIFICATION_ACCEPT, EventType.KEY_VERIFICATION_ACCEPT,
EventType.KEY_VERIFICATION_START, EventType.KEY_VERIFICATION_START,
EventType.KEY_VERIFICATION_CANCEL,
EventType.KEY_VERIFICATION_KEY, EventType.KEY_VERIFICATION_KEY,
EventType.KEY_VERIFICATION_MAC -> { EventType.KEY_VERIFICATION_MAC -> {
// These events are filtered from timeline in normal case // These events are filtered from timeline in normal case
// Only visible in developer mode // Only visible in developer mode
noticeItemFactory.create(event, highlight, callback) noticeItemFactory.create(event, highlight, callback)
} }
EventType.KEY_VERIFICATION_CANCEL,
EventType.KEY_VERIFICATION_DONE -> { EventType.KEY_VERIFICATION_DONE -> {
verificationConclusionItemFactory.create(event, highlight, callback) verificationConclusionItemFactory.create(event, highlight, callback)
} }

View file

@ -16,13 +16,17 @@
package im.vector.riotx.features.home.room.detail.timeline.factory package im.vector.riotx.features.home.room.detail.timeline.factory
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.MessageRelationContent import im.vector.matrix.android.api.session.room.model.message.MessageRelationContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationCancelContent
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.session.room.VerificationState import im.vector.matrix.android.internal.session.room.VerificationState
import im.vector.matrix.android.internal.session.room.isCanceled
import im.vector.riotx.core.epoxy.VectorEpoxyModel import im.vector.riotx.core.epoxy.VectorEpoxyModel
import im.vector.riotx.core.resources.ColorProvider import im.vector.riotx.core.resources.ColorProvider
import im.vector.riotx.core.resources.UserPreferencesProvider import im.vector.riotx.core.resources.UserPreferencesProvider
@ -70,15 +74,52 @@ class VerificationItemFactory @Inject constructor(
// If it's not a request ignore this event // If it's not a request ignore this event
if (refEvent.root.getClearContent().toModel<MessageVerificationRequestContent>() == null) return ignoredConclusion(event, highlight, callback) if (refEvent.root.getClearContent().toModel<MessageVerificationRequestContent>() == null) return ignoredConclusion(event, highlight, callback)
// Is the request referenced is actually really completed?
val referenceInformationData = messageInformationDataFactory.create(refEvent, null) val referenceInformationData = messageInformationDataFactory.create(refEvent, null)
if (referenceInformationData.referencesInfoData?.verificationStatus != VerificationState.DONE) return ignoredConclusion(event, highlight, callback)
val informationData = messageInformationDataFactory.create(event, null) val informationData = messageInformationDataFactory.create(event, null)
val attributes = messageItemAttributesFactory.create(null, informationData, callback) val attributes = messageItemAttributesFactory.create(null, informationData, callback)
when (event.root.getClearType()) { when (event.root.getClearType()) {
EventType.KEY_VERIFICATION_CANCEL -> {
// Is the request referenced is actually really cancelled?
// if (referenceInformationData.referencesInfoData?.verificationStatus?.isCanceled() == false) return ignoredConclusion(event, highlight, callback)
val cancelContent = event.root.getClearContent().toModel<MessageVerificationCancelContent>()
?: return ignoredConclusion(event, highlight, callback)
when (safeValueOf(cancelContent.code)) {
CancelCode.MismatchedCommitment,
CancelCode.MismatchedKeys,
CancelCode.MismatchedSas -> {
//We should display these bad conclusions
return VerificationRequestConclusionItem_()
.attributes(
VerificationRequestConclusionItem.Attributes(
toUserId = informationData.senderId,
toUserName = informationData.memberName.toString(),
isPositive = false,
informationData = informationData,
avatarRenderer = attributes.avatarRenderer,
colorProvider = colorProvider,
emojiTypeFace = attributes.emojiTypeFace,
itemClickListener = attributes.itemClickListener,
itemLongClickListener = attributes.itemLongClickListener,
reactionPillCallback = attributes.reactionPillCallback,
readReceiptsCallback = attributes.readReceiptsCallback
)
)
.highlighted(highlight)
.leftGuideline(avatarSizeProvider.leftGuideline)
}
else -> ignoredConclusion(event, highlight, callback)
}
}
EventType.KEY_VERIFICATION_DONE -> { EventType.KEY_VERIFICATION_DONE -> {
// Is the request referenced is actually really completed?
if (referenceInformationData.referencesInfoData?.verificationStatus != VerificationState.DONE) return ignoredConclusion(event, highlight, callback)
// We only tale the one sent by me // We only tale the one sent by me
if (informationData.sentByMe) { if (informationData.sentByMe) {
// We only display the done sent by the other user, the done send by me is ignored // We only display the done sent by the other user, the done send by me is ignored
@ -89,6 +130,7 @@ class VerificationItemFactory @Inject constructor(
VerificationRequestConclusionItem.Attributes( VerificationRequestConclusionItem.Attributes(
toUserId = informationData.senderId, toUserId = informationData.senderId,
toUserName = informationData.memberName.toString(), toUserName = informationData.memberName.toString(),
isPositive = true,
informationData = informationData, informationData = informationData,
avatarRenderer = attributes.avatarRenderer, avatarRenderer = attributes.avatarRenderer,
colorProvider = colorProvider, colorProvider = colorProvider,

View file

@ -20,6 +20,7 @@ import android.graphics.Typeface
import android.view.View import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import androidx.appcompat.widget.AppCompatTextView import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
@ -45,8 +46,15 @@ abstract class VerificationRequestConclusionItem : AbsBaseMessageItem<Verificati
holder.endGuideline.updateLayoutParams<RelativeLayout.LayoutParams> { holder.endGuideline.updateLayoutParams<RelativeLayout.LayoutParams> {
this.marginEnd = leftGuideline this.marginEnd = leftGuideline
} }
holder.titleView.text = holder.view.context.getString(R.string.sas_verified) val title = if (attributes.isPositive) R.string.sas_verified else R.string.verification_conclusion_warning
holder.titleView.text = holder.view.context.getString(title)
holder.descriptionView.text = "${attributes.informationData.memberName} (${attributes.informationData.senderId})" holder.descriptionView.text = "${attributes.informationData.memberName} (${attributes.informationData.senderId})"
val startDrawable = if (attributes.isPositive) R.drawable.ic_shield_trusted else R.drawable.ic_shield_warning
holder.titleView.setCompoundDrawablesWithIntrinsicBounds(
ContextCompat.getDrawable(holder.view.context, startDrawable),
null, null, null
)
} }
class Holder : AbsBaseMessageItem.Holder(STUB_ID) { class Holder : AbsBaseMessageItem.Holder(STUB_ID) {
@ -65,6 +73,7 @@ abstract class VerificationRequestConclusionItem : AbsBaseMessageItem<Verificati
data class Attributes( data class Attributes(
val toUserId: String, val toUserId: String,
val toUserName: String, val toUserName: String,
val isPositive: Boolean,
override val informationData: MessageInformationData, override val informationData: MessageInformationData,
override val avatarRenderer: AvatarRenderer, override val avatarRenderer: AvatarRenderer,
override val colorProvider: ColorProvider, override val colorProvider: ColorProvider,

View file

@ -0,0 +1,20 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:strokeWidth="1"
android:pathData="m12,21s9,-3.8 9,-9.5v-6.65l-9,-2.85 -9,2.85v6.65c0,5.7 9,9.5 9,9.5z"
android:strokeLineJoin="round"
android:fillColor="#ff4b55"
android:strokeColor="#fff"
android:fillType="evenOdd"
android:strokeLineCap="round"/>
<path
android:pathData="M12.05,5.5L12.05,5.5A1.25,1.25 0,0 1,13.3 6.75L13.3,12.25A1.25,1.25 0,0 1,12.05 13.5L12.05,13.5A1.25,1.25 0,0 1,10.8 12.25L10.8,6.75A1.25,1.25 0,0 1,12.05 5.5z"
android:fillColor="#fff"/>
<path
android:pathData="M12.05,15L12.05,15A1.25,1.25 0,0 1,13.3 16.25L13.3,16.25A1.25,1.25 0,0 1,12.05 17.5L12.05,17.5A1.25,1.25 0,0 1,10.8 16.25L10.8,16.25A1.25,1.25 0,0 1,12.05 15z"
android:fillColor="#fff"/>
</vector>

View file

@ -11,7 +11,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:drawableStart="@drawable/ic_shield_trusted" tools:drawableStart="@drawable/ic_shield_trusted"
android:drawablePadding="6dp" android:drawablePadding="6dp"
android:gravity="center" android:gravity="center"
android:textColor="?riotx_text_primary" android:textColor="?riotx_text_primary"

View file

@ -150,6 +150,7 @@
<string name="verification_request_you_cancelled">You cancelled</string> <string name="verification_request_you_cancelled">You cancelled</string>
<string name="verification_request_other_cancelled">%s cancelled</string> <string name="verification_request_other_cancelled">%s cancelled</string>
<string name="verification_request_waiting">Waiting…</string> <string name="verification_request_waiting">Waiting…</string>
<string name="verification_conclusion_warning">Untrusted sign in</string>
</resources> </resources>