Identity: protect against outdated homeserver

This commit is contained in:
Benoit Marty 2020-05-11 14:50:04 +02:00
parent 7afc7bdb31
commit 88e8c11ee5
8 changed files with 53 additions and 9 deletions

View File

@ -24,7 +24,11 @@ data class HomeServerCapabilities(
/**
* Max size of file which can be uploaded to the homeserver in bytes. [MAX_UPLOAD_FILE_SIZE_UNKNOWN] if unknown or not retrieved yet
*/
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN
val maxUploadFileSize: Long = MAX_UPLOAD_FILE_SIZE_UNKNOWN,
/**
* Last version identity server and binding supported
*/
val lastVersionIdentityServerSupported: Boolean = false
) {
companion object {
const val MAX_UPLOAD_FILE_SIZE_UNKNOWN = -1L

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.api.session.identity
sealed class IdentityServiceError(cause: Throwable? = null) : Throwable(cause = cause) {
object OutdatedIdentityServer : IdentityServiceError(null)
object OutdatedHomeServer : IdentityServiceError(null)
object NoIdentityServerConfigured : IdentityServiceError(null)
object TermsNotSignedException : IdentityServiceError(null)
object BulkLookupSha256NotSupported : IdentityServiceError(null)

View File

@ -27,14 +27,16 @@ internal object HomeServerCapabilitiesMapper {
fun map(entity: HomeServerCapabilitiesEntity): HomeServerCapabilities {
return HomeServerCapabilities(
canChangePassword = entity.canChangePassword,
maxUploadFileSize = entity.maxUploadFileSize
maxUploadFileSize = entity.maxUploadFileSize,
lastVersionIdentityServerSupported = entity.lastVersionIdentityServerSupported
)
}
fun map(domain: HomeServerCapabilities): HomeServerCapabilitiesEntity {
return HomeServerCapabilitiesEntity(
canChangePassword = domain.canChangePassword,
maxUploadFileSize = domain.maxUploadFileSize
maxUploadFileSize = domain.maxUploadFileSize,
lastVersionIdentityServerSupported = domain.lastVersionIdentityServerSupported
)
}
}

View File

@ -22,6 +22,7 @@ import io.realm.RealmObject
internal open class HomeServerCapabilitiesEntity(
var canChangePassword: Boolean = true,
var maxUploadFileSize: Long = HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN,
var lastVersionIdentityServerSupported: Boolean = false,
var lastUpdatedTimestamp: Long = 0L
) : RealmObject() {

View File

@ -16,6 +16,7 @@
package im.vector.matrix.android.internal.session.homeserver
import im.vector.matrix.android.api.auth.data.Versions
import im.vector.matrix.android.internal.network.NetworkConstants
import retrofit2.Call
import retrofit2.http.GET
@ -38,5 +39,11 @@ internal interface CapabilitiesAPI {
* Request the versions
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_ + "versions")
fun getVersions(): Call<Unit>
fun getVersions(): Call<Versions>
/**
* Ping the homeserver. We do not care about the returned data, so there is no use to parse them
*/
@GET(NetworkConstants.URI_API_PREFIX_PATH_ + "versions")
fun ping(): Call<Unit>
}

View File

@ -17,6 +17,9 @@
package im.vector.matrix.android.internal.session.homeserver
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Versions
import im.vector.matrix.android.api.auth.data.isLoginAndRegistrationSupportedBySdk
import im.vector.matrix.android.api.extensions.orFalse
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilities
import im.vector.matrix.android.internal.database.model.HomeServerCapabilitiesEntity
import im.vector.matrix.android.internal.database.query.getOrCreate
@ -57,12 +60,19 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
}
}.getOrNull()
// TODO Add other call here (get version, etc.)
val versions = runCatching {
executeRequest<Versions>(null) {
apiCall = capabilitiesAPI.getVersions()
}
}.getOrNull()
insertInDb(capabilities, uploadCapabilities)
insertInDb(capabilities, uploadCapabilities, versions)
}
private suspend fun insertInDb(getCapabilitiesResult: GetCapabilitiesResult?, getUploadCapabilitiesResult: GetUploadCapabilitiesResult) {
private suspend fun insertInDb(getCapabilitiesResult: GetCapabilitiesResult?,
getUploadCapabilitiesResult: GetUploadCapabilitiesResult,
getVersionResult: Versions?) {
monarchy.awaitTransaction { realm ->
val homeServerCapabilitiesEntity = HomeServerCapabilitiesEntity.getOrCreate(realm)
@ -71,6 +81,8 @@ internal class DefaultGetHomeServerCapabilitiesTask @Inject constructor(
homeServerCapabilitiesEntity.maxUploadFileSize = getUploadCapabilitiesResult.maxUploadSize
?: HomeServerCapabilities.MAX_UPLOAD_FILE_SIZE_UNKNOWN
homeServerCapabilitiesEntity.lastVersionIdentityServerSupported = getVersionResult?.isLoginAndRegistrationSupportedBySdk().orFalse()
homeServerCapabilitiesEntity.lastUpdatedTimestamp = Date().time
}
}

View File

@ -35,7 +35,7 @@ internal class HomeServerPinger @Inject constructor(private val taskExecutor: Ta
suspend fun canReachHomeServer(): Boolean {
return try {
executeRequest<Unit>(null) {
apiCall = capabilitiesAPI.getVersions()
apiCall = capabilitiesAPI.ping()
}
true
} catch (throwable: Throwable) {

View File

@ -25,6 +25,7 @@ import im.vector.matrix.android.api.extensions.tryThis
import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.failure.MatrixError
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.homeserver.HomeServerCapabilitiesService
import im.vector.matrix.android.api.session.identity.FoundThreePid
import im.vector.matrix.android.api.session.identity.IdentityService
import im.vector.matrix.android.api.session.identity.IdentityServiceError
@ -75,7 +76,8 @@ internal class DefaultIdentityService @Inject constructor(
private val submitTokenForBindingTask: IdentitySubmitTokenForBindingTask,
private val unbindThreePidsTask: UnbindThreePidsTask,
private val identityApiProvider: IdentityApiProvider,
private val accountDataDataSource: AccountDataDataSource
private val accountDataDataSource: AccountDataDataSource,
private val homeServerCapabilitiesService: HomeServerCapabilitiesService
) : IdentityService {
private val lifecycleOwner: LifecycleOwner = LifecycleOwner { lifecycleRegistry }
@ -123,6 +125,11 @@ internal class DefaultIdentityService @Inject constructor(
}
override fun startBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable {
if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) {
callback.onFailure(IdentityServiceError.OutdatedHomeServer)
return NoOpCancellable
}
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
identityRequestTokenForBindingTask.execute(IdentityRequestTokenForBindingTask.Params(threePid, false))
}
@ -141,6 +148,11 @@ internal class DefaultIdentityService @Inject constructor(
}
override fun finalizeBindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable {
if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) {
callback.onFailure(IdentityServiceError.OutdatedHomeServer)
return NoOpCancellable
}
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
bindThreePidsTask.execute(BindThreePidsTask.Params(threePid))
}
@ -153,6 +165,11 @@ internal class DefaultIdentityService @Inject constructor(
}
override fun unbindThreePid(threePid: ThreePid, callback: MatrixCallback<Unit>): Cancelable {
if (homeServerCapabilitiesService.getHomeServerCapabilities().lastVersionIdentityServerSupported.not()) {
callback.onFailure(IdentityServiceError.OutdatedHomeServer)
return NoOpCancellable
}
return GlobalScope.launchToCallback(coroutineDispatchers.main, callback) {
unbindThreePidsTask.execute(UnbindThreePidsTask.Params(threePid))
}