Merge pull request #9881 from nextcloud/customCertInEditorWebView

Allow accessing URLs with custom cert in editor
This commit is contained in:
Álvaro Brey 2022-03-02 10:32:09 +01:00 committed by GitHub
commit 263413d53f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 72 deletions

View file

@ -1 +1 @@
634
633

View file

@ -1,2 +1,2 @@
DO NOT TOUCH; GENERATED BY DRONE
<span class="mdl-layout-title">Lint Report: 94 warnings</span>
<span class="mdl-layout-title">Lint Report: 93 warnings</span>

View file

@ -54,8 +54,6 @@ import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@ -68,11 +66,9 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.SslErrorHandler;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
@ -100,7 +96,6 @@ import com.owncloud.android.lib.common.UserInfo;
import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException;
import com.owncloud.android.lib.common.accounts.AccountUtils.Constants;
import com.owncloud.android.lib.common.network.CertificateCombinedException;
import com.owncloud.android.lib.common.network.NetworkUtils;
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
import com.owncloud.android.lib.common.operations.RemoteOperation;
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
@ -115,6 +110,7 @@ import com.owncloud.android.operations.GetServerInfoOperation;
import com.owncloud.android.providers.DocumentsStorageProvider;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.services.OperationsService.OperationsServiceBinder;
import com.owncloud.android.ui.NextcloudWebViewClient;
import com.owncloud.android.ui.activity.FileDisplayActivity;
import com.owncloud.android.ui.dialog.IndeterminateProgressDialog;
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
@ -125,13 +121,8 @@ import com.owncloud.android.utils.PermissionUtil;
import com.owncloud.android.utils.theme.ThemeDrawableUtils;
import com.owncloud.android.utils.theme.ThemeToolbarUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.net.URLDecoder;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@ -179,7 +170,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
public static final byte ACTION_CREATE = 0;
public static final byte ACTION_UPDATE_EXPIRED_TOKEN = 2; // detected by the app
private static final String UNTRUSTED_CERT_DIALOG_TAG = "UNTRUSTED_CERT_DIALOG";
public static final String UNTRUSTED_CERT_DIALOG_TAG = "UNTRUSTED_CERT_DIALOG";
private static final String WAIT_DIALOG_TAG = "WAIT_DIALOG";
private static final String KEY_AUTH_IS_FIRST_ATTEMPT_TAG = "KEY_AUTH_IS_FIRST_ATTEMPT";
@ -408,7 +399,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
}
private void setClient() {
accountSetupWebviewBinding.loginWebview.setWebViewClient(new WebViewClient() {
accountSetupWebviewBinding.loginWebview.setWebViewClient(new NextcloudWebViewClient(getSupportFragmentManager()) {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
webViewFidoU2fBridge.delegateShouldInterceptRequest(view, request);
@ -447,21 +438,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
getWindow().setNavigationBarColor(primaryColor);
}
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
X509Certificate cert = getX509CertificateFromError(error);
try {
if (cert != null && NetworkUtils.isCertInKnownServersStore(cert, getApplicationContext())) {
handler.proceed();
} else {
showUntrustedCertDialog(cert, error, handler);
}
} catch (Exception e) {
Log_OC.e(TAG, "Cert could not be verified");
}
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE);
accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE);
@ -1390,23 +1366,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
return false; // always return false to grant that the software keyboard is hidden anyway
}
/**
* Show untrusted cert dialog
*/
public void showUntrustedCertDialog(X509Certificate x509Certificate, SslError error, SslErrorHandler handler) {
// Show a dialog with the certificate info
SslUntrustedCertDialog dialog;
if (x509Certificate == null) {
dialog = SslUntrustedCertDialog.newInstanceForEmptySslError(error, handler);
} else {
dialog = SslUntrustedCertDialog.newInstanceForFullSslError(x509Certificate, error, handler);
}
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.addToBackStack(null);
dialog.show(ft, UNTRUSTED_CERT_DIALOG_TAG);
}
/**
* Show untrusted cert dialog
@ -1515,30 +1474,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
}
}
/**
* Obtain the X509Certificate from SslError
*
* @param error SslError
* @return X509Certificate from error
*/
public static X509Certificate getX509CertificateFromError(SslError error) {
Bundle bundle = SslCertificate.saveState(error.getCertificate());
X509Certificate x509Certificate;
byte[] bytes = bundle.getByteArray("x509-certificate");
if (bytes == null) {
x509Certificate = null;
} else {
try {
CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
x509Certificate = (X509Certificate) cert;
} catch (CertificateException e) {
x509Certificate = null;
}
}
return x509Certificate;
}
/**
* Called from SslValidatorDialog when a new server certificate was correctly saved.
*/

View file

@ -0,0 +1,80 @@
package com.owncloud.android.ui
import android.annotation.SuppressLint
import android.net.http.SslCertificate
import android.net.http.SslError
import android.webkit.SslErrorHandler
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.fragment.app.FragmentManager
import com.owncloud.android.authentication.AuthenticatorActivity
import com.owncloud.android.lib.common.network.NetworkUtils
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.ui.dialog.SslUntrustedCertDialog
import java.io.ByteArrayInputStream
import java.security.cert.CertificateException
import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate
open class NextcloudWebViewClient(val supportFragmentManager: FragmentManager) : WebViewClient() {
private val tag: String? = NextcloudWebViewClient::class.simpleName
@Suppress("TooGenericExceptionCaught")
@SuppressLint("WebViewClientOnReceivedSslError")
override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler, error: SslError?) {
val cert = error?.let { getX509CertificateFromError(it) }
try {
if (NetworkUtils.isCertInKnownServersStore(cert, view?.context?.applicationContext)) {
handler.proceed()
} else {
showUntrustedCertDialog(cert, error, handler)
}
} catch (e: Exception) {
Log_OC.e(tag, "Cert could not be verified")
}
}
/**
* Obtain the X509Certificate from SslError
*
* @param error SslError
* @return X509Certificate from error
*/
open fun getX509CertificateFromError(error: SslError): X509Certificate? {
val bundle = SslCertificate.saveState(error.certificate)
val x509Certificate: X509Certificate?
val bytes = bundle.getByteArray("x509-certificate")
x509Certificate = if (bytes == null) {
null
} else {
try {
val certFactory = CertificateFactory.getInstance("X.509")
val cert = certFactory.generateCertificate(ByteArrayInputStream(bytes))
cert as X509Certificate
} catch (e: CertificateException) {
null
}
}
return x509Certificate
}
/**
* Show untrusted cert dialog
*/
private fun showUntrustedCertDialog(
x509Certificate: X509Certificate?,
error: SslError?,
handler: SslErrorHandler?
) {
// Show a dialog with the certificate info
val dialog: SslUntrustedCertDialog = if (x509Certificate == null) {
SslUntrustedCertDialog.newInstanceForEmptySslError(error, handler)
} else {
SslUntrustedCertDialog.newInstanceForFullSslError(x509Certificate, error, handler)
}
val fm: FragmentManager = supportFragmentManager
val ft = fm.beginTransaction()
ft.addToBackStack(null)
dialog.show(ft, AuthenticatorActivity.UNTRUSTED_CERT_DIALOG_TAG)
}
}

View file

@ -31,13 +31,13 @@ import android.view.Window;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import com.owncloud.android.MainApp;
import com.owncloud.android.R;
import com.owncloud.android.databinding.ExternalsiteWebviewBinding;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.ui.NextcloudWebViewClient;
import com.owncloud.android.utils.DisplayUtils;
import com.owncloud.android.utils.theme.ThemeToolbarUtils;
@ -141,7 +141,7 @@ public class ExternalSiteWebView extends FileActivity {
});
}
getWebView().setWebViewClient(new WebViewClient() {
getWebView().setWebViewClient(new NextcloudWebViewClient(getSupportFragmentManager()) {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
InputStream resources = getResources().openRawResource(R.raw.custom_error);
String customError = DisplayUtils.getData(resources);