Fix race condition when AuthActivity finished and resumes BrowseFiles

CloudList gets refreshed and token is updated, if the token update is faster, resuming overwrites the delegates in DispatchingCloudContentRepository again due to authentication fail because the old folder is used in the onFolderReloadContent because folder in BrowseFilesActivity isn't yet updated.
This commit is contained in:
Julian Raufelder 2022-02-03 22:37:03 +01:00
parent 022e42b8c8
commit d93624c5dd
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
3 changed files with 39 additions and 36 deletions

View File

@ -8,7 +8,7 @@ public class CryptoCloud implements Cloud {
private final Vault vault; private final Vault vault;
public CryptoCloud(Vault vault) { CryptoCloud(Vault vault) {
this.vault = vault; this.vault = vault;
} }

View File

@ -1,24 +0,0 @@
package org.cryptomator.domain.usecases.vault;
import org.cryptomator.domain.Vault;
import org.cryptomator.domain.exception.BackendException;
import org.cryptomator.domain.repository.VaultRepository;
import org.cryptomator.generator.Parameter;
import org.cryptomator.generator.UseCase;
@UseCase
class ReloadVault {
private final VaultRepository vaultRepository;
private final Vault vault;
public ReloadVault(VaultRepository vaultRepository, @Parameter Vault vault) {
this.vaultRepository = vaultRepository;
this.vault = vault;
}
public Vault execute() throws BackendException {
return vaultRepository.load(vault.getId());
}
}

View File

@ -6,8 +6,8 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.provider.DocumentsContract import android.provider.DocumentsContract
import android.widget.Toast import android.widget.Toast
import org.cryptomator.data.cloud.crypto.CryptoCloud
import org.cryptomator.data.cloud.crypto.CryptoFolder import org.cryptomator.data.cloud.crypto.CryptoFolder
import org.cryptomator.domain.Cloud
import org.cryptomator.domain.CloudFile import org.cryptomator.domain.CloudFile
import org.cryptomator.domain.CloudFolder import org.cryptomator.domain.CloudFolder
import org.cryptomator.domain.CloudNode import org.cryptomator.domain.CloudNode
@ -23,6 +23,7 @@ import org.cryptomator.domain.usecases.CloudFolderRecursiveListing
import org.cryptomator.domain.usecases.CloudNodeRecursiveListing import org.cryptomator.domain.usecases.CloudNodeRecursiveListing
import org.cryptomator.domain.usecases.CopyDataUseCase import org.cryptomator.domain.usecases.CopyDataUseCase
import org.cryptomator.domain.usecases.DownloadFile import org.cryptomator.domain.usecases.DownloadFile
import org.cryptomator.domain.usecases.GetDecryptedCloudForVaultUseCase
import org.cryptomator.domain.usecases.ResultRenamed import org.cryptomator.domain.usecases.ResultRenamed
import org.cryptomator.domain.usecases.cloud.CreateFolderUseCase import org.cryptomator.domain.usecases.cloud.CreateFolderUseCase
import org.cryptomator.domain.usecases.cloud.DeleteNodesUseCase import org.cryptomator.domain.usecases.cloud.DeleteNodesUseCase
@ -110,6 +111,7 @@ class BrowseFilesPresenter @Inject constructor( //
private val moveFilesUseCase: MoveFilesUseCase, // private val moveFilesUseCase: MoveFilesUseCase, //
private val moveFoldersUseCase: MoveFoldersUseCase, // private val moveFoldersUseCase: MoveFoldersUseCase, //
private val getCloudListRecursiveUseCase: GetCloudListRecursiveUseCase, // private val getCloudListRecursiveUseCase: GetCloudListRecursiveUseCase, //
private val getDecryptedCloudForVaultUseCase: GetDecryptedCloudForVaultUseCase, //
private val contentResolverUtil: ContentResolverUtil, // private val contentResolverUtil: ContentResolverUtil, //
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, // private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
private val createNewVaultWorkflow: CreateNewVaultWorkflow, // private val createNewVaultWorkflow: CreateNewVaultWorkflow, //
@ -132,6 +134,8 @@ class BrowseFilesPresenter @Inject constructor( //
private lateinit var existingFilesForUpload: MutableMap<String, UploadFile> private lateinit var existingFilesForUpload: MutableMap<String, UploadFile>
private lateinit var downloadFiles: MutableList<DownloadFile> private lateinit var downloadFiles: MutableList<DownloadFile>
private var resumedAfterAuthentication = false
@InjectIntent @InjectIntent
lateinit var intent: BrowseFilesIntent lateinit var intent: BrowseFilesIntent
@ -206,6 +210,7 @@ class BrowseFilesPresenter @Inject constructor( //
view?.showLoading(false) view?.showLoading(false)
when { when {
authenticationExceptionHandler.handleAuthenticationException(this@BrowseFilesPresenter, e, ActivityResultCallbacks.getCloudListAfterAuthentication(cloudFolderModel)) -> { authenticationExceptionHandler.handleAuthenticationException(this@BrowseFilesPresenter, e, ActivityResultCallbacks.getCloudListAfterAuthentication(cloudFolderModel)) -> {
resumedAfterAuthentication = true
return return
} }
e is EmptyDirFileException -> { e is EmptyDirFileException -> {
@ -225,24 +230,42 @@ class BrowseFilesPresenter @Inject constructor( //
}) })
} }
@Callback @Callback(dispatchResultOkOnly = false)
fun getCloudListAfterAuthentication(result: ActivityResult, cloudFolderModel: CloudFolderModel) { fun getCloudListAfterAuthentication(result: ActivityResult, cloudFolderModel: CloudFolderModel) {
val cloudModel = result.getSingleResult(CloudModel::class.java) resumedAfterAuthentication = false
val cloudNode = cloudFolderModel.toCloudNode() if(result.isResultOk) {
val cloudModel = result.getSingleResult(CloudModel::class.java) // FIXME update other vaults using this cloud as well
val updatedCloud = if (cloudNode is CryptoFolder) { val cloudNode = cloudFolderModel.toCloudNode()
CryptoCloud(Vault.aCopyOf(cloudFolderModel.vault()?.toVault()).withCloud(cloudModel.toCloud()).build()) if (cloudNode is CryptoFolder) {
updatedDecryptedCloudFor(Vault.aCopyOf(cloudFolderModel.vault()!!.toVault()).withCloud(cloudModel.toCloud()).build(), cloudFolderModel)
} else {
updatePlaintextCloud(cloudFolderModel, cloudModel)
}
} else { } else {
cloudModel.toCloud() Timber.tag("BrowseFilesPresenter").e("Authentication failed")
} }
}
cloudNode.withCloud(updatedCloud)?.let { private fun updatePlaintextCloud(cloudFolderModel: CloudFolderModel, updatedCloud: CloudModel) {
cloudFolderModel.toCloudNode().withCloud(updatedCloud.toCloud())?.let {
val folder = cloudFolderModelMapper.toModel(it) val folder = cloudFolderModelMapper.toModel(it)
view?.updateActiveFolderDueToAuthenticationProblem(folder) view?.updateActiveFolderDueToAuthenticationProblem(folder)
getCloudList(folder) getCloudList(folder)
} ?: throw FatalBackendException("cloudFolderModel with updated Cloud shouldn't be null") } ?: throw FatalBackendException("cloudFolderModel with updated Cloud shouldn't be null")
} }
private fun updatedDecryptedCloudFor(vault: Vault, cloudFolderModel: CloudFolderModel) {
getDecryptedCloudForVaultUseCase //
.withVault(vault) //
.run(object : DefaultResultHandler<Cloud>() {
override fun onSuccess(cloud: Cloud) {
val folder = cloudFolderModelMapper.toModel(cloudFolderModel.toCloudNode().withCloud(cloud)!!)
view?.updateActiveFolderDueToAuthenticationProblem(folder)
getCloudList(folder)
}
})
}
fun onCreateFolderPressed(cloudFolder: CloudFolderModel, folderName: String?) { fun onCreateFolderPressed(cloudFolder: CloudFolderModel, folderName: String?) {
createFolderUseCase // createFolderUseCase //
.withParent(cloudFolder.toCloudNode()) // .withParent(cloudFolder.toCloudNode()) //
@ -1128,7 +1151,9 @@ class BrowseFilesPresenter @Inject constructor( //
} }
fun onFolderReloadContent(folder: CloudFolderModel) { fun onFolderReloadContent(folder: CloudFolderModel) {
getCloudList(folder) if(!resumedAfterAuthentication) {
getCloudList(folder)
}
} }
fun onExportFolderClicked(cloudFolder: CloudFolderModel, exportTriggeredByUser: ExportOperation) { fun onExportFolderClicked(cloudFolder: CloudFolderModel, exportTriggeredByUser: ExportOperation) {
@ -1262,6 +1287,7 @@ class BrowseFilesPresenter @Inject constructor( //
companion object { companion object {
const val OPEN_FILE_FINISHED = 12 const val OPEN_FILE_FINISHED = 12
val EXPORT_AFTER_APP_CHOOSER: ExportOperation = object : ExportOperation { val EXPORT_AFTER_APP_CHOOSER: ExportOperation = object : ExportOperation {
override fun export(presenter: BrowseFilesPresenter, downloadFiles: List<DownloadFile>) { override fun export(presenter: BrowseFilesPresenter, downloadFiles: List<DownloadFile>) {
presenter.copyFile(downloadFiles) presenter.copyFile(downloadFiles)
@ -1285,7 +1311,8 @@ class BrowseFilesPresenter @Inject constructor( //
renameFolderUseCase, // renameFolderUseCase, //
copyDataUseCase, // copyDataUseCase, //
moveFilesUseCase, // moveFilesUseCase, //
moveFoldersUseCase moveFoldersUseCase, //
getDecryptedCloudForVaultUseCase
) )
this.authenticationExceptionHandler = authenticationExceptionHandler this.authenticationExceptionHandler = authenticationExceptionHandler
} }