mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 20:06:51 +03:00
Better session detection
This commit is contained in:
parent
8955e5461c
commit
f0a9be2ec7
7 changed files with 53 additions and 16 deletions
|
@ -32,6 +32,7 @@ import im.vector.matrix.android.api.util.Optional
|
|||
import im.vector.matrix.android.api.util.toOptional
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
|
||||
import im.vector.matrix.android.internal.session.sync.model.accountdata.UserAccountDataEvent
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
@ -131,6 +132,13 @@ class RxSession(private val session: Session) {
|
|||
}
|
||||
}
|
||||
|
||||
fun liveCrossSigningPrivateKeys(): Observable<Optional<PrivateKeysInfo>> {
|
||||
return session.cryptoService().crossSigningService().getLiveCrossSigningPrivateKeys().asObservable()
|
||||
.startWithCallable {
|
||||
session.cryptoService().crossSigningService().getCrossSigningPrivateKeys().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
fun liveAccountData(types: Set<String>): Observable<List<UserAccountDataEvent>> {
|
||||
return session.getLiveAccountDataEvents(types).asObservable()
|
||||
.startWithCallable {
|
||||
|
|
|
@ -55,6 +55,8 @@ interface CrossSigningService {
|
|||
|
||||
fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
|
||||
|
||||
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
|
||||
|
||||
fun canCrossSign(): Boolean
|
||||
|
||||
fun trustUser(otherUserId: String,
|
||||
|
|
|
@ -470,6 +470,10 @@ internal class DefaultCrossSigningService @Inject constructor(
|
|||
return cryptoStore.getCrossSigningPrivateKeys()
|
||||
}
|
||||
|
||||
override fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>> {
|
||||
return cryptoStore.getLiveCrossSigningPrivateKeys()
|
||||
}
|
||||
|
||||
override fun canCrossSign(): Boolean {
|
||||
return checkSelfTrust().isVerified() && cryptoStore.getCrossSigningPrivateKeys()?.selfSigned != null
|
||||
&& cryptoStore.getCrossSigningPrivateKeys()?.user != null
|
||||
|
|
|
@ -409,6 +409,7 @@ internal interface IMXCryptoStore {
|
|||
fun storeUSKPrivateKey(usk: String?)
|
||||
|
||||
fun getCrossSigningPrivateKeys(): PrivateKeysInfo?
|
||||
fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>>
|
||||
|
||||
fun saveBackupRecoveryKey(recoveryKey: String?, version: String?)
|
||||
fun getKeyBackupRecoveryKeyInfo() : SavedKeyBackupKeyInfo?
|
||||
|
|
|
@ -366,6 +366,25 @@ internal class RealmCryptoStore @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getLiveCrossSigningPrivateKeys(): LiveData<Optional<PrivateKeysInfo>> {
|
||||
val liveData = monarchy.findAllMappedWithChanges(
|
||||
{ realm: Realm ->
|
||||
realm
|
||||
.where<CryptoMetadataEntity>()
|
||||
},
|
||||
{
|
||||
PrivateKeysInfo(
|
||||
master = it.xSignMasterPrivateKey,
|
||||
selfSigned = it.xSignSelfSignedPrivateKey,
|
||||
user = it.xSignUserPrivateKey
|
||||
)
|
||||
}
|
||||
)
|
||||
return Transformations.map(liveData) {
|
||||
it.firstOrNull().toOptional()
|
||||
}
|
||||
}
|
||||
|
||||
override fun storePrivateKeysInfo(msk: String?, usk: String?, ssk: String?) {
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
realm.where<CryptoMetadataEntity>().findFirst()?.apply {
|
||||
|
|
|
@ -90,7 +90,8 @@ class HomeDetailFragment @Inject constructor(
|
|||
|
||||
unknownDeviceDetectorSharedViewModel.subscribe { state ->
|
||||
state.unknownSessions.invoke()?.let { unknownDevices ->
|
||||
if (state.canCrossSign && unknownDevices.isNotEmpty()) {
|
||||
// Timber.v("## Detector Triggerred in fragment - ${unknownDevices.firstOrNull()}")
|
||||
if (unknownDevices.firstOrNull()?.currentSessionTrust == true) {
|
||||
Timber.v("## Detector - ${unknownDevices.size} Unknown sessions")
|
||||
unknownDevices.forEachIndexed { index, detectionInfo ->
|
||||
Timber.v("## Detector - #$index deviceId:${detectionInfo.deviceInfo.deviceId} lastSeenTs:${detectionInfo.deviceInfo.lastSeenTs} is New: ${detectionInfo.isNew}")
|
||||
|
@ -125,6 +126,9 @@ class HomeDetailFragment @Inject constructor(
|
|||
(weakCurrentActivity?.get() as? VectorBaseActivity)
|
||||
?.navigator
|
||||
?.requestSessionVerification(requireContext(), newest.deviceId ?: "")
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
UnknownDeviceDetectorSharedViewModel.Action.IgnoreDevice(newest.deviceId?.let { listOf(it) } ?: emptyList())
|
||||
)
|
||||
}
|
||||
dismissedAction = Runnable {
|
||||
unknownDeviceDetectorSharedViewModel.handle(
|
||||
|
|
|
@ -26,9 +26,11 @@ import im.vector.matrix.android.api.NoOpMatrixCallback
|
|||
import im.vector.matrix.android.api.extensions.orFalse
|
||||
import im.vector.matrix.android.api.session.Session
|
||||
import im.vector.matrix.android.api.util.MatrixItem
|
||||
import im.vector.matrix.android.api.util.Optional
|
||||
import im.vector.matrix.android.api.util.toMatrixItem
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.store.PrivateKeysInfo
|
||||
import im.vector.matrix.rx.rx
|
||||
import im.vector.riotx.core.di.HasScreenInjector
|
||||
import im.vector.riotx.core.platform.EmptyViewEvents
|
||||
|
@ -36,19 +38,19 @@ import im.vector.riotx.core.platform.VectorViewModel
|
|||
import im.vector.riotx.core.platform.VectorViewModelAction
|
||||
import im.vector.riotx.features.settings.VectorPreferences
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.functions.BiFunction
|
||||
import io.reactivex.functions.Function3
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class UnknownDevicesState(
|
||||
val myMatrixItem: MatrixItem.UserItem? = null,
|
||||
val unknownSessions: Async<List<DeviceDetectionInfo>> = Uninitialized,
|
||||
val canCrossSign: Boolean = false
|
||||
val unknownSessions: Async<List<DeviceDetectionInfo>> = Uninitialized
|
||||
) : MvRxState
|
||||
|
||||
data class DeviceDetectionInfo(
|
||||
val deviceInfo: DeviceInfo,
|
||||
val isNew: Boolean
|
||||
val isNew: Boolean,
|
||||
val currentSessionTrust: Boolean
|
||||
)
|
||||
|
||||
class UnknownDeviceDetectorSharedViewModel(
|
||||
|
@ -76,10 +78,13 @@ class UnknownDeviceDetectorSharedViewModel(
|
|||
}
|
||||
)
|
||||
|
||||
Observable.combineLatest<List<CryptoDeviceInfo>, List<DeviceInfo>, List<DeviceDetectionInfo>>(
|
||||
Observable.combineLatest<List<CryptoDeviceInfo>, List<DeviceInfo>, Optional<PrivateKeysInfo>, List<DeviceDetectionInfo>>(
|
||||
session.rx().liveUserCryptoDevices(session.myUserId),
|
||||
session.rx().liveMyDeviceInfo(),
|
||||
BiFunction { cryptoList, infoList ->
|
||||
session.rx().liveCrossSigningPrivateKeys(),
|
||||
Function3 { cryptoList, infoList, pInfo ->
|
||||
// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}")
|
||||
// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}")
|
||||
infoList
|
||||
.filter { info ->
|
||||
// filter verified session, by checking the crypto device info
|
||||
|
@ -92,16 +97,15 @@ class UnknownDeviceDetectorSharedViewModel(
|
|||
val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firsTimeSeenLocalTs ?: 0
|
||||
DeviceDetectionInfo(
|
||||
deviceInfo,
|
||||
deviceKnownSince > currentSessionTs + 60_000 // short window to avoid false positive
|
||||
deviceKnownSince > currentSessionTs + 60_000, // short window to avoid false positive,
|
||||
pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change
|
||||
)
|
||||
// .also {
|
||||
// Timber.v("## Detector - first seen Difference with ${deviceInfo.deviceId} is ${deviceKnownSince - currentSessionTs}")
|
||||
// }
|
||||
}
|
||||
}
|
||||
)
|
||||
.distinct()
|
||||
.execute { async ->
|
||||
// Timber.v("## Detector trigger passed distinct")
|
||||
copy(
|
||||
myMatrixItem = session.getUser(session.myUserId)?.toMatrixItem(),
|
||||
unknownSessions = async
|
||||
|
@ -116,11 +120,6 @@ class UnknownDeviceDetectorSharedViewModel(
|
|||
session.cryptoService().fetchDevicesList(NoOpMatrixCallback())
|
||||
}.disposeOnClear()
|
||||
|
||||
session.rx().liveCrossSigningInfo(session.myUserId)
|
||||
.execute {
|
||||
copy(canCrossSign = session.cryptoService().crossSigningService().canCrossSign())
|
||||
}
|
||||
|
||||
// trigger a refresh of lastSeen / last Ip
|
||||
session.cryptoService().fetchDevicesList(NoOpMatrixCallback())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue