#316 fix OAuth login in pCloud using Google

By switching from WebView to real browser
This commit is contained in:
Julian Raufelder 2021-06-18 13:08:33 +02:00
parent c4496ca148
commit 46aec2022a
No known key found for this signature in database
GPG Key ID: 17EE71F6634E381D
13 changed files with 306 additions and 181 deletions

View File

@ -122,7 +122,6 @@ dependencies {
implementation project(':util') implementation project(':util')
implementation project(':domain') implementation project(':domain')
implementation project(':data') implementation project(':data')
implementation project(':pcloud-sdk-android')
coreLibraryDesugaring dependencies.coreDesugaring coreLibraryDesugaring dependencies.coreDesugaring

View File

@ -265,10 +265,10 @@ class AuthenticateCloudPresenter @Inject constructor( //
} }
override fun resumed(intent: AuthenticateCloudIntent) { override fun resumed(intent: AuthenticateCloudIntent) {
when { if (authenticationStarted) {
ExceptionUtil.contains(intent.error(), WrongCredentialsException::class.java) -> { finish()
if (!authenticationStarted) { } else {
startAuthentication() startAuthentication(intent.cloud())
Toast.makeText( Toast.makeText(
context(), context(),
String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()), String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()),
@ -276,65 +276,47 @@ class AuthenticateCloudPresenter @Inject constructor( //
).show() ).show()
} }
} }
else -> {
Timber.tag("AuthicateCloudPrester").e(intent.error())
failAuthentication(intent.cloud().name())
}
}
}
private fun startAuthentication() { private fun startAuthentication(cloud: CloudModel) {
authenticationStarted = true authenticationStarted = true
val authIntent: Intent = AuthorizationActivity.createIntent( showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
context(), view?.skipTransition()
AuthorizationRequest.create()
.setType(AuthorizationRequest.Type.TOKEN)
.setClientId(BuildConfig.PCLOUD_CLIENT_ID)
.setForceAccessApproval(true)
.addPermission("manageshares")
.build()
)
requestActivityResult( requestActivityResult(
ActivityResultCallbacks.pCloudReAuthenticationFinished(), // ActivityResultCallbacks.pCloudReAuthenticationFinished(cloud), //
authIntent Intents.cloudConnectionListIntent() //
.withCloudType(CloudTypeModel.PCLOUD) //
.withDialogTitle(context().getString(R.string.screen_update_pcloud_connections_title)) //
.withFinishOnCloudItemClick(false) //
) )
} }
} }
@Callback @Callback
fun pCloudReAuthenticationFinished(activityResult: ActivityResult) { fun pCloudReAuthenticationFinished(activityResult: ActivityResult, cloud: CloudModel) {
val authData: AuthorizationData = AuthorizationActivity.getResult(activityResult.intent()) val code = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_OAUTH_AUTH_CODE, "")
val result: AuthorizationResult = authData.result val hostname = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_HOSTNAME, "")
when (result) { if (!code.isNullOrEmpty() && !hostname.isNullOrEmpty()) {
AuthorizationResult.ACCESS_GRANTED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud OAuth code successfully retrieved")
val accessToken: String = CredentialCryptor //
.getInstance(context()) // val accessToken = CredentialCryptor //
.encrypt(authData.token) .getInstance(this.context()) //
val pCloudSkeleton: PCloud = PCloud.aPCloud() // .encrypt(code)
val pCloudSkeleton = PCloud.aPCloud() //
.withAccessToken(accessToken) .withAccessToken(accessToken)
.withUrl(authData.apiHost) .withUrl(hostname)
.build(); .build();
getUsernameUseCase // getUsernameUseCase //
.withCloud(pCloudSkeleton) // .withCloud(pCloudSkeleton) //
.run(object : DefaultResultHandler<String>() { .run(object : DefaultResultHandler<String>() {
override fun onSuccess(username: String?) { override fun onSuccess(username: String) {
Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication successfully")
prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build()) prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build())
} }
}) })
} } else {
AuthorizationResult.ACCESS_DENIED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication not successful")
Timber.tag("CloudConnListPresenter").e("Account access denied") failAuthentication(cloud.name())
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)))
}
} }
} }

