Fix QR code not always displayed
This commit is contained in:
parent
0c1e439313
commit
bed2c221e3
@ -206,14 +206,14 @@ class VerificationTest : InstrumentedTest {
|
|||||||
|
|
||||||
aliceReadyPendingVerificationRequest!!.let { pr ->
|
aliceReadyPendingVerificationRequest!!.let { pr ->
|
||||||
pr.isSasSupported shouldBe expectedResultForAlice.sasIsSupported
|
pr.isSasSupported shouldBe expectedResultForAlice.sasIsSupported
|
||||||
pr.otherCanShowQrCode shouldBe expectedResultForAlice.otherCanShowQrCode
|
pr.weShouldShowScanOption shouldBe expectedResultForAlice.otherCanShowQrCode
|
||||||
pr.otherCanScanQrCode shouldBe expectedResultForAlice.otherCanScanQrCode
|
pr.weShouldDisplayQRCode shouldBe expectedResultForAlice.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
bobReadyPendingVerificationRequest!!.let { pr ->
|
bobReadyPendingVerificationRequest!!.let { pr ->
|
||||||
pr.isSasSupported shouldBe expectedResultForBob.sasIsSupported
|
pr.isSasSupported shouldBe expectedResultForBob.sasIsSupported
|
||||||
pr.otherCanShowQrCode shouldBe expectedResultForBob.otherCanShowQrCode
|
pr.weShouldShowScanOption shouldBe expectedResultForBob.otherCanShowQrCode
|
||||||
pr.otherCanScanQrCode shouldBe expectedResultForBob.otherCanScanQrCode
|
pr.weShouldDisplayQRCode shouldBe expectedResultForBob.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
|
@ -246,14 +246,14 @@ class VerificationTest : InstrumentedTest {
|
|||||||
|
|
||||||
aliceReadyPendingVerificationRequest!!.let { pr ->
|
aliceReadyPendingVerificationRequest!!.let { pr ->
|
||||||
pr.isSasSupported shouldBe expectedResultForAlice.sasIsSupported
|
pr.isSasSupported shouldBe expectedResultForAlice.sasIsSupported
|
||||||
pr.otherCanShowQrCode shouldBe expectedResultForAlice.otherCanShowQrCode
|
pr.weShouldShowScanOption shouldBe expectedResultForAlice.otherCanShowQrCode
|
||||||
pr.otherCanScanQrCode shouldBe expectedResultForAlice.otherCanScanQrCode
|
pr.weShouldDisplayQRCode shouldBe expectedResultForAlice.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
|
|
||||||
bobReadyPendingVerificationRequest!!.let { pr ->
|
bobReadyPendingVerificationRequest!!.let { pr ->
|
||||||
pr.isSasSupported shouldBe expectedResultForBob.sasIsSupported
|
pr.isSasSupported shouldBe expectedResultForBob.sasIsSupported
|
||||||
pr.otherCanShowQrCode shouldBe expectedResultForBob.otherCanShowQrCode
|
pr.weShouldShowScanOption shouldBe expectedResultForBob.otherCanShowQrCode
|
||||||
pr.otherCanScanQrCode shouldBe expectedResultForBob.otherCanScanQrCode
|
pr.weShouldDisplayQRCode shouldBe expectedResultForBob.otherCanScanQrCode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ package org.matrix.android.sdk.internal.crypto.verification
|
|||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.IVerificationRequest
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
|
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
|
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
|
||||||
@ -35,7 +34,7 @@ internal class KotlinVerificationRequest(
|
|||||||
val otherUserId: String,
|
val otherUserId: String,
|
||||||
var state: EVerificationState,
|
var state: EVerificationState,
|
||||||
val ageLocalTs: Long
|
val ageLocalTs: Long
|
||||||
) : IVerificationRequest {
|
) {
|
||||||
|
|
||||||
var roomId: String? = null
|
var roomId: String? = null
|
||||||
var qrCodeData: QrCodeData? = null
|
var qrCodeData: QrCodeData? = null
|
||||||
@ -44,21 +43,21 @@ internal class KotlinVerificationRequest(
|
|||||||
var readyInfo: ValidVerificationInfoReady? = null
|
var readyInfo: ValidVerificationInfoReady? = null
|
||||||
var cancelCode: CancelCode? = null
|
var cancelCode: CancelCode? = null
|
||||||
|
|
||||||
override fun requestId() = requestId
|
// fun requestId() = requestId
|
||||||
|
//
|
||||||
|
// fun incoming() = incoming
|
||||||
|
//
|
||||||
|
// fun otherUserId() = otherUserId
|
||||||
|
//
|
||||||
|
// fun roomId() = roomId
|
||||||
|
//
|
||||||
|
// fun targetDevices() = targetDevices
|
||||||
|
//
|
||||||
|
// fun state() = state
|
||||||
|
//
|
||||||
|
// fun ageLocalTs() = ageLocalTs
|
||||||
|
|
||||||
override fun incoming() = incoming
|
fun otherDeviceId(): String? {
|
||||||
|
|
||||||
override fun otherUserId() = otherUserId
|
|
||||||
|
|
||||||
override fun roomId() = roomId
|
|
||||||
|
|
||||||
override fun targetDevices() = targetDevices
|
|
||||||
|
|
||||||
override fun state() = state
|
|
||||||
|
|
||||||
override fun ageLocalTs() = ageLocalTs
|
|
||||||
|
|
||||||
override fun otherDeviceId(): String? {
|
|
||||||
return if (incoming) {
|
return if (incoming) {
|
||||||
requestInfo?.fromDevice
|
requestInfo?.fromDevice
|
||||||
} else {
|
} else {
|
||||||
@ -66,12 +65,12 @@ internal class KotlinVerificationRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancelCode(): CancelCode? = cancelCode
|
fun cancelCode(): CancelCode? = cancelCode
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SAS is supported if I support it and the other party support it.
|
* SAS is supported if I support it and the other party support it.
|
||||||
*/
|
*/
|
||||||
override fun isSasSupported(): Boolean {
|
private fun isSasSupported(): Boolean {
|
||||||
return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() &&
|
return requestInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse() &&
|
||||||
readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS).orFalse()
|
||||||
}
|
}
|
||||||
@ -79,7 +78,7 @@ internal class KotlinVerificationRequest(
|
|||||||
/**
|
/**
|
||||||
* Other can show QR code if I can scan QR code and other can show QR code.
|
* Other can show QR code if I can scan QR code and other can show QR code.
|
||||||
*/
|
*/
|
||||||
override fun otherCanShowQrCode(): Boolean {
|
private fun otherCanShowQrCode(): Boolean {
|
||||||
return if (incoming) {
|
return if (incoming) {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() &&
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse() &&
|
||||||
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse()
|
||||||
@ -92,7 +91,7 @@ internal class KotlinVerificationRequest(
|
|||||||
/**
|
/**
|
||||||
* Other can scan QR code if I can show QR code and other can scan QR code.
|
* Other can scan QR code if I can show QR code and other can scan QR code.
|
||||||
*/
|
*/
|
||||||
override fun otherCanScanQrCode(): Boolean {
|
private fun otherCanScanQrCode(): Boolean {
|
||||||
return if (incoming) {
|
return if (incoming) {
|
||||||
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() &&
|
requestInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN).orFalse() &&
|
||||||
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW).orFalse()
|
||||||
@ -102,7 +101,7 @@ internal class KotlinVerificationRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun qrCodeText() = qrCodeData?.toEncodedString()
|
fun qrCodeText() = qrCodeData?.toEncodedString()
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return toPendingVerificationRequest().toString()
|
return toPendingVerificationRequest().toString()
|
||||||
@ -122,9 +121,11 @@ internal class KotlinVerificationRequest(
|
|||||||
targetDevices = targetDevices,
|
targetDevices = targetDevices,
|
||||||
qrCodeText = qrCodeText(),
|
qrCodeText = qrCodeText(),
|
||||||
isSasSupported = isSasSupported(),
|
isSasSupported = isSasSupported(),
|
||||||
otherCanShowQrCode = otherCanShowQrCode(),
|
weShouldShowScanOption = otherCanShowQrCode(),
|
||||||
otherCanScanQrCode = otherCanScanQrCode(),
|
weShouldDisplayQRCode = otherCanScanQrCode(),
|
||||||
otherDeviceId = otherDeviceId()
|
otherDeviceId = otherDeviceId()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isFinished() = state == EVerificationState.Cancelled || state == EVerificationState.Done
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,8 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
*/
|
*/
|
||||||
private val pendingRequests = HashMap<String, MutableList<KotlinVerificationRequest>>()
|
private val pendingRequests = HashMap<String, MutableList<KotlinVerificationRequest>>()
|
||||||
|
|
||||||
// Replaces the typical list of listeners pattern. Looks to me as the sane setup, not sure if more than 1 is needed as extraBufferCapacity
|
// Replaces the typical list of listeners pattern.
|
||||||
// We don't want to use emit as it would block if no listener is subscribed
|
// Looks to me as the sane setup, not sure if more than 1 is needed as extraBufferCapacity
|
||||||
// So we should use try emit using extraBufferCapacity, we use drop_oldest instead of suspend.
|
// So we should use try emit using extraBufferCapacity, we use drop_oldest instead of suspend.
|
||||||
val eventFlow = MutableSharedFlow<VerificationEvent>(extraBufferCapacity = 4, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
val eventFlow = MutableSharedFlow<VerificationEvent>(extraBufferCapacity = 4, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
.v("[${myUserId.take(8)}]: $msg")
|
.v("[${myUserId.take(8)}]: $msg")
|
||||||
when (msg) {
|
when (msg) {
|
||||||
is VerificationIntent.ActionRequestVerification -> {
|
is VerificationIntent.ActionRequestVerification -> {
|
||||||
handleRequestAdd(msg)
|
handleActionRequestVerification(msg)
|
||||||
}
|
}
|
||||||
is VerificationIntent.OnReadyReceived -> {
|
is VerificationIntent.OnReadyReceived -> {
|
||||||
handleReadyReceived(msg)
|
handleReadyReceived(msg)
|
||||||
@ -1046,7 +1046,7 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
existing.state = SasTransactionState.Done(true)
|
existing.state = SasTransactionState.Done(true)
|
||||||
dispatchUpdate(VerificationEvent.TransactionUpdated(existing))
|
dispatchUpdate(VerificationEvent.TransactionUpdated(existing))
|
||||||
// we can forget about it
|
// we can forget about it
|
||||||
txMap[matchingRequest.otherUserId()]?.remove(matchingRequest.requestId)
|
txMap[matchingRequest.otherUserId]?.remove(matchingRequest.requestId)
|
||||||
// XXX whatabout waiting for done?
|
// XXX whatabout waiting for done?
|
||||||
matchingRequest.state = EVerificationState.Done
|
matchingRequest.state = EVerificationState.Done
|
||||||
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
||||||
@ -1137,7 +1137,7 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
existing.state = QRCodeVerificationState.Done
|
existing.state = QRCodeVerificationState.Done
|
||||||
dispatchUpdate(VerificationEvent.TransactionUpdated(existing))
|
dispatchUpdate(VerificationEvent.TransactionUpdated(existing))
|
||||||
// we can forget about it
|
// we can forget about it
|
||||||
txMap[matchingRequest.otherUserId()]?.remove(matchingRequest.requestId)
|
txMap[matchingRequest.otherUserId]?.remove(matchingRequest.requestId)
|
||||||
matchingRequest.state = EVerificationState.WaitingForDone
|
matchingRequest.state = EVerificationState.WaitingForDone
|
||||||
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
||||||
|
|
||||||
@ -1309,6 +1309,11 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
if (commonMethods.isEmpty()) {
|
if (commonMethods.isEmpty()) {
|
||||||
Timber.tag(loggerTag.value).v("Request ${msg.transactionId} no common methods")
|
Timber.tag(loggerTag.value).v("Request ${msg.transactionId} no common methods")
|
||||||
cancelRequest(existing, CancelCode.UnknownMethod)
|
cancelRequest(existing, CancelCode.UnknownMethod)
|
||||||
|
// Upon receipt of Alice’s m.key.verification.request message, if Bob’s device does not understand any of the methods,
|
||||||
|
// it should not cancel the request as one of his other devices may support the request.
|
||||||
|
|
||||||
|
// XXX How to o that??
|
||||||
|
// Instead, Bob’s device should tell Bob that no supported method was found, and allow him to manually reject the request.
|
||||||
msg.deferred.complete(null)
|
msg.deferred.complete(null)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -1367,7 +1372,7 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
|
|
||||||
private fun getMethodAgreement(
|
private fun getMethodAgreement(
|
||||||
otherUserMethods: List<String>?,
|
otherUserMethods: List<String>?,
|
||||||
methods: List<VerificationMethod>,
|
myMethods: List<VerificationMethod>,
|
||||||
): List<String> {
|
): List<String> {
|
||||||
if (otherUserMethods.isNullOrEmpty()) {
|
if (otherUserMethods.isNullOrEmpty()) {
|
||||||
return emptyList()
|
return emptyList()
|
||||||
@ -1375,18 +1380,18 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
|
|
||||||
val result = mutableSetOf<String>()
|
val result = mutableSetOf<String>()
|
||||||
|
|
||||||
if (VERIFICATION_METHOD_SAS in otherUserMethods && VerificationMethod.SAS in methods) {
|
if (VERIFICATION_METHOD_SAS in otherUserMethods && VerificationMethod.SAS in myMethods) {
|
||||||
// Other can do SAS and so do I
|
// Other can do SAS and so do I
|
||||||
result.add(VERIFICATION_METHOD_SAS)
|
result.add(VERIFICATION_METHOD_SAS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods || VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods) {
|
if (VERIFICATION_METHOD_RECIPROCATE in otherUserMethods) {
|
||||||
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in methods) {
|
if (VERIFICATION_METHOD_QR_CODE_SCAN in otherUserMethods && VerificationMethod.QR_CODE_SHOW in myMethods) {
|
||||||
// Other can Scan and I can show QR code
|
// Other can Scan and I can show QR code
|
||||||
result.add(VERIFICATION_METHOD_QR_CODE_SHOW)
|
result.add(VERIFICATION_METHOD_QR_CODE_SHOW)
|
||||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||||
}
|
}
|
||||||
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in methods) {
|
if (VERIFICATION_METHOD_QR_CODE_SHOW in otherUserMethods && VerificationMethod.QR_CODE_SCAN in myMethods) {
|
||||||
// Other can show and I can scan QR code
|
// Other can show and I can scan QR code
|
||||||
result.add(VERIFICATION_METHOD_QR_CODE_SCAN)
|
result.add(VERIFICATION_METHOD_QR_CODE_SCAN)
|
||||||
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
result.add(VERIFICATION_METHOD_RECIPROCATE)
|
||||||
@ -1400,7 +1405,11 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
return contains(VERIFICATION_METHOD_QR_CODE_SCAN) && contains(VERIFICATION_METHOD_RECIPROCATE)
|
return contains(VERIFICATION_METHOD_QR_CODE_SCAN) && contains(VERIFICATION_METHOD_RECIPROCATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun handleRequestAdd(msg: VerificationIntent.ActionRequestVerification) {
|
private fun List<String>.canShowCode(): Boolean {
|
||||||
|
return contains(VERIFICATION_METHOD_QR_CODE_SHOW) && contains(VERIFICATION_METHOD_RECIPROCATE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun handleActionRequestVerification(msg: VerificationIntent.ActionRequestVerification) {
|
||||||
val requestsForUser = pendingRequests.getOrPut(msg.otherUserId) { mutableListOf() }
|
val requestsForUser = pendingRequests.getOrPut(msg.otherUserId) { mutableListOf() }
|
||||||
// there can only be one active request per user, so cancel existing ones
|
// there can only be one active request per user, so cancel existing ones
|
||||||
requestsForUser.toList().forEach { existingRequest ->
|
requestsForUser.toList().forEach { existingRequest ->
|
||||||
@ -1410,8 +1419,6 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val validLocalId = LocalEcho.createLocalEchoId()
|
|
||||||
|
|
||||||
val methodValues = if (cryptoStore.getMyCrossSigningInfo()?.isTrusted().orFalse()) {
|
val methodValues = if (cryptoStore.getMyCrossSigningInfo()?.isTrusted().orFalse()) {
|
||||||
// Add reciprocate method if application declares it can scan or show QR codes
|
// Add reciprocate method if application declares it can scan or show QR codes
|
||||||
// Not sure if it ok to do that (?)
|
// Not sure if it ok to do that (?)
|
||||||
@ -1521,6 +1528,10 @@ internal class VerificationActor @AssistedInject constructor(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (matchingRequest.requestInfo?.methods?.canShowCode().orFalse() &&
|
||||||
|
msg.readyInfo.methods.canScanCode()) {
|
||||||
|
matchingRequest.qrCodeData = createQrCodeData(matchingRequest.requestId, msg.fromUser, msg.readyInfo.fromDevice)
|
||||||
|
}
|
||||||
matchingRequest.readyInfo = msg.readyInfo
|
matchingRequest.readyInfo = msg.readyInfo
|
||||||
matchingRequest.state = EVerificationState.Ready
|
matchingRequest.state = EVerificationState.Ready
|
||||||
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
dispatchUpdate(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -32,31 +32,3 @@ enum class EVerificationState {
|
|||||||
Cancelled,
|
Cancelled,
|
||||||
HandledByOtherSession
|
HandledByOtherSession
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove that
|
|
||||||
interface IVerificationRequest {
|
|
||||||
|
|
||||||
fun requestId(): String
|
|
||||||
|
|
||||||
fun incoming(): Boolean
|
|
||||||
fun otherUserId(): String
|
|
||||||
fun roomId(): String?
|
|
||||||
|
|
||||||
// target devices in case of to_device self verification
|
|
||||||
fun targetDevices(): List<String>?
|
|
||||||
|
|
||||||
fun state(): EVerificationState
|
|
||||||
fun ageLocalTs(): Long
|
|
||||||
|
|
||||||
fun isSasSupported(): Boolean
|
|
||||||
fun otherCanShowQrCode(): Boolean
|
|
||||||
fun otherCanScanQrCode(): Boolean
|
|
||||||
|
|
||||||
fun otherDeviceId(): String?
|
|
||||||
|
|
||||||
fun qrCodeText(): String?
|
|
||||||
|
|
||||||
fun isFinished(): Boolean = state() == EVerificationState.Cancelled || state() == EVerificationState.Done
|
|
||||||
|
|
||||||
fun cancelCode(): CancelCode?
|
|
||||||
}
|
|
@ -38,11 +38,10 @@ data class PendingVerificationRequest(
|
|||||||
// if available store here the qr code to show
|
// if available store here the qr code to show
|
||||||
val qrCodeText: String? = null,
|
val qrCodeText: String? = null,
|
||||||
val isSasSupported: Boolean = false,
|
val isSasSupported: Boolean = false,
|
||||||
val otherCanShowQrCode: Boolean = false,
|
val weShouldShowScanOption: Boolean = false,
|
||||||
val otherCanScanQrCode: Boolean = false,
|
val weShouldDisplayQRCode: Boolean = false,
|
||||||
|
|
||||||
) {
|
) {
|
||||||
// val isReady: Boolean = readyInfo != null
|
// val isReady: Boolean = readyInfo != null
|
||||||
//
|
//
|
||||||
// val isFinished: Boolean = isSuccessful || cancelConclusion != null
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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 org.matrix.android.sdk.internal.crypto.verification
|
||||||
|
|
||||||
|
import dagger.Lazy
|
||||||
|
import io.mockk.coEvery
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.channels.SendChannel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
|
||||||
|
import org.matrix.android.sdk.api.session.events.model.Content
|
||||||
|
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.room.model.message.MessageVerificationReadyContent
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
||||||
|
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||||
|
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
|
import org.matrix.android.sdk.internal.util.time.Clock
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
internal class VerificationActorHelper {
|
||||||
|
|
||||||
|
data class TestData(
|
||||||
|
val aliceActor: VerificationActor,
|
||||||
|
val bobActor: VerificationActor,
|
||||||
|
)
|
||||||
|
|
||||||
|
val actorAScope = CoroutineScope(SupervisorJob())
|
||||||
|
val actorBScope = CoroutineScope(SupervisorJob())
|
||||||
|
val transportScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
|
var bobChannel: SendChannel<VerificationIntent>? = null
|
||||||
|
var aliceChannel: SendChannel<VerificationIntent>? = null
|
||||||
|
|
||||||
|
fun setUpActors(): TestData {
|
||||||
|
val aliceTransportLayer = mockTransportTo(FakeCryptoStoreForVerification.aliceMxId) { bobChannel }
|
||||||
|
val bobTransportLayer = mockTransportTo(FakeCryptoStoreForVerification.bobMxId) { aliceChannel }
|
||||||
|
|
||||||
|
val aliceActor = fakeActor(
|
||||||
|
actorAScope,
|
||||||
|
FakeCryptoStoreForVerification.aliceMxId,
|
||||||
|
FakeCryptoStoreForVerification(StoreMode.Alice).instance,
|
||||||
|
aliceTransportLayer,
|
||||||
|
mockk<Lazy<CrossSigningService>> {
|
||||||
|
every {
|
||||||
|
get()
|
||||||
|
} returns mockk<CrossSigningService>(relaxed = true)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
aliceChannel = aliceActor.channel
|
||||||
|
|
||||||
|
val bobActor = fakeActor(
|
||||||
|
actorBScope,
|
||||||
|
FakeCryptoStoreForVerification.aliceMxId,
|
||||||
|
FakeCryptoStoreForVerification(StoreMode.Alice).instance,
|
||||||
|
bobTransportLayer,
|
||||||
|
mockk<Lazy<CrossSigningService>> {
|
||||||
|
every {
|
||||||
|
get()
|
||||||
|
} returns mockk<CrossSigningService>(relaxed = true)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
bobChannel = bobActor.channel
|
||||||
|
|
||||||
|
return TestData(
|
||||||
|
aliceActor,
|
||||||
|
bobActor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun mockTransportTo(fromUser: String, otherChannel: (() -> SendChannel<VerificationIntent>?)): VerificationTransportLayer {
|
||||||
|
return mockk<VerificationTransportLayer> {
|
||||||
|
coEvery { sendToOther(any(), any(), any()) } answers {
|
||||||
|
val request = firstArg<KotlinVerificationRequest>()
|
||||||
|
val type = secondArg<String>()
|
||||||
|
val info = thirdArg<VerificationInfo<*>>()
|
||||||
|
|
||||||
|
transportScope.launch(Dispatchers.IO) {
|
||||||
|
when (type) {
|
||||||
|
EventType.KEY_VERIFICATION_READY -> {
|
||||||
|
val readyContent = info.asValidObject()
|
||||||
|
otherChannel()?.send(
|
||||||
|
VerificationIntent.OnReadyReceived(
|
||||||
|
transactionId = request.requestId,
|
||||||
|
fromUser = fromUser,
|
||||||
|
viaRoom = request.roomId,
|
||||||
|
readyInfo = readyContent as ValidVerificationInfoReady,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
coEvery { sendInRoom(any(), any(), any(), any()) } answers {
|
||||||
|
val type = secondArg<String>()
|
||||||
|
val roomId = thirdArg<String>()
|
||||||
|
val content = arg<Content>(3)
|
||||||
|
|
||||||
|
val fakeEventId = UUID.randomUUID().toString()
|
||||||
|
transportScope.launch(Dispatchers.IO) {
|
||||||
|
when (type) {
|
||||||
|
EventType.MESSAGE -> {
|
||||||
|
val requestContent = content.toModel<MessageVerificationRequestContent>()?.copy(
|
||||||
|
transactionId = fakeEventId
|
||||||
|
)?.asValidObject()
|
||||||
|
otherChannel()?.send(
|
||||||
|
VerificationIntent.OnVerificationRequestReceived(
|
||||||
|
requestContent!!,
|
||||||
|
senderId = FakeCryptoStoreForVerification.aliceMxId,
|
||||||
|
roomId = roomId,
|
||||||
|
timeStamp = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
EventType.KEY_VERIFICATION_READY -> {
|
||||||
|
val readyContent = content.toModel<MessageVerificationReadyContent>()
|
||||||
|
?.asValidObject()
|
||||||
|
otherChannel()?.send(
|
||||||
|
VerificationIntent.OnReadyReceived(
|
||||||
|
transactionId = readyContent!!.transactionId,
|
||||||
|
fromUser = fromUser,
|
||||||
|
viaRoom = roomId,
|
||||||
|
readyInfo = readyContent,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fakeEventId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fakeActor(
|
||||||
|
scope: CoroutineScope,
|
||||||
|
userId: String,
|
||||||
|
cryptoStore: IMXCryptoStore,
|
||||||
|
transportLayer: VerificationTransportLayer,
|
||||||
|
crossSigningService: dagger.Lazy<CrossSigningService>,
|
||||||
|
): VerificationActor {
|
||||||
|
return VerificationActor(
|
||||||
|
scope,
|
||||||
|
// channel = channel,
|
||||||
|
clock = mockk<Clock> {
|
||||||
|
every { epochMillis() } returns System.currentTimeMillis()
|
||||||
|
},
|
||||||
|
myUserId = userId,
|
||||||
|
cryptoStore = cryptoStore,
|
||||||
|
secretShareManager = mockk<SecretShareManager> {},
|
||||||
|
transportLayer = transportLayer,
|
||||||
|
crossSigningService = crossSigningService,
|
||||||
|
setDeviceVerificationAction = SetDeviceVerificationAction(
|
||||||
|
cryptoStore = cryptoStore,
|
||||||
|
userId = userId,
|
||||||
|
defaultKeysBackupService = mockk {
|
||||||
|
coEvery { checkAndStartKeysBackup() } coAnswers { }
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -17,9 +17,7 @@
|
|||||||
package org.matrix.android.sdk.internal.crypto.verification.org.matrix.android.sdk.internal.crypto.verification
|
package org.matrix.android.sdk.internal.crypto.verification.org.matrix.android.sdk.internal.crypto.verification
|
||||||
|
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import io.mockk.coEvery
|
|
||||||
import io.mockk.every
|
import io.mockk.every
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.mockkStatic
|
import io.mockk.mockkStatic
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@ -27,44 +25,32 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.channels.SendChannel
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.amshove.kluent.fail
|
||||||
|
import org.amshove.kluent.internal.assertEquals
|
||||||
|
import org.amshove.kluent.internal.assertNotEquals
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.amshove.kluent.shouldNotBe
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.MXCrossSigningInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.IVerificationRequest
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||||
import org.matrix.android.sdk.api.session.events.model.Content
|
|
||||||
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.room.model.message.MessageVerificationReadyContent
|
|
||||||
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
|
|
||||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
|
||||||
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.FakeCryptoStoreForVerification
|
import org.matrix.android.sdk.internal.crypto.verification.FakeCryptoStoreForVerification
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.StoreMode
|
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationActor
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationActor
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationInfo
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationActorHelper
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationIntent
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationIntent
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationTransportLayer
|
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
class VerificationActorTest {
|
class VerificationActorTest {
|
||||||
|
|
||||||
val transportScope = CoroutineScope(SupervisorJob())
|
val transportScope = CoroutineScope(SupervisorJob())
|
||||||
val actorAScope = CoroutineScope(SupervisorJob())
|
// val actorAScope = CoroutineScope(SupervisorJob())
|
||||||
val actorBScope = CoroutineScope(SupervisorJob())
|
// val actorBScope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
@ -86,38 +72,10 @@ class VerificationActorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Request and accept`() = runTest {
|
fun `If ready both side should support sas and Qr show and scan`() = runTest {
|
||||||
var bobChannel: SendChannel<VerificationIntent>? = null
|
val testData = VerificationActorHelper().setUpActors()
|
||||||
var aliceChannel: SendChannel<VerificationIntent>? = null
|
val aliceActor = testData.aliceActor
|
||||||
|
val bobActor = testData.bobActor
|
||||||
val aliceTransportLayer = mockTransportTo(FakeCryptoStoreForVerification.aliceMxId) { bobChannel }
|
|
||||||
val bobTransportLayer = mockTransportTo(FakeCryptoStoreForVerification.bobMxId) { aliceChannel }
|
|
||||||
|
|
||||||
val aliceActor = fakeActor(
|
|
||||||
actorAScope,
|
|
||||||
FakeCryptoStoreForVerification.aliceMxId,
|
|
||||||
FakeCryptoStoreForVerification(StoreMode.Alice).instance,
|
|
||||||
aliceTransportLayer,
|
|
||||||
mockk<dagger.Lazy<CrossSigningService>> {
|
|
||||||
every {
|
|
||||||
get()
|
|
||||||
} returns mockk<CrossSigningService>(relaxed = true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
aliceChannel = aliceActor.channel
|
|
||||||
|
|
||||||
val bobActor = fakeActor(
|
|
||||||
actorBScope,
|
|
||||||
FakeCryptoStoreForVerification.aliceMxId,
|
|
||||||
FakeCryptoStoreForVerification(StoreMode.Alice).instance,
|
|
||||||
bobTransportLayer,
|
|
||||||
mockk<dagger.Lazy<CrossSigningService>> {
|
|
||||||
every {
|
|
||||||
get()
|
|
||||||
} returns mockk<CrossSigningService>(relaxed = true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
bobChannel = bobActor.channel
|
|
||||||
|
|
||||||
val completableDeferred = CompletableDeferred<PendingVerificationRequest>()
|
val completableDeferred = CompletableDeferred<PendingVerificationRequest>()
|
||||||
|
|
||||||
@ -130,23 +88,14 @@ class VerificationActorTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
awaitDeferrable<PendingVerificationRequest> {
|
aliceActor.requestVerification(listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SHOW, VerificationMethod.QR_CODE_SCAN))
|
||||||
aliceActor.send(
|
|
||||||
VerificationIntent.ActionRequestVerification(
|
|
||||||
otherUserId = FakeCryptoStoreForVerification.bobMxId,
|
|
||||||
roomId = "aRoom",
|
|
||||||
methods = listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SHOW, VerificationMethod.QR_CODE_SCAN),
|
|
||||||
deferred = it
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val bobIncomingRequest = completableDeferred.await()
|
val bobIncomingRequest = completableDeferred.await()
|
||||||
bobIncomingRequest.state shouldBeEqualTo EVerificationState.Requested
|
bobIncomingRequest.state shouldBeEqualTo EVerificationState.Requested
|
||||||
|
|
||||||
val aliceReadied = CompletableDeferred<PendingVerificationRequest>()
|
val aliceReadied = CompletableDeferred<PendingVerificationRequest>()
|
||||||
|
|
||||||
val theJob = transportScope.launch {
|
transportScope.launch {
|
||||||
aliceActor.eventFlow.collect {
|
aliceActor.eventFlow.collect {
|
||||||
if (it is VerificationEvent.RequestUpdated && it.request.state == EVerificationState.Ready) {
|
if (it is VerificationEvent.RequestUpdated && it.request.state == EVerificationState.Ready) {
|
||||||
aliceReadied.complete(it.request)
|
aliceReadied.complete(it.request)
|
||||||
@ -156,7 +105,7 @@ class VerificationActorTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// test ready
|
// test ready
|
||||||
awaitDeferrable<PendingVerificationRequest?> {
|
val bobReadied = awaitDeferrable<PendingVerificationRequest?> {
|
||||||
bobActor.send(
|
bobActor.send(
|
||||||
VerificationIntent.ActionReadyRequest(
|
VerificationIntent.ActionReadyRequest(
|
||||||
bobIncomingRequest.transactionId,
|
bobIncomingRequest.transactionId,
|
||||||
@ -168,11 +117,143 @@ class VerificationActorTest {
|
|||||||
|
|
||||||
val readiedAliceSide = aliceReadied.await()
|
val readiedAliceSide = aliceReadied.await()
|
||||||
|
|
||||||
println("transporte scope active? ${transportScope.isActive}")
|
|
||||||
println("the job? ${theJob.isActive}")
|
|
||||||
|
|
||||||
readiedAliceSide.isSasSupported shouldBeEqualTo true
|
readiedAliceSide.isSasSupported shouldBeEqualTo true
|
||||||
readiedAliceSide.otherCanScanQrCode shouldBeEqualTo true
|
readiedAliceSide.weShouldDisplayQRCode shouldBeEqualTo true
|
||||||
|
|
||||||
|
bobReadied shouldNotBe null
|
||||||
|
bobReadied!!.isSasSupported shouldBeEqualTo true
|
||||||
|
bobReadied.weShouldDisplayQRCode shouldBeEqualTo true
|
||||||
|
|
||||||
|
bobReadied.qrCodeText shouldNotBe null
|
||||||
|
readiedAliceSide.qrCodeText shouldNotBe null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test alice can show but not scan QR`() = runTest {
|
||||||
|
val testData = VerificationActorHelper().setUpActors()
|
||||||
|
val aliceActor = testData.aliceActor
|
||||||
|
val bobActor = testData.bobActor
|
||||||
|
|
||||||
|
println("Alice sends a request")
|
||||||
|
val outgoingRequest = aliceActor.requestVerification(
|
||||||
|
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SHOW)
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for bob to get it
|
||||||
|
println("Wait for bob to get it")
|
||||||
|
waitForBobToSeeIncomingRequest(bobActor, outgoingRequest)
|
||||||
|
|
||||||
|
println("let bob ready it")
|
||||||
|
val bobReady = bobActor.readyVerification(
|
||||||
|
outgoingRequest.transactionId,
|
||||||
|
listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW)
|
||||||
|
)
|
||||||
|
|
||||||
|
println("Wait for alice to get the ready")
|
||||||
|
retryUntil {
|
||||||
|
awaitDeferrable<PendingVerificationRequest?> {
|
||||||
|
aliceActor.send(VerificationIntent.GetExistingRequest(outgoingRequest.transactionId, outgoingRequest.otherUserId, it))
|
||||||
|
}?.state == EVerificationState.Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
val aliceReady = awaitDeferrable<PendingVerificationRequest?> {
|
||||||
|
aliceActor.send(VerificationIntent.GetExistingRequest(outgoingRequest.transactionId, outgoingRequest.otherUserId, it))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
aliceReady.isSasSupported shouldBeEqualTo bobReady.isSasSupported
|
||||||
|
|
||||||
|
// alice can't scan so there should not be option to do so
|
||||||
|
assertEquals("Alice should not show scan option", false, aliceReady.weShouldShowScanOption)
|
||||||
|
assertEquals("Alice should show QR as bob can scan", true, aliceReady.weShouldDisplayQRCode)
|
||||||
|
|
||||||
|
assertEquals("Bob should be able to scan", true, bobReady.weShouldShowScanOption)
|
||||||
|
assertEquals("Bob should not show QR as alice can scan", false, bobReady.weShouldDisplayQRCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test bob can show but not scan QR`() = runTest {
|
||||||
|
val testData = VerificationActorHelper().setUpActors()
|
||||||
|
val aliceActor = testData.aliceActor
|
||||||
|
val bobActor = testData.bobActor
|
||||||
|
|
||||||
|
println("Alice sends a request")
|
||||||
|
val outgoingRequest = aliceActor.requestVerification(
|
||||||
|
listOf(VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW)
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for bob to get it
|
||||||
|
println("Wait for bob to get it")
|
||||||
|
waitForBobToSeeIncomingRequest(bobActor, outgoingRequest)
|
||||||
|
|
||||||
|
println("let bob ready it")
|
||||||
|
val bobReady = bobActor.readyVerification(
|
||||||
|
outgoingRequest.transactionId,
|
||||||
|
listOf(VerificationMethod.QR_CODE_SHOW)
|
||||||
|
)
|
||||||
|
|
||||||
|
println("Wait for alice to get the ready")
|
||||||
|
retryUntil {
|
||||||
|
awaitDeferrable<PendingVerificationRequest?> {
|
||||||
|
aliceActor.send(VerificationIntent.GetExistingRequest(outgoingRequest.transactionId, outgoingRequest.otherUserId, it))
|
||||||
|
}?.state == EVerificationState.Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
val aliceReady = awaitDeferrable<PendingVerificationRequest?> {
|
||||||
|
aliceActor.send(VerificationIntent.GetExistingRequest(outgoingRequest.transactionId, outgoingRequest.otherUserId, it))
|
||||||
|
}!!
|
||||||
|
|
||||||
|
assertEquals("Alice sas is not supported", false, aliceReady.isSasSupported)
|
||||||
|
aliceReady.isSasSupported shouldBeEqualTo bobReady.isSasSupported
|
||||||
|
|
||||||
|
// alice can't scan so there should not be option to do so
|
||||||
|
assertEquals("Alice should show scan option", true, aliceReady.weShouldShowScanOption)
|
||||||
|
assertEquals("Alice QR data should be null", null, aliceReady.qrCodeText)
|
||||||
|
assertEquals("Alice should not show QR as bob can scan", false, aliceReady.weShouldDisplayQRCode)
|
||||||
|
|
||||||
|
assertEquals("Bob should not should not show cam option as it can't scan", false, bobReady.weShouldShowScanOption)
|
||||||
|
assertNotEquals("Bob QR data should be there", null, bobReady.qrCodeText)
|
||||||
|
assertEquals("Bob should show QR as alice can scan", true, bobReady.weShouldDisplayQRCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun VerificationActor.requestVerification(methods: List<VerificationMethod>): PendingVerificationRequest {
|
||||||
|
return awaitDeferrable<PendingVerificationRequest> {
|
||||||
|
send(
|
||||||
|
VerificationIntent.ActionRequestVerification(
|
||||||
|
otherUserId = FakeCryptoStoreForVerification.bobMxId,
|
||||||
|
roomId = "aRoom",
|
||||||
|
methods = methods,
|
||||||
|
deferred = it
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun waitForBobToSeeIncomingRequest(bobActor: VerificationActor, aliceOutgoing: PendingVerificationRequest) {
|
||||||
|
retryUntil {
|
||||||
|
awaitDeferrable<PendingVerificationRequest?> {
|
||||||
|
bobActor.send(
|
||||||
|
VerificationIntent.GetExistingRequest(
|
||||||
|
aliceOutgoing.transactionId,
|
||||||
|
FakeCryptoStoreForVerification.aliceMxId, it
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}?.state == EVerificationState.Requested
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val backoff = listOf(500L, 1_000L, 1_000L, 3_000L, 3_000L, 5_000L)
|
||||||
|
|
||||||
|
private suspend fun retryUntil(condition: suspend (() -> Boolean)) {
|
||||||
|
var tryCount = 0
|
||||||
|
while (!condition()) {
|
||||||
|
if (tryCount >= backoff.size) {
|
||||||
|
fail("Retry Until Fialed")
|
||||||
|
}
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
|
delay(backoff[tryCount])
|
||||||
|
}
|
||||||
|
tryCount++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun <T> awaitDeferrable(block: suspend ((CompletableDeferred<T>) -> Unit)): T {
|
private suspend fun <T> awaitDeferrable(block: suspend ((CompletableDeferred<T>) -> Unit)): T {
|
||||||
@ -181,113 +262,85 @@ class VerificationActorTest {
|
|||||||
return deferred.await()
|
return deferred.await()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mockTransportTo(fromUser: String, otherChannel: (() -> SendChannel<VerificationIntent>?)): VerificationTransportLayer {
|
private suspend fun VerificationActor.readyVerification(transactionId: String, methods: List<VerificationMethod>): PendingVerificationRequest {
|
||||||
return mockk<VerificationTransportLayer> {
|
return awaitDeferrable<PendingVerificationRequest?> {
|
||||||
coEvery { sendToOther(any(), any(), any()) } answers {
|
send(
|
||||||
val request = firstArg<IVerificationRequest>()
|
VerificationIntent.ActionReadyRequest(
|
||||||
val type = secondArg<String>()
|
transactionId,
|
||||||
val info = thirdArg<VerificationInfo<*>>()
|
methods = methods,
|
||||||
|
it
|
||||||
transportScope.launch(Dispatchers.IO) {
|
|
||||||
when (type) {
|
|
||||||
EventType.KEY_VERIFICATION_READY -> {
|
|
||||||
val readyContent = info.asValidObject()
|
|
||||||
otherChannel()?.send(
|
|
||||||
VerificationIntent.OnReadyReceived(
|
|
||||||
transactionId = request.requestId(),
|
|
||||||
fromUser = fromUser,
|
|
||||||
viaRoom = request.roomId(),
|
|
||||||
readyInfo = readyContent as ValidVerificationInfoReady,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}!!
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coEvery { sendInRoom(any(), any(), any(), any()) } answers {
|
|
||||||
val type = secondArg<String>()
|
|
||||||
val roomId = thirdArg<String>()
|
|
||||||
val content = arg<Content>(3)
|
|
||||||
|
|
||||||
val fakeEventId = UUID.randomUUID().toString()
|
|
||||||
transportScope.launch(Dispatchers.IO) {
|
|
||||||
when (type) {
|
|
||||||
EventType.MESSAGE -> {
|
|
||||||
val requestContent = content.toModel<MessageVerificationRequestContent>()?.copy(
|
|
||||||
transactionId = fakeEventId
|
|
||||||
)?.asValidObject()
|
|
||||||
otherChannel()?.send(
|
|
||||||
VerificationIntent.OnVerificationRequestReceived(
|
|
||||||
requestContent!!,
|
|
||||||
senderId = FakeCryptoStoreForVerification.aliceMxId,
|
|
||||||
roomId = roomId,
|
|
||||||
timeStamp = 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
EventType.KEY_VERIFICATION_READY -> {
|
|
||||||
val readyContent = content.toModel<MessageVerificationReadyContent>()
|
|
||||||
?.asValidObject()
|
|
||||||
otherChannel()?.send(
|
|
||||||
VerificationIntent.OnReadyReceived(
|
|
||||||
transactionId = readyContent!!.transactionId,
|
|
||||||
fromUser = fromUser,
|
|
||||||
viaRoom = roomId,
|
|
||||||
readyInfo = readyContent,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fakeEventId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
// @Test
|
||||||
fun `Every testing`() {
|
// fun `Every testing`() {
|
||||||
val mockStore = mockk<IMXCryptoStore>()
|
// val mockStore = mockk<IMXCryptoStore>()
|
||||||
every { mockStore.getDeviceId() } returns "A"
|
// every { mockStore.getDeviceId() } returns "A"
|
||||||
println("every ${mockStore.getDeviceId()}")
|
// println("every ${mockStore.getDeviceId()}")
|
||||||
every { mockStore.getDeviceId() } returns "B"
|
// every { mockStore.getDeviceId() } returns "B"
|
||||||
println("every ${mockStore.getDeviceId()}")
|
// println("every ${mockStore.getDeviceId()}")
|
||||||
|
//
|
||||||
|
// every { mockStore.getDeviceId() } returns "A"
|
||||||
|
// every { mockStore.getDeviceId() } returns "B"
|
||||||
|
// println("every ${mockStore.getDeviceId()}")
|
||||||
|
//
|
||||||
|
// every { mockStore.getCrossSigningInfo(any()) } returns null
|
||||||
|
// every { mockStore.getCrossSigningInfo("alice") } returns MXCrossSigningInfo("alice", emptyList(), false)
|
||||||
|
//
|
||||||
|
// println("XS ${mockStore.getCrossSigningInfo("alice")}")
|
||||||
|
// println("XS ${mockStore.getCrossSigningInfo("bob")}")
|
||||||
|
// }
|
||||||
|
|
||||||
every { mockStore.getDeviceId() } returns "A"
|
// @Test
|
||||||
every { mockStore.getDeviceId() } returns "B"
|
// fun `Basic channel test`() {
|
||||||
println("every ${mockStore.getDeviceId()}")
|
// // val sharedFlow = MutableSharedFlow<Int>(replay = 0, extraBufferCapacity = 2, BufferOverflow.DROP_OLDEST)
|
||||||
|
// val sharedFlow = MutableSharedFlow<Int>(replay = 0)//, extraBufferCapacity = 0, BufferOverflow.DROP_OLDEST)
|
||||||
every { mockStore.getCrossSigningInfo(any()) } returns null
|
//
|
||||||
every { mockStore.getCrossSigningInfo("alice") } returns MXCrossSigningInfo("alice", emptyList(), false)
|
// val scope = CoroutineScope(SupervisorJob())
|
||||||
|
// val deferred = CompletableDeferred<Unit>()
|
||||||
println("XS ${mockStore.getCrossSigningInfo("alice")}")
|
// val listener = scope.launch {
|
||||||
println("XS ${mockStore.getCrossSigningInfo("bob")}")
|
// sharedFlow.onEach {
|
||||||
}
|
// println("L1 : Just collected $it")
|
||||||
|
// delay(1000)
|
||||||
private fun fakeActor(
|
// println("L1 : Just processed $it")
|
||||||
scope: CoroutineScope,
|
// if (it == 2) {
|
||||||
userId: String,
|
// deferred.complete(Unit)
|
||||||
cryptoStore: IMXCryptoStore,
|
// }
|
||||||
transportLayer: VerificationTransportLayer,
|
// }.launchIn(scope)
|
||||||
crossSigningService: dagger.Lazy<CrossSigningService>,
|
// }
|
||||||
): VerificationActor {
|
//
|
||||||
return VerificationActor(
|
// // scope.launch {
|
||||||
scope,
|
// // delay(700)
|
||||||
// channel = channel,
|
// println("Pre Emit 1")
|
||||||
clock = mockk<Clock> {
|
// sharedFlow.tryEmit(1)
|
||||||
every { epochMillis() } returns System.currentTimeMillis()
|
// println("Emited 1")
|
||||||
},
|
// sharedFlow.tryEmit(2)
|
||||||
myUserId = userId,
|
// println("Emited 2")
|
||||||
cryptoStore = cryptoStore,
|
// // }
|
||||||
secretShareManager = mockk<SecretShareManager> {},
|
//
|
||||||
transportLayer = transportLayer,
|
// // runBlocking {
|
||||||
crossSigningService = crossSigningService,
|
// // deferred.await()
|
||||||
setDeviceVerificationAction = SetDeviceVerificationAction(
|
// // }
|
||||||
cryptoStore = cryptoStore,
|
//
|
||||||
userId = userId,
|
// sharedFlow.onEach {
|
||||||
defaultKeysBackupService = mockk {
|
// println("L2: Just collected $it")
|
||||||
coEvery { checkAndStartKeysBackup() } coAnswers { }
|
// delay(1000)
|
||||||
}
|
// println("L2: Just processed $it")
|
||||||
)
|
// }.launchIn(scope)
|
||||||
)
|
//
|
||||||
}
|
//
|
||||||
|
// runBlocking {
|
||||||
|
// deferred.await()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val now = System.currentTimeMillis()
|
||||||
|
// println("Just give some time for execution")
|
||||||
|
// val job = scope.launch { delay(10_000) }
|
||||||
|
// runBlocking {
|
||||||
|
// job.join()
|
||||||
|
// }
|
||||||
|
// println("enough ${System.currentTimeMillis() - now}")
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,7 @@ class UserVerificationController @Inject constructor(
|
|||||||
notice(scanCodeInstructions.toEpoxyCharSequence())
|
notice(scanCodeInstructions.toEpoxyCharSequence())
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.otherCanScanQrCode && !request.qrCodeText.isNullOrEmpty()) {
|
if (request.weShouldDisplayQRCode && !request.qrCodeText.isNullOrEmpty()) {
|
||||||
bottomSheetVerificationQrCodeItem {
|
bottomSheetVerificationQrCodeItem {
|
||||||
id("qr")
|
id("qr")
|
||||||
data(request.qrCodeText!!)
|
data(request.qrCodeText!!)
|
||||||
@ -198,7 +198,7 @@ class UserVerificationController @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.otherCanShowQrCode) {
|
if (request.weShouldShowScanOption) {
|
||||||
bottomSheetVerificationActionItem {
|
bottomSheetVerificationActionItem {
|
||||||
id("openCamera")
|
id("openCamera")
|
||||||
title(scanOtherCodeTitle)
|
title(scanOtherCodeTitle)
|
||||||
|
Loading…
Reference in New Issue
Block a user