From d3290d90e3c7412bd932ddcb0e00b618dd75ad68 Mon Sep 17 00:00:00 2001 From: A117870935 Date: Mon, 12 Aug 2024 18:31:18 +0530 Subject: [PATCH] NMC-3250 -> opening login flow in internal webview instead of external browser. --- .../authentication/AuthenticatorActivity.java | 203 +----------------- .../main/res/layout/account_setup_webview.xml | 20 +- 2 files changed, 12 insertions(+), 211 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index 5ae6ec8ebb98..d9ec0bd82f8a 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -43,28 +43,21 @@ import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebView; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import com.blikoon.qrcodescanner.QrCodeActivity; -import com.google.android.material.button.MaterialButton; import com.google.android.material.snackbar.Snackbar; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; import com.nextcloud.android.common.ui.color.ColorUtil; import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; -import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; -import com.nextcloud.common.PlainClient; -import com.nextcloud.operations.PostMethod; import com.nextcloud.utils.extensions.BundleExtensionsKt; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -111,8 +104,6 @@ import com.owncloud.android.utils.theme.CapabilityUtils; import com.owncloud.android.utils.theme.ViewThemeUtils; -import org.json.JSONObject; - import java.io.InputStream; import java.net.URLDecoder; import java.util.HashMap; @@ -120,8 +111,6 @@ import java.util.Map; import java.util.Optional; import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -137,16 +126,11 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleEventObserver; -import androidx.lifecycle.ProcessLifecycleOwner; import de.cotech.hw.fido.WebViewFidoBridge; import de.cotech.hw.fido.ui.FidoDialogOptions; import de.cotech.hw.fido2.WebViewWebauthnBridge; import de.cotech.hw.fido2.ui.WebauthnDialogOptions; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import okhttp3.FormBody; -import okhttp3.RequestBody; import static com.owncloud.android.utils.PermissionUtil.PERMISSIONS_CAMERA; @@ -184,17 +168,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity private static final String KEY_USERNAME = "USERNAME"; private static final String KEY_PASSWORD = "PASSWORD"; private static final String KEY_ASYNC_TASK_IN_PROGRESS = "AUTH_IN_PROGRESS"; - - /** - * Login Flow v1 - */ - // public static final String WEB_LOGIN = "/index.php/login/flow"; - - /** - * Login Flow v2 - */ - public static final String WEB_LOGIN = "/index.php/login/v2"; - + public static final String WEB_LOGIN = "/index.php/login/flow"; public static final String PROTOCOL_SUFFIX = "://"; public static final String LOGIN_URL_DATA_KEY_VALUE_SEPARATOR = ":"; public static final String HTTPS_PROTOCOL = "https://"; @@ -248,9 +222,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity @Inject PassCodeManager passCodeManager; @Inject ViewThemeUtils.Factory viewThemeUtilsFactory; @Inject ColorUtil colorUtil; - @Inject ClientFactory clientFactory; - - private String token; private boolean onlyAdd = false; @SuppressLint("ResourceAsColor") @ColorInt @@ -275,7 +246,7 @@ protected void onCreate(Bundle savedInstanceState) { viewThemeUtils = viewThemeUtilsFactory.withPrimaryAsBackground(); viewThemeUtils.platform.themeStatusBar(this, ColorRole.PRIMARY); - // WebViewUtil webViewUtil = new WebViewUtil(this); + WebViewUtil webViewUtil = new WebViewUtil(this); Uri data = getIntent().getData(); boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme)); @@ -340,8 +311,7 @@ protected void onCreate(Bundle savedInstanceState) { if (webViewLoginMethod) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - anonymouslyPostLoginRequest(webloginUrl); - // initWebViewLogin(webloginUrl, false); + initWebViewLogin(webloginUrl, false); } else { accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater()); setContentView(accountSetupBinding.getRoot()); @@ -356,18 +326,10 @@ protected void onCreate(Bundle savedInstanceState) { } initServerPreFragment(savedInstanceState); - ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); - // webViewUtil.checkWebViewVersion(); + webViewUtil.checkWebViewVersion(); } - private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { - if (event == Lifecycle.Event.ON_START && token != null) { - Log_OC.d(TAG, "Start poolLogin"); - poolLogin(); - } - }); - private void deleteCookies() { try { CookieSyncManager.createInstance(this); @@ -377,72 +339,11 @@ private void deleteCookies() { } } - private String baseUrl; - - /** - * This function facilitates the login process by anonymously posting a login request to a specified URL. - * After posting the request, it retrieves the login URL for completing the login flow. - * The login flow version used is v2. - * - * @param url The URL where the login request is to be anonymously posted. - * This URL should handle the login request and return the login URL. - * It's typically the entry point for the login process. - * Example: "..." - */ - private void anonymouslyPostLoginRequest(String url) { - baseUrl = url; - - Thread thread = new Thread(() -> { - String response = getResponseOfAnonymouslyPostLoginRequest(); - - try { - JsonObject jsonObject = JsonParser.parseString(response).getAsJsonObject(); - String loginUrl = getLoginUrl(jsonObject); - runOnUiThread(() -> { - initLoginInfoView(); - launchDefaultWebBrowser(loginUrl); - }); - token = jsonObject.getAsJsonObject("poll").get("token").getAsString(); - } catch (Throwable t) { - Log_OC.d(TAG, "Error caught at anonymouslyPostLoginRequest: " + t); - DisplayUtils.showSnackMessage(this, R.string.authenticator_activity_login_error); - } - }); - - thread.start(); - } - - private String getResponseOfAnonymouslyPostLoginRequest() { - PostMethod post = new PostMethod(baseUrl, false, new FormBody.Builder().build()); - PlainClient client = clientFactory.createPlainClient(); - post.execute(client); - return post.getResponseBodyAsString(); - } - - private String getLoginUrl(JsonObject response) { - String result = response.get("login").getAsString(); - if (result == null) { - result = getResources().getString(R.string.webview_login_url); - } - - return result; - } - - private void launchDefaultWebBrowser(String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - private static String getWebLoginUserAgent() { return Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + " " + Build.MODEL + " (Android)"; } - /** - * @Deprecated This function is deprecated. Please use the {@link #anonymouslyPostLoginRequest(String)} method instead, which utilizes the improved login flow v2. - */ - @Deprecated @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT") @SuppressLint("SetJavaScriptEnabled") private void initWebViewLogin(String baseURL, boolean useGenericUserAgent) { @@ -802,8 +703,7 @@ protected void onNewIntent(Intent intent) { if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - anonymouslyPostLoginRequest(getString(R.string.provider_registration_server)); - // initWebViewLogin(getString(R.string.provider_registration_server), true); + initWebViewLogin(getString(R.string.provider_registration_server), true); } } @@ -1040,16 +940,7 @@ private void onGetServerInfoFinish(RemoteOperationResult result) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); - - if (!isLoginProcessCompleted) { - if (!isRedirectedToTheDefaultBrowser) { - anonymouslyPostLoginRequest(mServerInfo.mBaseUrl + WEB_LOGIN); - isRedirectedToTheDefaultBrowser = true; - } else { - initLoginInfoView(); - } - // initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); - } + initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); } } else { updateServerStatusIconAndText(result); @@ -1062,20 +953,6 @@ private void onGetServerInfoFinish(RemoteOperationResult result) { } } - // region LoginInfoView - private void initLoginInfoView() { - LinearLayout loginFlowLayout = accountSetupWebviewBinding.loginFlowV2.getRoot(); - MaterialButton cancelButton = accountSetupWebviewBinding.loginFlowV2.cancelButton; - loginFlowLayout.setVisibility(View.VISIBLE); - - cancelButton.setOnClickListener(v -> { - loginFlowExecutorService.shutdown(); - ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); - recreate(); - }); - } - // endregion - /** * Chooses the right icon and text to show to the user for the received operation result. * @@ -1317,8 +1194,7 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) } else { // authorization fail due to client side - probably wrong credentials if (accountSetupWebviewBinding != null) { - anonymouslyPostLoginRequest(mServerInfo.mBaseUrl + WEB_LOGIN); - // initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); + initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false); DisplayUtils.showSnackMessage(this, accountSetupWebviewBinding.loginWebview, R.string.auth_access_failed, result.getLogMessage()); @@ -1662,71 +1538,6 @@ public void onServiceDisconnected(ComponentName component) { } } - private final ScheduledExecutorService loginFlowExecutorService = Executors.newSingleThreadScheduledExecutor(); - private boolean isLoginProcessCompleted = false; - private boolean isRedirectedToTheDefaultBrowser = false; - - private void poolLogin() { - loginFlowExecutorService.scheduleWithFixedDelay(() -> { - if (!isLoginProcessCompleted) { - performLoginFlowV2(); - } - }, 0, 30, TimeUnit.SECONDS); - } - - private void performLoginFlowV2() { - String postRequestUrl = baseUrl + "/poll"; - - RequestBody requestBody = new FormBody.Builder() - .add("token", token) - .build(); - - PlainClient client = clientFactory.createPlainClient(); - PostMethod post = new PostMethod(postRequestUrl, false, requestBody); - int status = post.execute(client); - String response = post.getResponseBodyAsString(); - - Log_OC.d(TAG, "performLoginFlowV2 status: " + status); - Log_OC.d(TAG, "performLoginFlowV2 response: " + response); - - if (!response.isEmpty()) { - runOnUiThread(() -> completeLoginFlow(response, status)); - } - } - - private void completeLoginFlow(String response, int status) { - try { - JSONObject jsonObject = new JSONObject(response); - - String server = jsonObject.getString("server"); - String loginName = jsonObject.getString("loginName"); - String appPassword = jsonObject.getString("appPassword"); - - LoginUrlInfo loginUrlInfo = new LoginUrlInfo(); - loginUrlInfo.serverAddress = server; - loginUrlInfo.username = loginName; - loginUrlInfo.password = appPassword; - - isLoginProcessCompleted = (status == 200 && !server.isEmpty() && !loginName.isEmpty() && !appPassword.isEmpty()); - - if (accountSetupBinding != null) { - accountSetupBinding.hostUrlInput.setText(""); - } - mServerInfo.mBaseUrl = AuthenticatorUrlUtils.INSTANCE.normalizeUrlSuffix(loginUrlInfo.serverAddress); - webViewUser = loginUrlInfo.username; - webViewPassword = loginUrlInfo.password; - } catch (Exception e) { - Log_OC.d(TAG, "Error caught at completeLoginFlow: " + e); - mServerStatusIcon = R.drawable.ic_alert; - mServerStatusText = getString(R.string.qr_could_not_be_read); - showServerStatus(); - } - - checkOcServer(); - loginFlowExecutorService.shutdown(); - ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); - } - /** * Called from SslValidatorDialog when a new server certificate was correctly saved. */ diff --git a/app/src/main/res/layout/account_setup_webview.xml b/app/src/main/res/layout/account_setup_webview.xml index 583aa20126ea..54c520c617a0 100644 --- a/app/src/main/res/layout/account_setup_webview.xml +++ b/app/src/main/res/layout/account_setup_webview.xml @@ -7,31 +7,21 @@ ~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only --> + android:layout_height="match_parent"> - - - - + android:indeterminate="true"/>