View File

@ -116,6 +116,24 @@
android:name=".ui.activity.AuthenticateCloudActivity" android:name=".ui.activity.AuthenticateCloudActivity"
android:theme="@style/Theme.Transparent" /> android:theme="@style/Theme.Transparent" />
<activity
android:name=".ui.activity.AuthenticatePCloudActivity"
android:configChanges="orientation|keyboard"
android:launchMode="singleTask">
<intent-filter>
<data
android:host="redirect"
android:scheme="pcloudoauth" />
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity <activity
android:name=".ui.activity.ImagePreviewActivity" android:name=".ui.activity.ImagePreviewActivity"
android:theme="@style/FullscreenTheme" /> android:theme="@style/FullscreenTheme" />

View File

@ -5,6 +5,7 @@ import android.app.Activity;
import org.cryptomator.domain.di.PerView; import org.cryptomator.domain.di.PerView;
import org.cryptomator.presentation.di.module.ActivityModule; import org.cryptomator.presentation.di.module.ActivityModule;
import org.cryptomator.presentation.ui.activity.AuthenticateCloudActivity; import org.cryptomator.presentation.ui.activity.AuthenticateCloudActivity;
import org.cryptomator.presentation.ui.activity.AuthenticatePCloudActivity;
import org.cryptomator.presentation.ui.activity.AutoUploadChooseVaultActivity; import org.cryptomator.presentation.ui.activity.AutoUploadChooseVaultActivity;
import org.cryptomator.presentation.ui.activity.BiometricAuthSettingsActivity; import org.cryptomator.presentation.ui.activity.BiometricAuthSettingsActivity;
import org.cryptomator.presentation.ui.activity.BrowseFilesActivity; import org.cryptomator.presentation.ui.activity.BrowseFilesActivity;
@ -109,6 +110,8 @@ public interface ActivityComponent {
void inject(AuthenticateCloudActivity authenticateCloudActivity); void inject(AuthenticateCloudActivity authenticateCloudActivity);
void inject(AuthenticatePCloudActivity authenticatePCloudActivity);
void inject(ImagePreviewActivity imagePreviewActivity); void inject(ImagePreviewActivity imagePreviewActivity);
void inject(ImagePreviewFragment imagePreviewFragment); void inject(ImagePreviewFragment imagePreviewFragment);

View File

@ -0,0 +1,8 @@
package org.cryptomator.presentation.intent;
import org.cryptomator.generator.Intent;
import org.cryptomator.presentation.ui.activity.AuthenticatePCloudActivity;
@Intent(AuthenticatePCloudActivity.class)
public interface AuthenticatePCloudIntent {
}

View File

@ -6,10 +6,6 @@ import android.net.Uri
import android.os.Build import android.os.Build
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import com.pcloud.sdk.AuthorizationActivity
import com.pcloud.sdk.AuthorizationData
import com.pcloud.sdk.AuthorizationRequest
import com.pcloud.sdk.AuthorizationResult
import org.cryptomator.domain.Cloud import org.cryptomator.domain.Cloud
import org.cryptomator.domain.LocalStorageCloud import org.cryptomator.domain.LocalStorageCloud
import org.cryptomator.domain.PCloud import org.cryptomator.domain.PCloud
@ -22,7 +18,6 @@ import org.cryptomator.domain.usecases.cloud.RemoveCloudUseCase
import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase import org.cryptomator.domain.usecases.vault.DeleteVaultUseCase
import org.cryptomator.domain.usecases.vault.GetVaultListUseCase import org.cryptomator.domain.usecases.vault.GetVaultListUseCase
import org.cryptomator.generator.Callback import org.cryptomator.generator.Callback
import org.cryptomator.presentation.BuildConfig
import org.cryptomator.presentation.R import org.cryptomator.presentation.R
import org.cryptomator.presentation.exception.ExceptionHandlers import org.cryptomator.presentation.exception.ExceptionHandlers
import org.cryptomator.presentation.intent.Intents import org.cryptomator.presentation.intent.Intents
@ -33,6 +28,7 @@ import org.cryptomator.presentation.model.S3CloudModel
import org.cryptomator.presentation.model.WebDavCloudModel 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.CloudConnectionListView import org.cryptomator.presentation.ui.activity.view.CloudConnectionListView
import org.cryptomator.presentation.ui.dialog.PCloudCredentialsUpdatedDialog
import org.cryptomator.presentation.workflow.ActivityResult import org.cryptomator.presentation.workflow.ActivityResult
import org.cryptomator.util.crypto.CredentialCryptor import org.cryptomator.util.crypto.CredentialCryptor
import java.util.* import java.util.*
@ -136,18 +132,9 @@ class CloudConnectionListPresenter @Inject constructor( //
Intents.webDavAddOrChangeIntent() Intents.webDavAddOrChangeIntent()
) )
CloudTypeModel.PCLOUD -> { CloudTypeModel.PCLOUD -> {
val authIntent: Intent = AuthorizationActivity.createIntent(
this.context(),
AuthorizationRequest.create()
.setType(AuthorizationRequest.Type.TOKEN)
.setClientId(BuildConfig.PCLOUD_CLIENT_ID)
.setForceAccessApproval(true)
.addPermission("manageshares")
.build()
)
requestActivityResult( requestActivityResult(
ActivityResultCallbacks.pCloudAuthenticationFinished(), // ActivityResultCallbacks.pCloudAuthenticationFinished(), //
authIntent Intents.authenticatePCloudIntent()
) )
} }
CloudTypeModel.S3 -> requestActivityResult( CloudTypeModel.S3 -> requestActivityResult(
@ -210,38 +197,28 @@ class CloudConnectionListPresenter @Inject constructor( //
@Callback @Callback
fun pCloudAuthenticationFinished(activityResult: ActivityResult) { fun pCloudAuthenticationFinished(activityResult: ActivityResult) {
val authData: AuthorizationData = AuthorizationActivity.getResult(activityResult.intent()) val code = activityResult.intent().extras?.getString(PCLOUD_OAUTH_AUTH_CODE, "")
val result: AuthorizationResult = authData.result val hostname = activityResult.intent().extras?.getString(PCLOUD_HOSTNAME, "")
when (result) { if (!code.isNullOrEmpty() && !hostname.isNullOrEmpty()) {
AuthorizationResult.ACCESS_GRANTED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud OAuth code successfully retrieved")
val accessToken: String = CredentialCryptor //
val accessToken = CredentialCryptor //
.getInstance(this.context()) // .getInstance(this.context()) //
.encrypt(authData.token) .encrypt(code)
val pCloudSkeleton: PCloud = PCloud.aPCloud() // val pCloudSkeleton = PCloud.aPCloud() //
.withAccessToken(accessToken) .withAccessToken(accessToken)
.withUrl(authData.apiHost) .withUrl(hostname)
.build(); .build();
getUsernameUseCase // getUsernameUseCase //
.withCloud(pCloudSkeleton) // .withCloud(pCloudSkeleton) //
.run(object : DefaultResultHandler<String>() { .run(object : DefaultResultHandler<String>() {
override fun onSuccess(username: String?) { override fun onSuccess(username: String) {
prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build()) prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build())
} }
}) })
} } else {
AuthorizationResult.ACCESS_DENIED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication not successful")
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)))
}
} }
} }
@ -253,13 +230,13 @@ class CloudConnectionListPresenter @Inject constructor( //
clouds.firstOrNull { clouds.firstOrNull {
(it as PCloud).username() == cloud.username() (it as PCloud).username() == cloud.username()
}?.let { }?.let {
it as PCloud
saveCloud( saveCloud(
PCloud.aCopyOf(it) // PCloud.aCopyOf(it as PCloud) //
.withUrl(cloud.url()) .withUrl(cloud.url())
.withAccessToken(cloud.accessToken()) .withAccessToken(cloud.accessToken())
.build() .build()
) )
view?.showDialog(PCloudCredentialsUpdatedDialog.newInstance(it.username()))
} ?: saveCloud(cloud) } ?: saveCloud(cloud)
} }
}) })
@ -284,8 +261,7 @@ class CloudConnectionListPresenter @Inject constructor( //
LocalStorageCloud.aLocalStorage() // LocalStorageCloud.aLocalStorage() //
.withRootUri(rootTreeUriOfLocalStorage.toString()) // .withRootUri(rootTreeUriOfLocalStorage.toString()) //
.build() .build()
) // ).run(object : DefaultResultHandler<Void?>() {
.run(object : DefaultResultHandler<Void?>() {
override fun onSuccess(void: Void?) { override fun onSuccess(void: Void?) {
loadCloudList() loadCloudList()
} }
@ -327,6 +303,9 @@ class CloudConnectionListPresenter @Inject constructor( //
companion object { companion object {
const val SELECTED_CLOUD = "selectedCloudConnection" const val SELECTED_CLOUD = "selectedCloudConnection"
const val PCLOUD_OAUTH_AUTH_CODE = "pCloudOAuthCode"
const val PCLOUD_HOSTNAME = "pCloudHostname"
} }
init { init {

View File

@ -0,0 +1,66 @@
package org.cryptomator.presentation.ui.activity
import android.content.Intent
import android.net.Uri
import android.widget.Toast
import org.cryptomator.generator.Activity
import org.cryptomator.presentation.BuildConfig
import org.cryptomator.presentation.R
import org.cryptomator.presentation.presenter.CloudConnectionListPresenter
import java.util.TreeMap
import timber.log.Timber
@Activity
class AuthenticatePCloudActivity : BaseActivity() {
override fun setupView() {
val uri = Uri.parse("https://my.pcloud.com/oauth2/authorize")
.buildUpon()
.appendQueryParameter("response_type", "token")
.appendQueryParameter("client_id", BuildConfig.PCLOUD_CLIENT_ID)
.appendQueryParameter("redirect_uri", "pcloudoauth://redirect")
.build()
startActivityForResult(Intent(Intent.ACTION_VIEW, uri), 25)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
super.onActivityResult(requestCode, resultCode, intent)
finish()
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
intent.data?.let {
if(it.host == "redirect" && it.scheme == "pcloudoauth") {
val parameters = parseUrlFragmentParameters(it)
val accessToken = parameters["access_token"]
val hostname = parameters["hostname"]
if (accessToken != null && hostname != null) {
val result = Intent()
result.putExtra(CloudConnectionListPresenter.PCLOUD_OAUTH_AUTH_CODE, accessToken)
result.putExtra(CloudConnectionListPresenter.PCLOUD_HOSTNAME, hostname)
setResult(android.app.Activity.RESULT_OK, result)
finish()
} else {
Toast.makeText(this, R.string.error_authentication_failed, Toast.LENGTH_LONG).show()
}
} else {
Timber.tag("AuthenticatePCloudActivity").e("Tried to call activity using a different redirect scheme")
}
}
}
private fun parseUrlFragmentParameters(url: Uri): Map<String, String> {
url.fragment?.let {
val parameters: MutableMap<String, String> = TreeMap()
val keyPairs = it.split("&".toRegex()).toTypedArray()
keyPairs.forEach { keyPair ->
val delimiterIndex = keyPair.indexOf('=')
parameters[keyPair.substring(0, delimiterIndex)] = keyPair.substring(delimiterIndex + 1, keyPair.length)
}
return parameters
}
return emptyMap()
}
}

View File

@ -11,6 +11,7 @@ import org.cryptomator.presentation.presenter.CloudConnectionListPresenter
import org.cryptomator.presentation.ui.activity.view.CloudConnectionListView import org.cryptomator.presentation.ui.activity.view.CloudConnectionListView
import org.cryptomator.presentation.ui.bottomsheet.CloudConnectionSettingsBottomSheet import org.cryptomator.presentation.ui.bottomsheet.CloudConnectionSettingsBottomSheet
import org.cryptomator.presentation.ui.dialog.DeleteCloudConnectionWithVaultsDialog import org.cryptomator.presentation.ui.dialog.DeleteCloudConnectionWithVaultsDialog
import org.cryptomator.presentation.ui.dialog.PCloudCredentialsUpdatedDialog
import org.cryptomator.presentation.ui.fragment.CloudConnectionListFragment import org.cryptomator.presentation.ui.fragment.CloudConnectionListFragment
import java.util.ArrayList import java.util.ArrayList
import javax.inject.Inject import javax.inject.Inject
@ -20,7 +21,8 @@ import kotlinx.android.synthetic.main.toolbar_layout.toolbar
class CloudConnectionListActivity : BaseActivity(), class CloudConnectionListActivity : BaseActivity(),
CloudConnectionListView, CloudConnectionListView,
CloudConnectionSettingsBottomSheet.Callback, CloudConnectionSettingsBottomSheet.Callback,
DeleteCloudConnectionWithVaultsDialog.Callback { DeleteCloudConnectionWithVaultsDialog.Callback,
PCloudCredentialsUpdatedDialog.Callback {
@Inject @Inject
lateinit var presenter: CloudConnectionListPresenter lateinit var presenter: CloudConnectionListPresenter
@ -74,4 +76,8 @@ class CloudConnectionListActivity : BaseActivity(),
override fun onDeleteCloudConnectionAndVaults(cloudModel: CloudModel, vaultsOfCloud: ArrayList<Vault>) { override fun onDeleteCloudConnectionAndVaults(cloudModel: CloudModel, vaultsOfCloud: ArrayList<Vault>) {
presenter.onDeleteCloudConnectionAndVaults(cloudModel, vaultsOfCloud) presenter.onDeleteCloudConnectionAndVaults(cloudModel, vaultsOfCloud)
} }
override fun onNotifyForPCloudCredentialsUpdateFinished() {
// nothing to do here
}
} }

View File

@ -0,0 +1,64 @@
package org.cryptomator.presentation.ui.dialog
import android.content.DialogInterface
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.app.AlertDialog
import org.cryptomator.generator.Dialog
import org.cryptomator.presentation.R
import org.cryptomator.presentation.util.ResourceHelper
import kotlinx.android.synthetic.main.dialog_pcloud_credentials_updated.tv_pcloud_credentials_updated
@Dialog(R.layout.dialog_pcloud_credentials_updated)
class PCloudCredentialsUpdatedDialog : BaseDialog<PCloudCredentialsUpdatedDialog.Callback>() {
interface Callback {
fun onNotifyForPCloudCredentialsUpdateFinished()
}
val someActivityResultLauncher = registerForActivityResult(StartActivityForResult()) {
dismiss()
callback?.onNotifyForPCloudCredentialsUpdateFinished()
}
override fun onStart() {
super.onStart()
val dialog = dialog as AlertDialog?
dialog?.let {
tv_pcloud_credentials_updated.setOnClickListener {
someActivityResultLauncher.launch(Intent(Intent.ACTION_VIEW, Uri.parse("https://www.pcloud.com")))
}
}
}
public override fun setupDialog(builder: AlertDialog.Builder): android.app.Dialog {
val username = requireArguments().getString(ARG_PCLOUD_USERNAME)
builder //
.setTitle(String.format(ResourceHelper.getString(R.string.dialog_pcloud_credentials_updated_title), username)) //
.setNeutralButton(getString(R.string.dialog_unable_to_share_positive_button)) { _: DialogInterface, _: Int ->
callback?.onNotifyForPCloudCredentialsUpdateFinished()
}
return builder.create()
}
public override fun setupView() {
// empty
}
companion object {
private const val ARG_PCLOUD_USERNAME = "USERNAME"
fun newInstance(username: String): PCloudCredentialsUpdatedDialog {
val args = Bundle()
args.putString(ARG_PCLOUD_USERNAME, username)
val fragment = PCloudCredentialsUpdatedDialog()
fragment.arguments = args
return fragment
}
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="@dimen/activity_vertical_margin">
<TextView
android:id="@+id/tv_pcloud_credentials_updated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_pcloud_credentials_updated" />
</RelativeLayout>
</androidx.core.widget.NestedScrollView>

View File

@ -290,6 +290,7 @@
<!-- ## screen: authenticate cloud --> <!-- ## screen: authenticate cloud -->
<string name="screen_authenticate_auth_authentication_failed">%1$s could not be authenticated.</string> <string name="screen_authenticate_auth_authentication_failed">%1$s could not be authenticated.</string>
<string name="screen_update_pcloud_connections_title">Update pCloud credentials</string>
<!-- ## screen: empty dir file info --> <!-- ## screen: empty dir file info -->
<string name="screen_empty_dir_file_info_title">\'%1$s\' unreachable</string> <string name="screen_empty_dir_file_info_title">\'%1$s\' unreachable</string>
@ -469,6 +470,9 @@
<string name="dialog_no_more_images_to_display">No more images to display&#8230;</string> <string name="dialog_no_more_images_to_display">No more images to display&#8230;</string>
<string name="dialog_pcloud_credentials_updated_title">Credentials of \'%1$s\' updated</string>
<string name="dialog_pcloud_credentials_updated">If you intended to add a new pCloud account, click on this url <a href="https://www.pcloud.com">www.pcloud.com</a>, log out from the current account and click again on the \'+\' in this app to create a new cloud connection.</string>
<string name="permission_snackbar_auth_local_vault">Cryptomator needs storage access to use local vaults</string> <string name="permission_snackbar_auth_local_vault">Cryptomator needs storage access to use local vaults</string>
<string name="permission_snackbar_auth_auto_upload">Cryptomator needs storage access to use auto photo upload</string> <string name="permission_snackbar_auth_auto_upload">Cryptomator needs storage access to use auto photo upload</string>

View File

@ -3,15 +3,10 @@ package org.cryptomator.presentation.presenter
import android.Manifest import android.Manifest
import android.accounts.AccountManager import android.accounts.AccountManager
import android.content.ActivityNotFoundException import android.content.ActivityNotFoundException
import android.content.Intent
import android.widget.Toast import android.widget.Toast
import com.dropbox.core.android.Auth import com.dropbox.core.android.Auth
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential
import com.google.api.services.drive.DriveScopes import com.google.api.services.drive.DriveScopes
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
@ -62,6 +57,7 @@ import java.security.cert.X509Certificate
import javax.inject.Inject import javax.inject.Inject
import timber.log.Timber import timber.log.Timber
@PerView @PerView
class AuthenticateCloudPresenter @Inject constructor( // class AuthenticateCloudPresenter @Inject constructor( //
exceptionHandlers: ExceptionHandlers, // exceptionHandlers: ExceptionHandlers, //
@ -310,10 +306,10 @@ class AuthenticateCloudPresenter @Inject constructor( //
} }
override fun resumed(intent: AuthenticateCloudIntent) { override fun resumed(intent: AuthenticateCloudIntent) {
when { if (authenticationStarted) {
ExceptionUtil.contains(intent.error(), WrongCredentialsException::class.java) -> { finish()
if (!authenticationStarted) { } else {
startAuthentication() startAuthentication(intent.cloud())
Toast.makeText( Toast.makeText(
context(), context(),
String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()), String.format(getString(R.string.error_authentication_failed_re_authenticate), intent.cloud().username()),
@ -321,65 +317,47 @@ class AuthenticateCloudPresenter @Inject constructor( //
).show() ).show()
} }
} }
else -> {
Timber.tag("AuthicateCloudPrester").e(intent.error())
failAuthentication(intent.cloud().name())
}
}
}
private fun startAuthentication() { private fun startAuthentication(cloud: CloudModel) {
authenticationStarted = true authenticationStarted = true
val authIntent: Intent = AuthorizationActivity.createIntent( showProgress(ProgressModel(ProgressStateModel.AUTHENTICATION))
context(), view?.skipTransition()
AuthorizationRequest.create()
.setType(AuthorizationRequest.Type.TOKEN)
.setClientId(BuildConfig.PCLOUD_CLIENT_ID)
.setForceAccessApproval(true)
.addPermission("manageshares")
.build()
)
requestActivityResult( requestActivityResult(
ActivityResultCallbacks.pCloudReAuthenticationFinished(), // ActivityResultCallbacks.pCloudReAuthenticationFinished(cloud), //
authIntent Intents.cloudConnectionListIntent() //
.withCloudType(CloudTypeModel.PCLOUD) //
.withDialogTitle(context().getString(R.string.screen_update_pcloud_connections_title)) //
.withFinishOnCloudItemClick(false) //
) )
} }
} }
@Callback @Callback
fun pCloudReAuthenticationFinished(activityResult: ActivityResult) { fun pCloudReAuthenticationFinished(activityResult: ActivityResult, cloud: CloudModel) {
val authData: AuthorizationData = AuthorizationActivity.getResult(activityResult.intent()) val code = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_OAUTH_AUTH_CODE, "")
val result: AuthorizationResult = authData.result val hostname = activityResult.intent().extras?.getString(CloudConnectionListPresenter.PCLOUD_HOSTNAME, "")
when (result) { if (!code.isNullOrEmpty() && !hostname.isNullOrEmpty()) {
AuthorizationResult.ACCESS_GRANTED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud OAuth code successfully retrieved")
val accessToken: String = CredentialCryptor //
.getInstance(context()) // val accessToken = CredentialCryptor //
.encrypt(authData.token) .getInstance(this.context()) //
val pCloudSkeleton: PCloud = PCloud.aPCloud() // .encrypt(code)
val pCloudSkeleton = PCloud.aPCloud() //
.withAccessToken(accessToken) .withAccessToken(accessToken)
.withUrl(authData.apiHost) .withUrl(hostname)
.build(); .build();
getUsernameUseCase // getUsernameUseCase //
.withCloud(pCloudSkeleton) // .withCloud(pCloudSkeleton) //
.run(object : DefaultResultHandler<String>() { .run(object : DefaultResultHandler<String>() {
override fun onSuccess(username: String?) { override fun onSuccess(username: String) {
Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication successfully")
prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build()) prepareForSavingPCloud(PCloud.aCopyOf(pCloudSkeleton).withUsername(username).build())
} }
}) })
} } else {
AuthorizationResult.ACCESS_DENIED -> { Timber.tag("CloudConnectionListPresenter").i("PCloud Authentication not successful")
Timber.tag("CloudConnListPresenter").e("Account access denied") failAuthentication(cloud.name())
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)))
}
} }
} }

