#307 fix pCloud login in F-Droid

This commit is contained in:
Julian Raufelder 2021-04-24 23:37:07 +02:00
parent 9a1d9b7e61
commit 4f8f817808
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
2 changed files with 132 additions and 6 deletions

View File

@ -4,6 +4,7 @@ import org.cryptomator.data.cloud.crypto.CryptoCloudContentRepositoryFactory;
import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory; import org.cryptomator.data.cloud.dropbox.DropboxCloudContentRepositoryFactory;
import org.cryptomator.data.cloud.local.LocalStorageContentRepositoryFactory; import org.cryptomator.data.cloud.local.LocalStorageContentRepositoryFactory;
import org.cryptomator.data.cloud.onedrive.OnedriveCloudContentRepositoryFactory; import org.cryptomator.data.cloud.onedrive.OnedriveCloudContentRepositoryFactory;
import org.cryptomator.data.cloud.pcloud.PCloudContentRepositoryFactory;
import org.cryptomator.data.cloud.webdav.WebDavCloudContentRepositoryFactory; import org.cryptomator.data.cloud.webdav.WebDavCloudContentRepositoryFactory;
import org.cryptomator.data.repository.CloudContentRepositoryFactory; import org.cryptomator.data.repository.CloudContentRepositoryFactory;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -23,12 +24,14 @@ public class CloudContentRepositoryFactories implements Iterable<CloudContentRep
@Inject @Inject
public CloudContentRepositoryFactories(DropboxCloudContentRepositoryFactory dropboxFactory, // public CloudContentRepositoryFactories(DropboxCloudContentRepositoryFactory dropboxFactory, //
OnedriveCloudContentRepositoryFactory oneDriveFactory, // OnedriveCloudContentRepositoryFactory oneDriveFactory, //
PCloudContentRepositoryFactory pCloudFactory, //
CryptoCloudContentRepositoryFactory cryptoFactory, // CryptoCloudContentRepositoryFactory cryptoFactory, //
LocalStorageContentRepositoryFactory localStorageFactory, // LocalStorageContentRepositoryFactory localStorageFactory, //
WebDavCloudContentRepositoryFactory webDavFactory) { WebDavCloudContentRepositoryFactory webDavFactory) {
factories = asList(dropboxFactory, // factories = asList(dropboxFactory, //
oneDriveFactory, // oneDriveFactory, //
pCloudFactory, //
cryptoFactory, // cryptoFactory, //
localStorageFactory, // localStorageFactory, //
webDavFactory); webDavFactory);

View File

