mirror of
https://github.com/vector-im/element-android.git
synced 2024-11-16 02:05:06 +08:00
Use workers to send verification messages
This commit is contained in:
parent
5b210df7c5
commit
f541661059
@ -106,6 +106,7 @@ dependencies {
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
|
||||
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
|
||||
implementation("com.google.guava:guava:28.2-jre")
|
||||
|
||||
// Network
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.6.2'
|
||||
|
@ -55,7 +55,7 @@ interface SasVerificationService {
|
||||
*/
|
||||
fun beginKeyVerification(method: String, userId: String, deviceID: String): String?
|
||||
|
||||
fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback<String>?): PendingVerificationRequest
|
||||
fun requestKeyVerificationInDMs(userId: String, roomId: String): PendingVerificationRequest
|
||||
|
||||
fun beginKeyVerificationInDMs(method: String,
|
||||
transactionId: String,
|
||||
|
@ -180,6 +180,9 @@ internal abstract class CryptoModule {
|
||||
@Binds
|
||||
abstract fun bindEncryptEventTask(encryptEventTask: DefaultEncryptEventTask): EncryptEventTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindSendVerificationMessageTask(sendDefaultSendVerificationMessageTask: DefaultSendVerificationMessageTask): SendVerificationMessageTask
|
||||
|
||||
@Binds
|
||||
abstract fun bindClaimOneTimeKeysForUsersDeviceTask(claimOneTimeKeysForUsersDevice: DefaultClaimOneTimeKeysForUsersDevice)
|
||||
: ClaimOneTimeKeysForUsersDeviceTask
|
||||
|
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater
|
||||
import im.vector.matrix.android.internal.session.room.send.SendResponse
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface RequestVerificationDMTask : Task<RequestVerificationDMTask.Params, SendResponse> {
|
||||
data class Params(
|
||||
val event: Event,
|
||||
val cryptoService: CryptoService
|
||||
)
|
||||
|
||||
fun createParamsAndLocalEcho(roomId: String, from: String, methods: List<String>, to: String, cryptoService: CryptoService): Params
|
||||
}
|
||||
|
||||
internal class DefaultRequestVerificationDMTask @Inject constructor(
|
||||
private val localEchoUpdater: LocalEchoUpdater,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val encryptEventTask: DefaultEncryptEventTask,
|
||||
private val monarchy: Monarchy,
|
||||
private val roomAPI: RoomAPI)
|
||||
: RequestVerificationDMTask {
|
||||
|
||||
override fun createParamsAndLocalEcho(roomId: String, from: String, methods: List<String>, to: String, cryptoService: CryptoService)
|
||||
: RequestVerificationDMTask.Params {
|
||||
val event = localEchoEventFactory.createVerificationRequest(roomId, from, to, methods)
|
||||
.also { localEchoEventFactory.saveLocalEcho(monarchy, it) }
|
||||
return RequestVerificationDMTask.Params(
|
||||
event,
|
||||
cryptoService
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun execute(params: RequestVerificationDMTask.Params): SendResponse {
|
||||
val event = handleEncryption(params)
|
||||
val localID = event.eventId!!
|
||||
|
||||
try {
|
||||
localEchoUpdater.updateSendState(localID, SendState.SENDING)
|
||||
val executeRequest = executeRequest<SendResponse> {
|
||||
apiCall = roomAPI.send(
|
||||
localID,
|
||||
roomId = event.roomId ?: "",
|
||||
content = event.content,
|
||||
eventType = event.type // message or room.encrypted
|
||||
)
|
||||
}
|
||||
localEchoUpdater.updateSendState(localID, SendState.SENT)
|
||||
return executeRequest
|
||||
} catch (e: Throwable) {
|
||||
localEchoUpdater.updateSendState(localID, SendState.UNDELIVERED)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun handleEncryption(params: RequestVerificationDMTask.Params): Event {
|
||||
val roomId = params.event.roomId ?: ""
|
||||
if (params.cryptoService.isRoomEncrypted(roomId)) {
|
||||
try {
|
||||
return encryptEventTask.execute(EncryptEventTask.Params(
|
||||
roomId,
|
||||
params.event,
|
||||
listOf("m.relates_to"),
|
||||
params.cryptoService
|
||||
))
|
||||
} catch (throwable: Throwable) {
|
||||
// We said it's ok to send verification request in clear
|
||||
}
|
||||
}
|
||||
return params.event
|
||||
}
|
||||
}
|
@ -15,64 +15,29 @@
|
||||
*/
|
||||
package im.vector.matrix.android.internal.crypto.tasks
|
||||
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Content
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||
import im.vector.matrix.android.api.session.events.model.UnsignedData
|
||||
import im.vector.matrix.android.api.session.room.send.SendState
|
||||
import im.vector.matrix.android.internal.di.UserId
|
||||
import im.vector.matrix.android.internal.network.executeRequest
|
||||
import im.vector.matrix.android.internal.session.room.RoomAPI
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoUpdater
|
||||
import im.vector.matrix.android.internal.session.room.send.SendResponse
|
||||
import im.vector.matrix.android.internal.task.Task
|
||||
import javax.inject.Inject
|
||||
|
||||
internal interface SendVerificationMessageTask : Task<SendVerificationMessageTask.Params, SendResponse> {
|
||||
internal interface SendVerificationMessageTask : Task<SendVerificationMessageTask.Params, String> {
|
||||
data class Params(
|
||||
val type: String,
|
||||
val event: Event,
|
||||
val cryptoService: CryptoService?
|
||||
)
|
||||
|
||||
fun createParamsAndLocalEcho(type: String,
|
||||
roomId: String,
|
||||
content: Content,
|
||||
cryptoService: CryptoService?) : Params
|
||||
}
|
||||
|
||||
internal class DefaultSendVerificationMessageTask @Inject constructor(
|
||||
private val localEchoUpdater: LocalEchoUpdater,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val encryptEventTask: DefaultEncryptEventTask,
|
||||
private val monarchy: Monarchy,
|
||||
@UserId private val userId: String,
|
||||
private val roomAPI: RoomAPI) : SendVerificationMessageTask {
|
||||
|
||||
override fun createParamsAndLocalEcho(type: String, roomId: String, content: Content, cryptoService: CryptoService?): SendVerificationMessageTask.Params {
|
||||
val localID = LocalEcho.createLocalEchoId()
|
||||
val event = Event(
|
||||
roomId = roomId,
|
||||
originServerTs = System.currentTimeMillis(),
|
||||
senderId = userId,
|
||||
eventId = localID,
|
||||
type = type,
|
||||
content = content,
|
||||
unsignedData = UnsignedData(age = null, transactionId = localID)
|
||||
).also {
|
||||
localEchoEventFactory.saveLocalEcho(monarchy, it)
|
||||
}
|
||||
return SendVerificationMessageTask.Params(
|
||||
type,
|
||||
event,
|
||||
cryptoService
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun execute(params: SendVerificationMessageTask.Params): SendResponse {
|
||||
override suspend fun execute(params: SendVerificationMessageTask.Params): String {
|
||||
val event = handleEncryption(params)
|
||||
val localID = event.eventId!!
|
||||
|
||||
@ -87,7 +52,7 @@ internal class DefaultSendVerificationMessageTask @Inject constructor(
|
||||
)
|
||||
}
|
||||
localEchoUpdater.updateSendState(localID, SendState.SENT)
|
||||
return executeRequest
|
||||
return executeRequest.eventId
|
||||
} catch (e: Throwable) {
|
||||
localEchoUpdater.updateSendState(localID, SendState.UNDELIVERED)
|
||||
throw e
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import dagger.Lazy
|
||||
@ -28,6 +29,7 @@ import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.crypto.sas.safeValueOf
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.LocalEcho
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.internal.crypto.DeviceListManager
|
||||
@ -37,12 +39,7 @@ import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultRequestVerificationDMTask
|
||||
import im.vector.matrix.android.internal.session.SessionScope
|
||||
import im.vector.matrix.android.internal.session.room.send.SendResponse
|
||||
import im.vector.matrix.android.internal.task.TaskConstraints
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
@ -55,16 +52,15 @@ import kotlin.collections.set
|
||||
|
||||
@SessionScope
|
||||
internal class DefaultSasVerificationService @Inject constructor(
|
||||
private val context: Context,
|
||||
private val credentials: Credentials,
|
||||
private val cryptoStore: IMXCryptoStore,
|
||||
private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
|
||||
private val deviceListManager: DeviceListManager,
|
||||
private val setDeviceVerificationAction: SetDeviceVerificationAction,
|
||||
private val requestVerificationDMTask: DefaultRequestVerificationDMTask,
|
||||
private val coroutineDispatchers: MatrixCoroutineDispatchers,
|
||||
private val sasTransportRoomMessageFactory: SasTransportRoomMessageFactory,
|
||||
private val sasTransportToDeviceFactory: SasTransportToDeviceFactory,
|
||||
private val taskExecutor: TaskExecutor
|
||||
private val sasTransportToDeviceFactory: SasTransportToDeviceFactory
|
||||
) : VerificationTransaction.Listener, SasVerificationService {
|
||||
|
||||
private val uiHandler = Handler(Looper.getMainLooper())
|
||||
@ -279,8 +275,9 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
if (startReq?.isValid()?.not() == true) {
|
||||
Timber.e("## received invalid verification request")
|
||||
if (startReq.transactionID != null) {
|
||||
sasTransportRoomMessageFactory.createTransport(event.roomId
|
||||
?: "", cryptoService, null).cancelTransaction(
|
||||
sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", null).cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
@ -291,11 +288,13 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
}
|
||||
|
||||
handleStart(otherUserId, startReq as VerificationInfoStart) {
|
||||
it.transport = sasTransportRoomMessageFactory.createTransport(event.roomId
|
||||
?: "", cryptoService, it)
|
||||
it.transport = sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", it)
|
||||
}?.let {
|
||||
sasTransportRoomMessageFactory.createTransport(event.roomId
|
||||
?: "", cryptoService, null).cancelTransaction(
|
||||
sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", event.roomId
|
||||
?: "", null).cancelTransaction(
|
||||
startReq.transactionID ?: "",
|
||||
otherUserId!!,
|
||||
startReq.fromDevice ?: event.getSenderKey()!!,
|
||||
@ -693,58 +692,38 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback<String>?)
|
||||
override fun requestKeyVerificationInDMs(userId: String, roomId: String)
|
||||
: PendingVerificationRequest {
|
||||
Timber.i("## SAS Requesting verification to user: $userId in room $roomId")
|
||||
|
||||
val requestsForUser = pendingRequests[userId]
|
||||
?: ArrayList<PendingVerificationRequest>().also {
|
||||
pendingRequests[userId] = it
|
||||
}
|
||||
|
||||
val params = requestVerificationDMTask.createParamsAndLocalEcho(
|
||||
roomId = roomId,
|
||||
from = credentials.deviceId ?: "",
|
||||
methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
||||
to = userId,
|
||||
cryptoService = cryptoService
|
||||
)
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", roomId, null)
|
||||
|
||||
val localID = LocalEcho.createLocalEchoId()
|
||||
|
||||
val verificationRequest = PendingVerificationRequest(
|
||||
ageLocalTs = System.currentTimeMillis(),
|
||||
isIncoming = false,
|
||||
localID = params.event.eventId ?: "",
|
||||
localID = localID,
|
||||
otherUserId = userId
|
||||
)
|
||||
|
||||
transport.sendVerificationRequest(localID, userId, roomId) { syncedId, info ->
|
||||
// We need to update with the syncedID
|
||||
updatePendingRequest(verificationRequest.copy(
|
||||
transactionId = syncedId,
|
||||
requestInfo = info
|
||||
))
|
||||
}
|
||||
|
||||
requestsForUser.add(verificationRequest)
|
||||
dispatchRequestAdded(verificationRequest)
|
||||
|
||||
requestVerificationDMTask.configureWith(
|
||||
requestVerificationDMTask.createParamsAndLocalEcho(
|
||||
roomId = roomId,
|
||||
from = credentials.deviceId ?: "",
|
||||
methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS),
|
||||
to = userId,
|
||||
cryptoService = cryptoService
|
||||
)
|
||||
) {
|
||||
this.callback = object : MatrixCallback<SendResponse> {
|
||||
override fun onSuccess(data: SendResponse) {
|
||||
params.event.getClearContent().toModel<MessageVerificationRequestContent>()?.let {
|
||||
updatePendingRequest(verificationRequest.copy(
|
||||
transactionId = data.eventId,
|
||||
requestInfo = it
|
||||
))
|
||||
}
|
||||
callback?.onSuccess(data.eventId)
|
||||
}
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
callback?.onFailure(failure)
|
||||
}
|
||||
}
|
||||
constraints = TaskConstraints(true)
|
||||
retryCount = 3
|
||||
}.executeBy(taskExecutor)
|
||||
|
||||
return verificationRequest
|
||||
}
|
||||
|
||||
@ -776,7 +755,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
transactionId,
|
||||
otherUserId,
|
||||
otherDeviceId)
|
||||
tx.transport = sasTransportRoomMessageFactory.createTransport(roomId, cryptoService, tx)
|
||||
tx.transport = sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", roomId, tx)
|
||||
addTransaction(tx)
|
||||
|
||||
tx.start()
|
||||
@ -790,7 +770,8 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
// Let's find the related request
|
||||
getExistingVerificationRequest(otherUserId)?.find { it.transactionId == transactionId }?.let {
|
||||
// we need to send a ready event, with matching methods
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(roomId, cryptoService, null)
|
||||
val transport = sasTransportRoomMessageFactory.createTransport(context, credentials.userId, credentials.deviceId
|
||||
?: "", roomId, null)
|
||||
val methods = it.requestInfo?.methods?.intersect(listOf(KeyVerificationStart.VERIF_METHOD_SAS))?.toList()
|
||||
if (methods.isNullOrEmpty()) {
|
||||
Timber.i("Cannot ready this request, no common methods found txId:$transactionId")
|
||||
@ -831,28 +812,4 @@ internal class DefaultSasVerificationService @Inject constructor(
|
||||
this.removeTransaction(tx.otherUserId, tx.transactionId)
|
||||
}
|
||||
}
|
||||
//
|
||||
// fun cancelTransaction(transactionId: String, userId: String, userDevice: String, code: CancelCode, roomId: String? = null) {
|
||||
// val cancelMessage = KeyVerificationCancel.create(transactionId, code)
|
||||
// val contentMap = MXUsersDevicesMap<Any>()
|
||||
// contentMap.setObject(userId, userDevice, cancelMessage)
|
||||
//
|
||||
// if (roomId != null) {
|
||||
//
|
||||
// } else {
|
||||
// sendToDeviceTask
|
||||
// .configureWith(SendToDeviceTask.Params(EventType.KEY_VERIFICATION_CANCEL, contentMap, transactionId)) {
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
||||
|
||||
/**
|
||||
* SAS verification can be performed using toDevice events or via DM.
|
||||
@ -33,6 +34,8 @@ internal interface SasTransport {
|
||||
onErrorReason: CancelCode,
|
||||
onDone: (() -> Unit)?)
|
||||
|
||||
fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String, callback: (String?, MessageVerificationRequestContent?) -> Unit)
|
||||
|
||||
fun cancelTransaction(transactionId: String, userId: String, userDevice: String, code: CancelCode)
|
||||
|
||||
fun done(transactionId: String)
|
||||
|
@ -15,33 +15,41 @@
|
||||
*/
|
||||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.work.*
|
||||
import com.zhuinden.monarchy.Monarchy
|
||||
import im.vector.matrix.android.R
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
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.toContent
|
||||
import im.vector.matrix.android.api.session.events.model.*
|
||||
import im.vector.matrix.android.api.session.room.model.message.*
|
||||
import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent
|
||||
import im.vector.matrix.android.internal.crypto.tasks.DefaultSendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.session.room.send.SendResponse
|
||||
import im.vector.matrix.android.internal.task.TaskConstraints
|
||||
import im.vector.matrix.android.internal.task.TaskExecutor
|
||||
import im.vector.matrix.android.internal.task.TaskThread
|
||||
import im.vector.matrix.android.internal.task.configureWith
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.KeyVerificationStart
|
||||
import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory
|
||||
import im.vector.matrix.android.internal.worker.WorkManagerUtil
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.util.*
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SasTransportRoomMessage(
|
||||
private val context: Context,
|
||||
private val userId: String,
|
||||
private val userDevice: String,
|
||||
private val roomId: String,
|
||||
private val cryptoService: CryptoService,
|
||||
private val tx: SASVerificationTransaction?,
|
||||
private val sendVerificationMessageTask: SendVerificationMessageTask,
|
||||
private val taskExecutor: TaskExecutor
|
||||
private val monarchy: Monarchy,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory,
|
||||
private val tx: SASVerificationTransaction?
|
||||
) : SasTransport {
|
||||
|
||||
private val listenerExecutor = Executors.newSingleThreadExecutor()
|
||||
|
||||
override fun sendToOther(type: String,
|
||||
verificationInfo: VerificationInfo,
|
||||
nextState: SasVerificationTxState,
|
||||
@ -49,83 +57,167 @@ internal class SasTransportRoomMessage(
|
||||
onDone: (() -> Unit)?) {
|
||||
Timber.d("## SAS sending msg type $type")
|
||||
Timber.v("## SAS sending msg info $verificationInfo")
|
||||
sendVerificationMessageTask.configureWith(
|
||||
sendVerificationMessageTask.createParamsAndLocalEcho(
|
||||
type,
|
||||
roomId,
|
||||
verificationInfo.toEventContent()!!,
|
||||
cryptoService
|
||||
)
|
||||
) {
|
||||
callbackThread = TaskThread.DM_VERIF
|
||||
executionThread = TaskThread.DM_VERIF
|
||||
constraints = TaskConstraints(true)
|
||||
callback = object : MatrixCallback<SendResponse> {
|
||||
override fun onSuccess(data: SendResponse) {
|
||||
if (onDone != null) {
|
||||
onDone()
|
||||
} else {
|
||||
tx?.state = nextState
|
||||
}
|
||||
}
|
||||
val event = createEventAndLocalEcho(
|
||||
type = type,
|
||||
roomId = roomId,
|
||||
content = verificationInfo.toEventContent()!!
|
||||
)
|
||||
|
||||
override fun onFailure(failure: Throwable) {
|
||||
Timber.e("## SAS verification [${tx?.transactionId}] failed to send toDevice in state : ${tx?.state}")
|
||||
tx?.cancel(onErrorReason)
|
||||
}
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
event = event
|
||||
))
|
||||
val enqueueInfo = enqueueSendWork(workerParams)
|
||||
|
||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||
// The callback is called while it is still Running ...
|
||||
|
||||
// Futures.addCallback(enqueueInfo.first.result, object : FutureCallback<Operation.State.SUCCESS> {
|
||||
// override fun onSuccess(result: Operation.State.SUCCESS?) {
|
||||
// if (onDone != null) {
|
||||
// onDone()
|
||||
// } else {
|
||||
// tx?.state = nextState
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun onFailure(t: Throwable) {
|
||||
// Timber.e("## SAS verification [${tx?.transactionId}] failed to send toDevice in state : ${tx?.state}, reason: ${t.localizedMessage}")
|
||||
// tx?.cancel(onErrorReason)
|
||||
// }
|
||||
// }, listenerExecutor)
|
||||
|
||||
val workLiveData = WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
|
||||
val observer = object : Observer<List<WorkInfo>> {
|
||||
override fun onChanged(workInfoList: List<WorkInfo>?) {
|
||||
workInfoList
|
||||
?.filter { it.state == WorkInfo.State.SUCCEEDED }
|
||||
?.firstOrNull { it.id == enqueueInfo.second }
|
||||
?.let { wInfo ->
|
||||
if (wInfo.outputData.getBoolean("failed", false)) {
|
||||
Timber.e("## SAS verification [${tx?.transactionId}] failed to send verification message in state : ${tx?.state}")
|
||||
tx?.cancel(onErrorReason)
|
||||
} else {
|
||||
if (onDone != null) {
|
||||
onDone()
|
||||
} else {
|
||||
tx?.state = nextState
|
||||
}
|
||||
}
|
||||
workLiveData.removeObserver(this)
|
||||
}
|
||||
}
|
||||
retryCount = 3
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
|
||||
// TODO listen to DB to get synced info
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
workLiveData.observeForever(observer)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String, callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||
val info = MessageVerificationRequestContent(
|
||||
body = context.getString(R.string.key_verification_request_fallback_message, userId),
|
||||
fromDevice = userDevice,
|
||||
toUserId = otherUserId,
|
||||
methods = listOf(KeyVerificationStart.VERIF_METHOD_SAS)
|
||||
)
|
||||
val content = info.toContent()
|
||||
|
||||
val event = createEventAndLocalEcho(
|
||||
localID,
|
||||
EventType.MESSAGE,
|
||||
roomId,
|
||||
content
|
||||
)
|
||||
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
event = event
|
||||
))
|
||||
|
||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
.setInputData(workerParams)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context)
|
||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||
.enqueue()
|
||||
|
||||
// I cannot just listen to the given work request, because when used in a uniqueWork,
|
||||
// The callback is called while it is still Running ...
|
||||
|
||||
val workLiveData = WorkManager.getInstance(context).getWorkInfosForUniqueWorkLiveData("${roomId}_VerificationWork")
|
||||
|
||||
val observer = object : Observer<List<WorkInfo>> {
|
||||
override fun onChanged(workInfoList: List<WorkInfo>?) {
|
||||
workInfoList
|
||||
?.filter { it.state == WorkInfo.State.SUCCEEDED }
|
||||
?.firstOrNull { it.id == workRequest.id }
|
||||
?.let { wInfo ->
|
||||
if (wInfo.outputData.getBoolean("failed", false)) {
|
||||
callback(null, null)
|
||||
} else if (wInfo.outputData.getString(localID) != null) {
|
||||
callback(wInfo.outputData.getString(localID), info)
|
||||
} else {
|
||||
callback(null, null)
|
||||
}
|
||||
workLiveData.removeObserver(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO listen to DB to get synced info
|
||||
GlobalScope.launch(Dispatchers.Main) {
|
||||
workLiveData.observeForever(observer)
|
||||
}
|
||||
}
|
||||
|
||||
override fun cancelTransaction(transactionId: String, userId: String, userDevice: String, code: CancelCode) {
|
||||
Timber.d("## SAS canceling transaction $transactionId for reason $code")
|
||||
sendVerificationMessageTask.configureWith(
|
||||
sendVerificationMessageTask.createParamsAndLocalEcho(
|
||||
EventType.KEY_VERIFICATION_CANCEL,
|
||||
roomId,
|
||||
MessageVerificationCancelContent.create(transactionId, code).toContent(),
|
||||
cryptoService
|
||||
)
|
||||
) {
|
||||
callbackThread = TaskThread.DM_VERIF
|
||||
executionThread = TaskThread.DM_VERIF
|
||||
constraints = TaskConstraints(true)
|
||||
retryCount = 3
|
||||
callback = object : MatrixCallback<SendResponse> {
|
||||
override fun onSuccess(data: SendResponse) {
|
||||
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)
|
||||
val event = createEventAndLocalEcho(
|
||||
type = EventType.KEY_VERIFICATION_CANCEL,
|
||||
roomId = roomId,
|
||||
content = MessageVerificationCancelContent.create(transactionId, code).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
event = event
|
||||
))
|
||||
enqueueSendWork(workerParams)
|
||||
}
|
||||
|
||||
override fun done(transactionId: String) {
|
||||
sendVerificationMessageTask.configureWith(
|
||||
sendVerificationMessageTask.createParamsAndLocalEcho(
|
||||
EventType.KEY_VERIFICATION_DONE,
|
||||
roomId,
|
||||
MessageVerificationDoneContent(
|
||||
relatesTo = RelationDefaultContent(
|
||||
RelationType.REFERENCE,
|
||||
transactionId
|
||||
)
|
||||
).toContent(),
|
||||
cryptoService
|
||||
)
|
||||
) {
|
||||
callbackThread = TaskThread.DM_VERIF
|
||||
executionThread = TaskThread.DM_VERIF
|
||||
constraints = TaskConstraints(true)
|
||||
retryCount = 3
|
||||
}
|
||||
.executeBy(taskExecutor)
|
||||
val event = createEventAndLocalEcho(
|
||||
type = EventType.KEY_VERIFICATION_DONE,
|
||||
roomId = roomId,
|
||||
content = MessageVerificationDoneContent(
|
||||
relatesTo = RelationDefaultContent(
|
||||
RelationType.REFERENCE,
|
||||
transactionId
|
||||
)
|
||||
).toContent()
|
||||
)
|
||||
val workerParams = WorkerParamsFactory.toData(SendVerificationMessageWorker.Params(
|
||||
userId = userId,
|
||||
event = event
|
||||
))
|
||||
enqueueSendWork(workerParams)
|
||||
}
|
||||
|
||||
private fun enqueueSendWork(workerParams: Data): Pair<Operation, UUID> {
|
||||
val workRequest = WorkManagerUtil.matrixOneTimeWorkRequestBuilder<SendVerificationMessageWorker>()
|
||||
.setConstraints(WorkManagerUtil.workConstraints)
|
||||
.setInputData(workerParams)
|
||||
.setBackoffCriteria(BackoffPolicy.LINEAR, 2_000L, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
return WorkManager.getInstance(context)
|
||||
.beginUniqueWork("${roomId}_VerificationWork", ExistingWorkPolicy.APPEND, workRequest)
|
||||
.enqueue() to workRequest.id
|
||||
}
|
||||
|
||||
override fun createAccept(tid: String,
|
||||
@ -171,23 +263,35 @@ internal class SasTransportRoomMessage(
|
||||
override fun createReady(tid: String, fromDevice: String, methods: List<String>): VerificationInfoReady {
|
||||
return MessageVerificationReadyContent(
|
||||
fromDevice = fromDevice,
|
||||
relatesTo = RelationDefaultContent(
|
||||
relatesTo = RelationDefaultContent(
|
||||
type = RelationType.REFERENCE,
|
||||
eventId = tid
|
||||
),
|
||||
methods = methods
|
||||
methods = methods
|
||||
)
|
||||
}
|
||||
|
||||
private fun createEventAndLocalEcho(localID: String = LocalEcho.createLocalEchoId(), type: String, roomId: String, content: Content): Event {
|
||||
return Event(
|
||||
roomId = roomId,
|
||||
originServerTs = System.currentTimeMillis(),
|
||||
senderId = userId,
|
||||
eventId = localID,
|
||||
type = type,
|
||||
content = content,
|
||||
unsignedData = UnsignedData(age = null, transactionId = localID)
|
||||
).also {
|
||||
localEchoEventFactory.saveLocalEcho(monarchy, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SasTransportRoomMessageFactory @Inject constructor(
|
||||
private val sendVerificationMessageTask: DefaultSendVerificationMessageTask,
|
||||
private val taskExecutor: TaskExecutor) {
|
||||
private val monarchy: Monarchy,
|
||||
private val localEchoEventFactory: LocalEchoEventFactory) {
|
||||
|
||||
fun createTransport(roomId: String,
|
||||
cryptoService: CryptoService,
|
||||
tx: SASVerificationTransaction?
|
||||
fun createTransport(context: Context, userId: String, userDevice: String, roomId: String, tx: SASVerificationTransaction?
|
||||
): SasTransportRoomMessage {
|
||||
return SasTransportRoomMessage(roomId, cryptoService, tx, sendVerificationMessageTask, taskExecutor)
|
||||
return SasTransportRoomMessage(context, userId, userDevice, roomId, monarchy, localEchoEventFactory, tx)
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import im.vector.matrix.android.api.MatrixCallback
|
||||
import im.vector.matrix.android.api.session.crypto.sas.CancelCode
|
||||
import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.*
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
|
||||
@ -33,6 +34,10 @@ internal class SasTransportToDevice(
|
||||
private var taskExecutor: TaskExecutor
|
||||
) : SasTransport {
|
||||
|
||||
override fun sendVerificationRequest(localID: String, otherUserId: String, roomId: String, callback: (String?, MessageVerificationRequestContent?) -> Unit) {
|
||||
// TODO "not implemented"
|
||||
}
|
||||
|
||||
override fun sendToOther(type: String,
|
||||
verificationInfo: VerificationInfo,
|
||||
nextState: SasVerificationTxState,
|
||||
|
@ -0,0 +1,61 @@
|
||||
package im.vector.matrix.android.internal.crypto.verification
|
||||
|
||||
import android.content.Context
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.Data
|
||||
import androidx.work.WorkerParameters
|
||||
import com.squareup.moshi.JsonClass
|
||||
import im.vector.matrix.android.api.failure.Failure
|
||||
import im.vector.matrix.android.api.failure.MatrixError
|
||||
import im.vector.matrix.android.api.session.crypto.CryptoService
|
||||
import im.vector.matrix.android.api.session.events.model.Event
|
||||
import im.vector.matrix.android.internal.crypto.tasks.SendVerificationMessageTask
|
||||
import im.vector.matrix.android.internal.worker.WorkerParamsFactory
|
||||
import im.vector.matrix.android.internal.worker.getSessionComponent
|
||||
import javax.inject.Inject
|
||||
|
||||
internal class SendVerificationMessageWorker constructor(context: Context, params: WorkerParameters)
|
||||
: CoroutineWorker(context, params) {
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
internal data class Params(
|
||||
val userId: String,
|
||||
val event: Event
|
||||
)
|
||||
|
||||
@Inject
|
||||
lateinit var sendVerificationMessageTask: SendVerificationMessageTask
|
||||
|
||||
@Inject
|
||||
lateinit var cryptoService: CryptoService
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val params = WorkerParamsFactory.fromData<Params>(inputData)
|
||||
?: return Result.success(Data.Builder().putBoolean("failed", true).build())
|
||||
|
||||
val sessionComponent = getSessionComponent(params.userId) ?: return Result.success()
|
||||
sessionComponent.inject(this)
|
||||
val localId = params.event.eventId ?: ""
|
||||
return try {
|
||||
val eventId = sendVerificationMessageTask.execute(
|
||||
SendVerificationMessageTask.Params(
|
||||
event = params.event,
|
||||
cryptoService = cryptoService
|
||||
)
|
||||
)
|
||||
|
||||
Result.success(Data.Builder().putString(localId, eventId).build())
|
||||
} catch (exception: Throwable) {
|
||||
if (exception.shouldBeRetried()) {
|
||||
Result.retry()
|
||||
} else {
|
||||
Result.success(Data.Builder().putBoolean("failed", true).build())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Throwable.shouldBeRetried(): Boolean {
|
||||
return this is Failure.NetworkConnection
|
||||
|| (this is Failure.ServerError && error.code == MatrixError.M_LIMIT_EXCEEDED)
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ import dagger.Component
|
||||
import im.vector.matrix.android.api.auth.data.SessionParams
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.internal.crypto.CryptoModule
|
||||
import im.vector.matrix.android.internal.crypto.verification.SendVerificationMessageWorker
|
||||
import im.vector.matrix.android.internal.di.MatrixComponent
|
||||
import im.vector.matrix.android.internal.di.SessionAssistedInjectModule
|
||||
import im.vector.matrix.android.internal.network.NetworkConnectivityChecker
|
||||
@ -98,6 +99,8 @@ internal interface SessionComponent {
|
||||
|
||||
fun inject(addHttpPusherWorker: AddHttpPusherWorker)
|
||||
|
||||
fun inject(sendVerificationMessageWorker: SendVerificationMessageWorker)
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(
|
||||
|
@ -109,7 +109,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
|
||||
is VerificationAction.RequestVerificationByDM -> {
|
||||
// session
|
||||
setState {
|
||||
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(otherUserId, roomId, null))
|
||||
copy(pendingRequest = session.getSasVerificationService().requestKeyVerificationInDMs(otherUserId, roomId))
|
||||
}
|
||||
}
|
||||
is VerificationAction.StartSASVerification -> {
|
||||
|
@ -398,7 +398,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
|
||||
popDraft()
|
||||
}
|
||||
is ParsedCommand.VerifyUser -> {
|
||||
session.getSasVerificationService().requestKeyVerificationInDMs(slashCommandResult.userId, room.roomId, null)
|
||||
session.getSasVerificationService().requestKeyVerificationInDMs(slashCommandResult.userId, room.roomId)
|
||||
_sendMessageResultLiveData.postLiveEvent(SendMessageResult.SlashCommandHandled())
|
||||
popDraft()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user