mirror of
https://github.com/element-hq/element-android
synced 2024-11-24 02:15:35 +03:00
Notify other devices of acceptance of verification request
This commit is contained in:
parent
f7303789a0
commit
123ad87eda
7 changed files with 95 additions and 2 deletions
1
changelog.d/5724.sdk
Normal file
1
changelog.d/5724.sdk
Normal file
|
@ -0,0 +1 @@
|
||||||
|
- Notifies other devices when a verification request sent from an Android device is accepted.`
|
|
@ -27,11 +27,13 @@ import org.matrix.android.sdk.api.auth.UIABaseAuth
|
||||||
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
|
||||||
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
import org.matrix.android.sdk.api.auth.UserPasswordAuth
|
||||||
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
import org.matrix.android.sdk.api.auth.registration.RegistrationFlowResponse
|
||||||
|
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
|
||||||
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.VerificationMethod
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
|
||||||
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
|
||||||
import org.matrix.android.sdk.common.CommonTestHelper
|
import org.matrix.android.sdk.common.CommonTestHelper
|
||||||
import org.matrix.android.sdk.common.CryptoTestHelper
|
import org.matrix.android.sdk.common.CryptoTestHelper
|
||||||
|
import org.matrix.android.sdk.common.SessionTestParams
|
||||||
import org.matrix.android.sdk.common.TestConstants
|
import org.matrix.android.sdk.common.TestConstants
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
import kotlin.coroutines.Continuation
|
import kotlin.coroutines.Continuation
|
||||||
|
@ -252,4 +254,48 @@ class VerificationTest : InstrumentedTest {
|
||||||
|
|
||||||
cryptoTestData.cleanUp(testHelper)
|
cryptoTestData.cleanUp(testHelper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_selfVerificationAcceptedCancelsItForOtherSessions() {
|
||||||
|
val defaultSessionParams = SessionTestParams(true)
|
||||||
|
val testHelper = CommonTestHelper(context())
|
||||||
|
|
||||||
|
val aliceSessionToVerify = testHelper.createAccount(TestConstants.USER_ALICE, defaultSessionParams)
|
||||||
|
val aliceSessionThatVerifies = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
||||||
|
val aliceSessionThatReceivesCanceledEvent = testHelper.logIntoAccount(aliceSessionToVerify.myUserId, TestConstants.PASSWORD, defaultSessionParams)
|
||||||
|
|
||||||
|
val verificationMethods = listOf(VerificationMethod.SAS, VerificationMethod.QR_CODE_SCAN, VerificationMethod.QR_CODE_SHOW)
|
||||||
|
|
||||||
|
val serviceOfVerified = aliceSessionToVerify.cryptoService().verificationService()
|
||||||
|
val serviceOfVerifier = aliceSessionThatVerifies.cryptoService().verificationService()
|
||||||
|
val serviceOfUserWhoReceivesCancellation = aliceSessionThatReceivesCanceledEvent.cryptoService().verificationService()
|
||||||
|
|
||||||
|
serviceOfVerifier.addListener(object : VerificationService.Listener {
|
||||||
|
override fun verificationRequestCreated(pr: PendingVerificationRequest) {
|
||||||
|
// Accept verification request
|
||||||
|
serviceOfVerifier.readyPendingVerification(
|
||||||
|
verificationMethods,
|
||||||
|
pr.otherUserId,
|
||||||
|
pr.transactionId!!,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
serviceOfVerified.requestKeyVerification(
|
||||||
|
methods = verificationMethods,
|
||||||
|
otherUserId = aliceSessionToVerify.myUserId,
|
||||||
|
otherDevices = listOfNotNull(aliceSessionThatVerifies.sessionParams.deviceId, aliceSessionThatReceivesCanceledEvent.sessionParams.deviceId),
|
||||||
|
)
|
||||||
|
|
||||||
|
testHelper.waitWithLatch { latch ->
|
||||||
|
testHelper.retryPeriodicallyWithLatch(latch) {
|
||||||
|
val requests = serviceOfUserWhoReceivesCancellation.getExistingVerificationRequests(aliceSessionToVerify.myUserId)
|
||||||
|
requests.any { it.cancelConclusion == CancelCode.AcceptedByAnotherDevice }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testHelper.signOutAndClose(aliceSessionToVerify)
|
||||||
|
testHelper.signOutAndClose(aliceSessionThatVerifies)
|
||||||
|
testHelper.signOutAndClose(aliceSessionThatReceivesCanceledEvent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ enum class CancelCode(val value: String, val humanReadable: String) {
|
||||||
MismatchedKeys("m.key_mismatch", "Key mismatch"),
|
MismatchedKeys("m.key_mismatch", "Key mismatch"),
|
||||||
UserError("m.user_error", "User error"),
|
UserError("m.user_error", "User error"),
|
||||||
MismatchedUser("m.user_mismatch", "User mismatch"),
|
MismatchedUser("m.user_mismatch", "User mismatch"),
|
||||||
QrCodeInvalid("m.qr_code.invalid", "Invalid QR code")
|
QrCodeInvalid("m.qr_code.invalid", "Invalid QR code"),
|
||||||
|
AcceptedByAnotherDevice("m.accepted", "Verification request accepted by another device")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun safeValueOf(code: String?): CancelCode {
|
fun safeValueOf(code: String?): CancelCode {
|
||||||
|
|
|
@ -942,6 +942,22 @@ internal class DefaultVerificationService @Inject constructor(
|
||||||
readyInfo = readyReq
|
readyInfo = readyReq
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
notifyOthersOfAcceptance(readyReq.transactionId, readyReq.fromDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of device ids excluding the current one.
|
||||||
|
*/
|
||||||
|
private fun getMyOtherDeviceIds(): List<String> = cryptoStore.getUserDevices(userId)?.keys?.filter { it != deviceId }.orEmpty()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies other devices that the current verification transaction is being handled by [acceptedByDeviceId].
|
||||||
|
*/
|
||||||
|
private fun notifyOthersOfAcceptance(transactionId: String, acceptedByDeviceId: String) {
|
||||||
|
val deviceIds = getMyOtherDeviceIds().filter { it != acceptedByDeviceId }
|
||||||
|
val transport = verificationTransportToDeviceFactory.createTransport(null)
|
||||||
|
transport.cancelTransaction(transactionId, userId, deviceIds, CancelCode.AcceptedByAnotherDevice)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
private fun createQrCodeData(requestId: String?, otherUserId: String, otherDeviceId: String?): QrCodeData? {
|
||||||
|
|
|
@ -49,6 +49,11 @@ internal interface VerificationTransport {
|
||||||
otherUserDeviceId: String?,
|
otherUserDeviceId: String?,
|
||||||
code: CancelCode)
|
code: CancelCode)
|
||||||
|
|
||||||
|
fun cancelTransaction(transactionId: String,
|
||||||
|
otherUserId: String,
|
||||||
|
otherUserDeviceIds: List<String>,
|
||||||
|
code: CancelCode)
|
||||||
|
|
||||||
fun done(transactionId: String,
|
fun done(transactionId: String,
|
||||||
onDone: (() -> Unit)?)
|
onDone: (() -> Unit)?)
|
||||||
|
|
||||||
|
|
|
@ -160,6 +160,9 @@ internal class VerificationTransportRoomMessage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceIds: List<String>, code: CancelCode) =
|
||||||
|
cancelTransaction(transactionId, otherUserId, null, code)
|
||||||
|
|
||||||
override fun done(transactionId: String,
|
override fun done(transactionId: String,
|
||||||
onDone: (() -> Unit)?) {
|
onDone: (() -> Unit)?) {
|
||||||
Timber.d("## SAS sending done for $transactionId")
|
Timber.d("## SAS sending done for $transactionId")
|
||||||
|
|
|
@ -193,6 +193,27 @@ internal class VerificationTransportToDevice(
|
||||||
.executeBy(taskExecutor)
|
.executeBy(taskExecutor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun cancelTransaction(transactionId: String, otherUserId: String, otherUserDeviceIds: List<String>, code: CancelCode) {
|
||||||
|
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||||
|
val cancelMessage = KeyVerificationCancel.create(transactionId, code)
|
||||||
|
val contentMap = MXUsersDevicesMap<Any>()
|
||||||
|
val messages = otherUserDeviceIds.associateWith { cancelMessage }
|
||||||
|
contentMap.setObjects(otherUserId, messages)
|
||||||
|
sendToDeviceTask
|
||||||
|
.configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap)) {
|
||||||
|
this.callback = object : MatrixCallback<Unit> {
|
||||||
|
override fun onSuccess(data: Unit) {
|
||||||
|
Timber.v("## SAS verification [$transactionId] canceled for reason ${code.value}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(failure: Throwable) {
|
||||||
|
Timber.e(failure, "## SAS verification [$transactionId] failed to cancel.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.executeBy(taskExecutor)
|
||||||
|
}
|
||||||
|
|
||||||
override fun createAccept(tid: String,
|
override fun createAccept(tid: String,
|
||||||
keyAgreementProtocol: String,
|
keyAgreementProtocol: String,
|
||||||
hash: String,
|
hash: String,
|
||||||
|
|
Loading…
Reference in a new issue