@ -2,17 +2,34 @@ package org.cryptomator.presentation.presenter
import android.Manifest import android.Manifest
import android.accounts.AccountManager import android.accounts.AccountManager
import android.content.Intent
import android.widget.Toast
import com.dropbox.core.android.Auth import com.dropbox.core.android.Auth
import com.pcloud.sdk.AuthorizationActivity
import com.pcloud.sdk.AuthorizationData
import com.pcloud.sdk.AuthorizationRequest
import com.pcloud.sdk.AuthorizationResult
import org.cryptomator.data.cloud.onedrive.OnedriveClientFactory import org.cryptomator.data.cloud.onedrive.OnedriveClientFactory
import org.cryptomator.data.cloud.onedrive.graph.ClientException import org.cryptomator.data.cloud.onedrive.graph.ClientException
import org.cryptomator.data.cloud.onedrive.graph.ICallback import org.cryptomator.data.cloud.onedrive.graph.ICallback
import org.cryptomator.data.util.X509CertificateHelper import org.cryptomator.data.util.X509CertificateHelper
import org.cryptomator.domain.* import org.cryptomator.domain.Cloud
import org.cryptomator.domain.CloudType
import org.cryptomator.domain.DropboxCloud
import org.cryptomator.domain.GoogleDriveCloud
import org.cryptomator.domain.OnedriveCloud
import org.cryptomator.domain.PCloud
import org.cryptomator.domain.WebDavCloud
import org.cryptomator.domain.di.PerView import org.cryptomator.domain.di.PerView
import org.cryptomator.domain.exception.FatalBackendException import org.cryptomator.domain.exception.FatalBackendException
import org.cryptomator.domain.exception.NetworkConnectionException import org.cryptomator.domain.exception.NetworkConnectionException
import org.cryptomator.domain.exception.authentication.* import org.cryptomator.domain.exception.authentication.AuthenticationException
import org.cryptomator.domain.exception.authentication.WebDavCertificateUntrustedAuthenticationException
import org.cryptomator.domain.exception.authentication.WebDavNotSupportedException
import org.cryptomator.domain.exception.authentication.WebDavServerNotFoundException
import org.cryptomator.domain.exception.authentication.WrongCredentialsException
import org.cryptomator.domain.usecases.cloud.AddOrChangeCloudConnectionUseCase import org.cryptomator.domain.usecases.cloud.AddOrChangeCloudConnectionUseCase
import org.cryptomator.domain.usecases.cloud.GetCloudsUseCase
import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase import org.cryptomator.domain.usecases.cloud.GetUsernameUseCase
import org.cryptomator.generator.Callback import org.cryptomator.generator.Callback
import org.cryptomator.presentation.BuildConfig import org.cryptomator.presentation.BuildConfig
@ -20,23 +37,32 @@ import org.cryptomator.presentation.R
import org.cryptomator.presentation.exception.ExceptionHandlers import org.cryptomator.presentation.exception.ExceptionHandlers
import org.cryptomator.presentation.exception.PermissionNotGrantedException import org.cryptomator.presentation.exception.PermissionNotGrantedException
import org.cryptomator.presentation.intent.AuthenticateCloudIntent import org.cryptomator.presentation.intent.AuthenticateCloudIntent
import org.cryptomator.presentation.model.* import org.cryptomator.presentation.model.CloudModel
import org.cryptomator.presentation.model.CloudTypeModel
import org.cryptomator.presentation.model.ProgressModel
import org.cryptomator.presentation.model.ProgressStateModel
import org.cryptomator.presentation.model.WebDavCloudModel
import org.cryptomator.presentation.model.mappers.CloudModelMapper import org.cryptomator.presentation.model.mappers.CloudModelMapper
import org.cryptomator.presentation.ui.activity.view.AuthenticateCloudView import org.cryptomator.presentation.ui.activity.view.AuthenticateCloudView
import org.cryptomator.presentation.workflow.* import org.cryptomator.presentation.workflow.ActivityResult
import org.cryptomator.presentation.workflow.AddExistingVaultWorkflow
import org.cryptomator.presentation.workflow.CreateNewVaultWorkflow
import org.cryptomator.presentation.workflow.PermissionsResult
import org.cryptomator.presentation.workflow.Workflow
import org.cryptomator.util.ExceptionUtil import org.cryptomator.util.ExceptionUtil
import org.cryptomator.util.crypto.CredentialCryptor import org.cryptomator.util.crypto.CredentialCryptor
import timber.log.Timber
import java.security.cert.CertificateEncodingException import java.security.cert.CertificateEncodingException
import java.security.cert.CertificateException import java.security.cert.CertificateException
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber
@PerView @PerView
class AuthenticateCloudPresenter @Inject constructor( // class AuthenticateCloudPresenter @Inject constructor( //
exceptionHandlers: ExceptionHandlers, // exceptionHandlers: ExceptionHandlers, //
private val cloudModelMapper: CloudModelMapper, // private val cloudModelMapper: CloudModelMapper, //
private val addOrChangeCloudConnectionUseCase: AddOrChangeCloudConnectionUseCase, // private val addOrChangeCloudConnectionUseCase: AddOrChangeCloudConnectionUseCase, //
private val getCloudsUseCase: GetCloudsUseCase, //
private val getUsernameUseCase: GetUsernameUseCase, // private val getUsernameUseCase: GetUsernameUseCase, //
private val addExistingVaultWorkflow: AddExistingVaultWorkflow, // private val addExistingVaultWorkflow: AddExistingVaultWorkflow, //
private val createNewVaultWorkflow: CreateNewVaultWorkflow) : Presenter<AuthenticateCloudView>(exceptionHandlers) { private val createNewVaultWorkflow: CreateNewVaultWorkflow) : Presenter<AuthenticateCloudView>(exceptionHandlers) {
@ -44,6 +70,7 @@ class AuthenticateCloudPresenter @Inject constructor( //
private val strategies = arrayOf( // private val strategies = arrayOf( //
DropboxAuthStrategy(), // DropboxAuthStrategy(), //
OnedriveAuthStrategy(), // OnedriveAuthStrategy(), //
PCloudAuthStrategy(), //
WebDAVAuthStrategy(), // WebDAVAuthStrategy(), //
LocalStorageAuthStrategy() // LocalStorageAuthStrategy() //
) )
@ -221,6 +248,102 @@ class AuthenticateCloudPresenter @Inject constructor( //
} }
} }
private inner class PCloudAuthStrategy : AuthStrategy {
private var authenticationStarted = false
override fun supports(cloud: CloudModel): Boolean {
return cloud.cloudType() == CloudTypeModel.PCLOUD
}
override fun resumed(intent: AuthenticateCloudIntent) {
when {
ExceptionUtil.contains(intent.error(), WrongCredentialsException::class.java) -> {
if (!authenticationStarted) {
startAuthentication()
Toast.makeText(
context(),
String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()),
Toast.LENGTH_LONG).show()
}
}
else -> {
Timber.tag("AuthicateCloudPrester").e(intent.error())
failAuthentication(intent.cloud().name())
}
}
}
private fun startAuthentication() {
authenticationStarted = true
val authIntent: Intent = AuthorizationActivity.createIntent(
context(),
AuthorizationRequest.create()
.setType(AuthorizationRequest.Type.TOKEN)
.setClientId(BuildConfig.PCLOUD_CLIENT_ID)
.setForceAccessApproval(true)
.addPermission("manageshares")
.build())
requestActivityResult(ActivityResultCallbacks.pCloudReAuthenticationFinished(), //
authIntent)
}
}
@Callback
fun pCloudReAuthenticationFinished(activityResult: ActivityResult) {
val authData: AuthorizationData = AuthorizationActivity.getResult(activityResult.intent())
val result: AuthorizationResult = authData.result
when (result) {
AuthorizationResult.ACCESS_GRANTED -> {
val accessToken: String = CredentialCryptor //
.getInstance(context()) //
.encrypt(authData.token)
val pCloudSkeleton: PCloud = PCloud.aPCloud() //
.withAccessToken(accessToken)
.withUrl(authData.apiHost)
.build();
getUsernameUseCase //
.withCloud(pCloudSkeleton) //
.run(object : DefaultResultHandler<String>() {
override fun onSuccess(username: String?) {
prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build())
}
})
}
AuthorizationResult.ACCESS_DENIED -> {
Timber.tag("CloudConnListPresenter").e("Account access denied")
view?.showMessage(String.format(getString(R.string.screen_authenticate_auth_authentication_failed), getString(R.string.cloud_names_pcloud)))
}
AuthorizationResult.AUTH_ERROR -> {
Timber.tag("CloudConnListPresenter").e("""Account access grant error: ${authData.errorMessage}""".trimIndent())
view?.showMessage(String.format(getString(R.string.screen_authenticate_auth_authentication_failed), getString(R.string.cloud_names_pcloud)))
}
AuthorizationResult.CANCELLED -> {
Timber.tag("CloudConnListPresenter").i("Account access grant cancelled")
view?.showMessage(String.format(getString(R.string.screen_authenticate_auth_authentication_failed), getString(R.string.cloud_names_pcloud)))
}
}
}
fun prepareForSavingPCloud(cloud: PCloud) {
getCloudsUseCase //
.withCloudType(cloud.type()) //
.run(object : DefaultResultHandler<List<Cloud>>() {
override fun onSuccess(clouds: List<Cloud>) {
clouds.firstOrNull {
(it as PCloud).username() == cloud.username()
}?.let {
it as PCloud
succeedAuthenticationWith(PCloud.aCopyOf(it) //
.withUrl(cloud.url())
.withAccessToken(cloud.accessToken())
.build())
} ?: succeedAuthenticationWith(cloud)
}
})
}
private inner class WebDAVAuthStrategy : AuthStrategy { private inner class WebDAVAuthStrategy : AuthStrategy {
override fun supports(cloud: CloudModel): Boolean { override fun supports(cloud: CloudModel): Boolean {
@ -342,6 +465,6 @@ class AuthenticateCloudPresenter @Inject constructor( //
} }
init { init {
unsubscribeOnDestroy(addOrChangeCloudConnectionUseCase, getUsernameUseCase) unsubscribeOnDestroy(addOrChangeCloudConnectionUseCase, getCloudsUseCase, getUsernameUseCase)
} }
} }