View File

@ -1,5 +1,4 @@
include ':generator', ':presentation', ':generator-api', ':domain', ':data', ':util', ':subsampling-image-view', ':msa-auth-for-android', ':pcloud-sdk-java-root', ':pcloud-sdk-java', ':pcloud-sdk-android' include ':generator', ':presentation', ':generator-api', ':domain', ':data', ':util', ':subsampling-image-view', ':msa-auth-for-android', ':pcloud-sdk-java-root', ':pcloud-sdk-java'
project(':subsampling-image-view').projectDir = file(new File(rootDir, 'subsampling-scale-image-view/library')) project(':subsampling-image-view').projectDir = file(new File(rootDir, 'subsampling-scale-image-view/library'))
project(':pcloud-sdk-java-root').projectDir = file(new File(rootDir, 'pcloud-sdk-java')) project(':pcloud-sdk-java-root').projectDir = file(new File(rootDir, 'pcloud-sdk-java'))
project(':pcloud-sdk-java').projectDir = file(new File(rootDir, 'pcloud-sdk-java/java-core')) project(':pcloud-sdk-java').projectDir = file(new File(rootDir, 'pcloud-sdk-java/java-core'))
project(':pcloud-sdk-android').projectDir = file(new File(rootDir, 'pcloud-sdk-java/android'))