Introducing some reusable usecases

This commit is contained in:
Maxime NATUREL 2022-09-01 09:25:11 +02:00
parent b995f798f9
commit a5ee4faef4
8 changed files with 206 additions and 23 deletions

View File

@ -0,0 +1,26 @@
/*
* 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 im.vector.app.features.settings.devices
/**
* Used to hold some info about the cross signing of the current Session.
*/
data class CurrentSessionCrossSigningInfo(
val deviceId: String?,
val isCrossSigningInitialized: Boolean,
val isCrossSigningVerified: Boolean,
)

View File

@ -101,6 +101,8 @@ class DevicesViewModel @AssistedInject constructor(
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val matrix: Matrix, private val matrix: Matrix,
private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase, private val checkIfSessionIsInactiveUseCase: CheckIfSessionIsInactiveUseCase,
getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase,
) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState), VerificationService.Listener { ) : VectorViewModel<DevicesViewState, DevicesAction, DevicesViewEvents>(initialState), VerificationService.Listener {
var uiaContinuation: Continuation<UIABaseAuth>? = null var uiaContinuation: Continuation<UIABaseAuth>? = null
@ -116,8 +118,9 @@ class DevicesViewModel @AssistedInject constructor(
private val refreshSource = PublishDataSource<Unit>() private val refreshSource = PublishDataSource<Unit>()
init { init {
val hasAccountCrossSigning = session.cryptoService().crossSigningService().isCrossSigningInitialized() val currentSessionCrossSigningInfo = getCurrentSessionCrossSigningInfoUseCase.execute()
val accountCrossSigningIsTrusted = session.cryptoService().crossSigningService().isCrossSigningVerified() val hasAccountCrossSigning = currentSessionCrossSigningInfo.isCrossSigningInitialized
val accountCrossSigningIsTrusted = currentSessionCrossSigningInfo.isCrossSigningVerified
setState { setState {
copy( copy(
@ -143,12 +146,7 @@ class DevicesViewModel @AssistedInject constructor(
.sortedByDescending { it.lastSeenTs } .sortedByDescending { it.lastSeenTs }
.map { deviceInfo -> .map { deviceInfo ->
val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId } val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }
val trustLevelForShield = computeTrustLevelForShield( val trustLevelForShield = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoDeviceInfo)
currentSessionCrossTrusted = accountCrossSigningIsTrusted,
legacyMode = !hasAccountCrossSigning,
deviceTrustLevel = cryptoDeviceInfo?.trustLevel,
isCurrentDevice = deviceInfo.deviceId == session.sessionParams.deviceId
)
val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0) val isInactive = checkIfSessionIsInactiveUseCase.execute(deviceInfo.lastSeenTs ?: 0)
DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive) DeviceFullInfo(deviceInfo, cryptoDeviceInfo, trustLevelForShield, isInactive)
} }
@ -268,20 +266,6 @@ class DevicesViewModel @AssistedInject constructor(
} }
} }
private fun computeTrustLevelForShield(
currentSessionCrossTrusted: Boolean,
legacyMode: Boolean,
deviceTrustLevel: DeviceTrustLevel?,
isCurrentDevice: Boolean,
): RoomEncryptionTrustLevel {
return TrustUtils.shieldForTrust(
currentDevice = isCurrentDevice,
trustMSK = currentSessionCrossTrusted,
legacyMode = legacyMode,
deviceTrustLevel = deviceTrustLevel
)
}
private fun handleInteractiveVerification(action: DevicesAction.VerifyMyDevice) { private fun handleInteractiveVerification(action: DevicesAction.VerifyMyDevice) {
val txID = session.cryptoService() val txID = session.cryptoService()
.verificationService() .verificationService()

View File

@ -0,0 +1,37 @@
/*
* 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 im.vector.app.features.settings.devices
import im.vector.app.core.di.ActiveSessionHolder
import javax.inject.Inject
// TODO add unit tests
class GetCurrentSessionCrossSigningInfoUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder,
) {
fun execute(): CurrentSessionCrossSigningInfo {
val session = activeSessionHolder.getActiveSession()
val isCrossSigningInitialized = session.cryptoService().crossSigningService().isCrossSigningInitialized()
val isCrossSigningVerified = session.cryptoService().crossSigningService().isCrossSigningVerified()
return CurrentSessionCrossSigningInfo(
deviceId = session.sessionParams.deviceId,
isCrossSigningInitialized = isCrossSigningInitialized,
isCrossSigningVerified = isCrossSigningVerified
)
}
}

View File

@ -0,0 +1,38 @@
/*
* 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 im.vector.app.features.settings.devices
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject
// TODO add unit tests
class GetEncryptionTrustLevelForCurrentDeviceUseCase @Inject constructor() {
fun execute(trustMSK: Boolean, legacyMode: Boolean): RoomEncryptionTrustLevel {
return if (legacyMode) {
// In legacy, current session is always trusted
RoomEncryptionTrustLevel.Trusted
} else {
// If current session doesn't trust MSK, show red shield for current device
if (trustMSK) {
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Warning
}
}
}
}

View File

@ -0,0 +1,40 @@
/*
* 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 im.vector.app.features.settings.devices
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject
// TODO add unit tests
class GetEncryptionTrustLevelForDeviceUseCase @Inject constructor(
private val getEncryptionTrustLevelForCurrentDeviceUseCase: GetEncryptionTrustLevelForCurrentDeviceUseCase,
private val getEncryptionTrustLevelForOtherDeviceUseCase: GetEncryptionTrustLevelForOtherDeviceUseCase,
) {
fun execute(currentSessionCrossSigningInfo: CurrentSessionCrossSigningInfo, cryptoDeviceInfo: CryptoDeviceInfo?): RoomEncryptionTrustLevel {
val legacyMode = !currentSessionCrossSigningInfo.isCrossSigningInitialized
val trustMSK = currentSessionCrossSigningInfo.isCrossSigningVerified
val isCurrentDevice = !cryptoDeviceInfo?.deviceId.isNullOrEmpty() && cryptoDeviceInfo?.deviceId == currentSessionCrossSigningInfo.deviceId
val deviceTrustLevel = cryptoDeviceInfo?.trustLevel
return when {
isCurrentDevice -> getEncryptionTrustLevelForCurrentDeviceUseCase.execute(trustMSK, legacyMode)
else -> getEncryptionTrustLevelForOtherDeviceUseCase.execute(trustMSK, legacyMode, deviceTrustLevel)
}
}
}

View File

@ -0,0 +1,49 @@
/*
* 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 im.vector.app.features.settings.devices
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject
// TODO add unit tests
class GetEncryptionTrustLevelForOtherDeviceUseCase @Inject constructor() {
fun execute(trustMSK: Boolean, legacyMode: Boolean, deviceTrustLevel: DeviceTrustLevel?): RoomEncryptionTrustLevel {
return if (legacyMode) {
// use local trust
if (deviceTrustLevel?.locallyVerified == true) {
RoomEncryptionTrustLevel.Trusted
} else {
RoomEncryptionTrustLevel.Warning
}
} else {
if (trustMSK) {
// use cross sign trust, put locally trusted in black
when {
deviceTrustLevel?.crossSigningVerified == true -> RoomEncryptionTrustLevel.Trusted
deviceTrustLevel?.locallyVerified == true -> RoomEncryptionTrustLevel.Default
else -> RoomEncryptionTrustLevel.Warning
}
} else {
// The current session is untrusted, so displays others in black
// as we can't know the cross-signing state
RoomEncryptionTrustLevel.Default
}
}
}
}

View File

@ -19,6 +19,7 @@ package im.vector.app.features.settings.devices
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
// TODO Replace usage by the use case GetEncryptionTrustLevelForDeviceUseCase
object TrustUtils { object TrustUtils {
fun shieldForTrust( fun shieldForTrust(

View File

@ -19,6 +19,8 @@ package im.vector.app.features.settings.devices.v2.overview
import androidx.lifecycle.asFlow import androidx.lifecycle.asFlow
import im.vector.app.core.di.ActiveSessionHolder import im.vector.app.core.di.ActiveSessionHolder
import im.vector.app.features.settings.devices.DeviceFullInfo import im.vector.app.features.settings.devices.DeviceFullInfo
import im.vector.app.features.settings.devices.GetCurrentSessionCrossSigningInfoUseCase
import im.vector.app.features.settings.devices.GetEncryptionTrustLevelForDeviceUseCase
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
@ -26,12 +28,16 @@ import org.matrix.android.sdk.api.util.Optional
import org.matrix.android.sdk.api.util.toOptional import org.matrix.android.sdk.api.util.toOptional
import javax.inject.Inject import javax.inject.Inject
// TODO update unit test
class GetDeviceFullInfoUseCase @Inject constructor( class GetDeviceFullInfoUseCase @Inject constructor(
private val activeSessionHolder: ActiveSessionHolder, private val activeSessionHolder: ActiveSessionHolder,
private val getCurrentSessionCrossSigningInfoUseCase: GetCurrentSessionCrossSigningInfoUseCase,
private val getEncryptionTrustLevelForDeviceUseCase: GetEncryptionTrustLevelForDeviceUseCase,
) { ) {
fun execute(deviceId: String): Flow<Optional<DeviceFullInfo>> { fun execute(deviceId: String): Flow<Optional<DeviceFullInfo>> {
return activeSessionHolder.getSafeActiveSession()?.let { session -> return activeSessionHolder.getSafeActiveSession()?.let { session ->
val currentSessionCrossSigningInfo = getCurrentSessionCrossSigningInfoUseCase.execute()
combine( combine(
session.cryptoService().getMyDevicesInfoLive(deviceId).asFlow(), session.cryptoService().getMyDevicesInfoLive(deviceId).asFlow(),
session.cryptoService().getLiveCryptoDeviceInfoWithId(deviceId).asFlow() session.cryptoService().getLiveCryptoDeviceInfoWithId(deviceId).asFlow()
@ -39,9 +45,11 @@ class GetDeviceFullInfoUseCase @Inject constructor(
val info = deviceInfo.getOrNull() val info = deviceInfo.getOrNull()
val cryptoInfo = cryptoDeviceInfo.getOrNull() val cryptoInfo = cryptoDeviceInfo.getOrNull()
val fullInfo = if (info != null && cryptoInfo != null) { val fullInfo = if (info != null && cryptoInfo != null) {
val roomEncryptionTrustLevel = getEncryptionTrustLevelForDeviceUseCase.execute(currentSessionCrossSigningInfo, cryptoInfo)
DeviceFullInfo( DeviceFullInfo(
deviceInfo = info, deviceInfo = info,
cryptoDeviceInfo = cryptoInfo cryptoDeviceInfo = cryptoInfo,
trustLevelForShield = roomEncryptionTrustLevel
) )
} else { } else {
null null