浏览代码

Merge pull request #13076 from nextcloud/bugfix/npe-SslUntrustedCertDialog

Bugfix NPE & Convert to Kotlin SSL Untrusted Cert Dialog
Alper Öztürk 10 月之前
父节点
当前提交
4f2fdbccaf

+ 4 - 2
app/src/main/java/com/owncloud/android/ui/adapter/CertificateCombinedExceptionViewAdapter.java

@@ -16,18 +16,20 @@ import com.owncloud.android.databinding.SslUntrustedCertLayoutBinding;
 import com.owncloud.android.lib.common.network.CertificateCombinedException;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 
+import androidx.annotation.NonNull;
+
 public class CertificateCombinedExceptionViewAdapter implements SslUntrustedCertDialog.ErrorViewAdapter {
 
     //private final static String TAG = CertificateCombinedExceptionViewAdapter.class.getSimpleName();
 
-    private CertificateCombinedException mSslException;
+    private final CertificateCombinedException mSslException;
 
     public CertificateCombinedExceptionViewAdapter(CertificateCombinedException sslException) {
         mSslException = sslException;
     }
 
     @Override
-    public void updateErrorView(SslUntrustedCertLayoutBinding binding) {
+    public void updateErrorView(@NonNull SslUntrustedCertLayoutBinding binding) {
         /// clean
         binding.reasonNoInfoAboutError.setVisibility(View.GONE);
 

+ 2 - 2
app/src/main/java/com/owncloud/android/ui/adapter/SslCertificateViewAdapter.java

@@ -24,7 +24,7 @@ public class SslCertificateViewAdapter implements SslUntrustedCertDialog.Certifi
 
     //private final static String TAG = SslCertificateViewAdapter.class.getSimpleName();
 
-    private SslCertificate mCertificate;
+    private final SslCertificate mCertificate;
 
     /**
      * Constructor
@@ -36,7 +36,7 @@ public class SslCertificateViewAdapter implements SslUntrustedCertDialog.Certifi
     }
 
     @Override
-    public void updateCertificateView(SslUntrustedCertLayoutBinding binding) {
+    public void updateCertificateView(@NonNull SslUntrustedCertLayoutBinding binding) {
         if (mCertificate != null) {
             binding.nullCert.setVisibility(View.GONE);
             showSubject(mCertificate.getIssuedTo(), binding);

+ 4 - 2
app/src/main/java/com/owncloud/android/ui/adapter/SslErrorViewAdapter.java

@@ -14,6 +14,8 @@ import android.view.View;
 import com.owncloud.android.databinding.SslUntrustedCertLayoutBinding;
 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
 
+import androidx.annotation.NonNull;
+
 /**
  * Dialog to show an Untrusted Certificate
  */
@@ -21,14 +23,14 @@ public class SslErrorViewAdapter implements SslUntrustedCertDialog.ErrorViewAdap
 
     //private final static String TAG = SslErrorViewAdapter.class.getSimpleName();
 
-    private SslError mSslError;
+    private final SslError mSslError;
 
     public SslErrorViewAdapter(SslError sslError) {
         mSslError = sslError;
     }
 
     @Override
-    public void updateErrorView(SslUntrustedCertLayoutBinding binding) {
+    public void updateErrorView(@NonNull SslUntrustedCertLayoutBinding binding) {
         /// clean
         binding.reasonNoInfoAboutError.setVisibility(View.GONE);
 

+ 3 - 3
app/src/main/java/com/owncloud/android/ui/adapter/X509CertificateViewAdapter.java

@@ -37,7 +37,7 @@ import androidx.annotation.NonNull;
  */
 public class X509CertificateViewAdapter implements SslUntrustedCertDialog.CertificateViewAdapter {
 
-    private X509Certificate mCertificate;
+    private final X509Certificate mCertificate;
 
     private static final String TAG = X509CertificateViewAdapter.class.getSimpleName();
 
@@ -46,7 +46,7 @@ public class X509CertificateViewAdapter implements SslUntrustedCertDialog.Certif
     }
 
     @Override
-    public void updateCertificateView(SslUntrustedCertLayoutBinding binding) {
+    public void updateCertificateView(@NonNull SslUntrustedCertLayoutBinding binding) {
         if (mCertificate != null) {
             binding.nullCert.setVisibility(View.GONE);
             showSubject(mCertificate.getSubjectX500Principal(), binding);
@@ -97,7 +97,7 @@ public class X509CertificateViewAdapter implements SslUntrustedCertDialog.Certif
 
     private String getDigestHexBytesWithColonsAndNewLines(Context context, final String digestType, final byte [] cert) {
         final byte[] rawDigest;
-        final String newLine = System.getProperty("line.separator");
+        final String newLine = System.lineSeparator();
 
         rawDigest = getDigest(digestType, cert);
 

+ 0 - 215
app/src/main/java/com/owncloud/android/ui/dialog/SslUntrustedCertDialog.java

@@ -1,215 +0,0 @@
-/*
- * Nextcloud - Android Client
- *
- * SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
- * SPDX-FileCopyrightText: 2018-2022 Andy Scherzinger <info@andy-scherzinger.de>
- * SPDX-FileCopyrightText: 2020 Stefan Niedermann <info@niedermann.it>
- * SPDX-FileCopyrightText: 2018 Tobias Kaminsky <tobias@kaminsky.me>
- * SPDX-FileCopyrightText: 2015 ownCloud Inc.
- * SPDX-FileCopyrightText: 2014 David A. Velasco <dvelasco@solidgear.es>
- * SPDX-FileCopyrightText: 2014 María Asensio Valverde <masensio@solidgear.es>
- * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
- */
-package com.owncloud.android.ui.dialog;
-
-import android.app.Activity;
-import android.app.Dialog;
-import android.net.http.SslError;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.Window;
-import android.webkit.SslErrorHandler;
-import android.widget.Button;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.nextcloud.client.di.Injectable;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.SslUntrustedCertLayoutBinding;
-import com.owncloud.android.lib.common.network.CertificateCombinedException;
-import com.owncloud.android.lib.common.network.NetworkUtils;
-import com.owncloud.android.lib.common.utils.Log_OC;
-import com.owncloud.android.ui.adapter.CertificateCombinedExceptionViewAdapter;
-import com.owncloud.android.ui.adapter.SslCertificateViewAdapter;
-import com.owncloud.android.ui.adapter.SslErrorViewAdapter;
-import com.owncloud.android.ui.adapter.X509CertificateViewAdapter;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.cert.X509Certificate;
-
-import javax.inject.Inject;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.DialogFragment;
-
-/**
- * Dialog to show information about an untrusted certificate and allow the user to decide trust on it or not.
- * Abstract implementation of common functionality for different dialogs that get the information about the error and
- * the certificate from different classes.
- */
-public class SslUntrustedCertDialog extends DialogFragment implements Injectable {
-
-    private final static String TAG = SslUntrustedCertDialog.class.getSimpleName();
-
-    @Inject ViewThemeUtils viewThemeUtils;
-
-    protected SslUntrustedCertLayoutBinding binding;
-    protected SslErrorHandler mHandler;
-    protected X509Certificate m509Certificate;
-
-    private ErrorViewAdapter mErrorViewAdapter;
-    private CertificateViewAdapter mCertificateViewAdapter;
-
-    public static SslUntrustedCertDialog newInstanceForEmptySslError(SslError error, SslErrorHandler handler) {
-        if (error == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter error == null");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter handler == null");
-        }
-        SslUntrustedCertDialog dialog = new SslUntrustedCertDialog();
-        dialog.mHandler = handler;
-        dialog.mErrorViewAdapter = new SslErrorViewAdapter(error);
-        dialog.mCertificateViewAdapter = new SslCertificateViewAdapter(error.getCertificate());
-        return dialog;
-    }
-
-    public static SslUntrustedCertDialog newInstanceForFullSslError(CertificateCombinedException sslException) {
-        if (sslException == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter sslException == null");
-        }
-        SslUntrustedCertDialog dialog = new SslUntrustedCertDialog();
-        dialog.m509Certificate = sslException.getServerCertificate();
-        dialog.mErrorViewAdapter = new CertificateCombinedExceptionViewAdapter(sslException);
-        dialog.mCertificateViewAdapter = new X509CertificateViewAdapter(sslException.getServerCertificate());
-        return dialog;
-    }
-
-    public static SslUntrustedCertDialog newInstanceForFullSslError(X509Certificate cert, SslError error, SslErrorHandler handler) {
-        if (cert == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter cert == null");
-        }
-        if (error == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter error == null");
-        }
-        if (handler == null) {
-            throw new IllegalArgumentException("Trying to create instance with parameter handler == null");
-        }
-        SslUntrustedCertDialog dialog = new SslUntrustedCertDialog();
-        dialog.m509Certificate = cert;
-        dialog.mHandler = handler;
-        dialog.mErrorViewAdapter = new SslErrorViewAdapter(error);
-        dialog.mCertificateViewAdapter = new X509CertificateViewAdapter(cert);
-        return dialog;
-    }
-
-    @Override
-    public void onAttach(@NonNull Activity activity) {
-        Log_OC.d(TAG, "onAttach");
-        super.onAttach(activity);
-        if (!(activity instanceof OnSslUntrustedCertListener)) {
-            throw new IllegalArgumentException("The host activity must implement " + OnSslUntrustedCertListener.class.getCanonicalName());
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreate, savedInstanceState is " + savedInstanceState);
-        super.onCreate(savedInstanceState);
-        setRetainInstance(true);    // force to keep the state of the fragment on configuration changes (such as device rotations)
-        setCancelable(false);
-        binding = null;
-    }
-
-    @NonNull
-    @Override
-    public Dialog onCreateDialog(Bundle savedInstanceState) {
-        Log_OC.d(TAG, "onCreateDialog, savedInstanceState is " + savedInstanceState);
-
-        binding = SslUntrustedCertLayoutBinding.inflate(getLayoutInflater(), null, false);
-        binding.detailsScroll.setVisibility(View.GONE);
-        mErrorViewAdapter.updateErrorView(binding);
-
-        binding.ok.setOnClickListener(new OnCertificateTrusted());
-
-        binding.cancel.setOnClickListener(new OnCertificateNotTrusted());
-
-        binding.detailsBtn.setOnClickListener(v -> {
-            if (binding.detailsScroll.getVisibility() == View.VISIBLE) {
-                binding.detailsScroll.setVisibility(View.GONE);
-                ((Button) v).setText(R.string.ssl_validator_btn_details_see);
-
-            } else {
-                binding.detailsScroll.setVisibility(View.VISIBLE);
-                ((Button) v).setText(R.string.ssl_validator_btn_details_hide);
-                mCertificateViewAdapter.updateCertificateView(binding);
-            }
-        });
-
-
-        MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(binding.getRoot().getContext());
-        builder.setView(binding.getRoot());
-
-        viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.getRoot().getContext(), builder);
-
-        final Dialog dialog = builder.create();
-        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-        return dialog;
-    }
-
-    @Override
-    public void onDestroyView() {
-        Log_OC.d(TAG, "onDestroyView");
-        if (getDialog() != null && getRetainInstance()) {
-            getDialog().setDismissMessage(null);
-        }
-        super.onDestroyView();
-    }
-
-    private class OnCertificateNotTrusted implements OnClickListener {
-
-        @Override
-        public void onClick(View v) {
-            getDialog().cancel();
-            if (mHandler != null) {
-                mHandler.cancel();
-            }
-        }
-    }
-
-    private class OnCertificateTrusted implements OnClickListener {
-
-        @Override
-        public void onClick(View v) {
-            dismiss();
-            if (mHandler != null) {
-                mHandler.proceed();
-            }
-            if (m509Certificate != null) {
-                Activity activity = getActivity();
-                try {
-                    NetworkUtils.addCertToKnownServersStore(m509Certificate, activity);   // TODO make this asynchronously, it can take some time
-                    ((OnSslUntrustedCertListener)activity).onSavedCertificate();
-                } catch (GeneralSecurityException | IOException e) {
-                    ((OnSslUntrustedCertListener)activity).onFailedSavingCertificate();
-                    Log_OC.e(TAG, "Server certificate could not be saved in the known-servers trust store ", e);
-                }
-            }
-        }
-    }
-
-    public interface OnSslUntrustedCertListener {
-        void onSavedCertificate();
-        void onFailedSavingCertificate();
-    }
-
-    public interface ErrorViewAdapter {
-        void updateErrorView(SslUntrustedCertLayoutBinding binding);
-    }
-
-    public interface CertificateViewAdapter {
-        void updateCertificateView(SslUntrustedCertLayoutBinding binding);
-    }
-}

+ 213 - 0
app/src/main/java/com/owncloud/android/ui/dialog/SslUntrustedCertDialog.kt

@@ -0,0 +1,213 @@
+/*
+ * Nextcloud - Android Client
+ *
+ * SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
+ * SPDX-FileCopyrightText: 2018-2022 Andy Scherzinger <info@andy-scherzinger.de>
+ * SPDX-FileCopyrightText: 2020 Stefan Niedermann <info@niedermann.it>
+ * SPDX-FileCopyrightText: 2018 Tobias Kaminsky <tobias@kaminsky.me>
+ * SPDX-FileCopyrightText: 2015 ownCloud Inc.
+ * SPDX-FileCopyrightText: 2014 David A. Velasco <dvelasco@solidgear.es>
+ * SPDX-FileCopyrightText: 2014 María Asensio Valverde <masensio@solidgear.es>
+ * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only)
+ */
+package com.owncloud.android.ui.dialog
+
+import android.app.Dialog
+import android.content.Context
+import android.net.http.SslError
+import android.os.Bundle
+import android.view.View
+import android.view.Window
+import android.webkit.SslErrorHandler
+import android.widget.Button
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import com.nextcloud.client.di.Injectable
+import com.owncloud.android.R
+import com.owncloud.android.databinding.SslUntrustedCertLayoutBinding
+import com.owncloud.android.lib.common.network.CertificateCombinedException
+import com.owncloud.android.lib.common.network.NetworkUtils
+import com.owncloud.android.lib.common.utils.Log_OC
+import com.owncloud.android.ui.adapter.CertificateCombinedExceptionViewAdapter
+import com.owncloud.android.ui.adapter.SslCertificateViewAdapter
+import com.owncloud.android.ui.adapter.SslErrorViewAdapter
+import com.owncloud.android.ui.adapter.X509CertificateViewAdapter
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import java.io.IOException
+import java.security.GeneralSecurityException
+import java.security.cert.X509Certificate
+import javax.inject.Inject
+
+/**
+ * Dialog to show information about an untrusted certificate and allow the user to decide trust on it or not.
+ * Abstract implementation of common functionality for different dialogs that get the information about the error and
+ * the certificate from different classes.
+ */
+open class SslUntrustedCertDialog : DialogFragment(), Injectable {
+
+    @JvmField
+    @Inject
+    var viewThemeUtils: ViewThemeUtils? = null
+
+    protected var binding: SslUntrustedCertLayoutBinding? = null
+    protected var sslErrorHandler: SslErrorHandler? = null
+    protected var x509Certificate: X509Certificate? = null
+    private var errorViewAdapter: ErrorViewAdapter? = null
+    private var certificateViewAdapter: CertificateViewAdapter? = null
+
+    override fun onAttach(context: Context) {
+        Log_OC.d(TAG, "onAttach")
+        super.onAttach(context)
+        require(activity is OnSslUntrustedCertListener) {
+            "The host activity must implement " + OnSslUntrustedCertListener::class.java.canonicalName
+        }
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        Log_OC.d(TAG, "onCreate, savedInstanceState is $savedInstanceState")
+        super.onCreate(savedInstanceState)
+
+        // force to keep the state of the fragment on configuration changes (such as device rotations)
+        retainInstance = true
+        isCancelable = false
+        binding = null
+    }
+
+    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+        Log_OC.d(TAG, "onCreateDialog, savedInstanceState is $savedInstanceState")
+
+        val layoutBinding = SslUntrustedCertLayoutBinding.inflate(layoutInflater, null, false)
+        this.binding = layoutBinding
+
+        layoutBinding.run {
+            detailsScroll.visibility = View.GONE
+            errorViewAdapter?.updateErrorView(layoutBinding)
+
+            ok.setOnClickListener(OnCertificateTrusted())
+
+            cancel.setOnClickListener(OnCertificateNotTrusted())
+
+            detailsBtn.setOnClickListener { v: View ->
+                if (detailsScroll.visibility == View.VISIBLE) {
+                    detailsScroll.visibility = View.GONE
+                    (v as Button).setText(R.string.ssl_validator_btn_details_see)
+                } else {
+                    detailsScroll.visibility = View.VISIBLE
+                    (v as Button).setText(R.string.ssl_validator_btn_details_hide)
+                    certificateViewAdapter?.updateCertificateView(layoutBinding)
+                }
+            }
+        }
+
+        val builder = MaterialAlertDialogBuilder(requireContext()).apply {
+            setView(layoutBinding.getRoot())
+        }
+
+        viewThemeUtils?.dialog?.colorMaterialAlertDialogBackground(requireContext(), builder)
+
+        return builder.create().apply {
+            requestWindowFeature(Window.FEATURE_NO_TITLE)
+        }
+    }
+
+    override fun onDestroyView() {
+        Log_OC.d(TAG, "onDestroyView")
+        if (retainInstance) {
+            dialog?.setDismissMessage(null)
+        }
+        super.onDestroyView()
+    }
+
+    private inner class OnCertificateNotTrusted : View.OnClickListener {
+        override fun onClick(v: View) {
+            dialog?.cancel()
+            sslErrorHandler?.cancel()
+        }
+    }
+
+    private inner class OnCertificateTrusted : View.OnClickListener {
+        override fun onClick(v: View) {
+            dismiss()
+            sslErrorHandler?.proceed()
+
+            if (x509Certificate == null) {
+                Log_OC.d(TAG, "m509Certificate is null onClick dismissed")
+                return
+            }
+
+            if (activity == null) {
+                Log_OC.d(TAG, "activity is null onClick dismissed")
+                return
+            }
+
+            try {
+                // TODO make this asynchronously, it can take some time
+                NetworkUtils.addCertToKnownServersStore(x509Certificate, activity)
+                (activity as OnSslUntrustedCertListener?)?.onSavedCertificate()
+            } catch (e: GeneralSecurityException) {
+                (activity as OnSslUntrustedCertListener?)?.onFailedSavingCertificate()
+                Log_OC.e(TAG, "Server certificate could not be saved in the known-servers trust store ", e)
+            } catch (e: IOException) {
+                (activity as OnSslUntrustedCertListener?)?.onFailedSavingCertificate()
+                Log_OC.e(TAG, "Server certificate could not be saved in the known-servers trust store ", e)
+            }
+        }
+    }
+
+    interface OnSslUntrustedCertListener {
+        fun onSavedCertificate()
+        fun onFailedSavingCertificate()
+    }
+
+    interface ErrorViewAdapter {
+        fun updateErrorView(binding: SslUntrustedCertLayoutBinding)
+    }
+
+    interface CertificateViewAdapter {
+        fun updateCertificateView(binding: SslUntrustedCertLayoutBinding)
+    }
+
+    companion object {
+        private val TAG: String = SslUntrustedCertDialog::class.java.simpleName
+
+        @JvmStatic
+        fun newInstanceForEmptySslError(error: SslError?, handler: SslErrorHandler?): SslUntrustedCertDialog {
+            requireNotNull(error) { "Trying to create instance with parameter error == null" }
+            requireNotNull(handler) { "Trying to create instance with parameter handler == null" }
+
+            return SslUntrustedCertDialog().apply {
+                sslErrorHandler = handler
+                errorViewAdapter = SslErrorViewAdapter(error)
+                certificateViewAdapter = SslCertificateViewAdapter(error.certificate)
+            }
+        }
+
+        @JvmStatic
+        fun newInstanceForFullSslError(sslException: CertificateCombinedException?): SslUntrustedCertDialog {
+            requireNotNull(sslException) { "Trying to create instance with parameter sslException == null" }
+
+            return SslUntrustedCertDialog().apply {
+                x509Certificate = sslException.serverCertificate
+                errorViewAdapter = CertificateCombinedExceptionViewAdapter(sslException)
+                certificateViewAdapter = X509CertificateViewAdapter(sslException.serverCertificate)
+            }
+        }
+
+        fun newInstanceForFullSslError(
+            cert: X509Certificate?,
+            error: SslError?,
+            handler: SslErrorHandler?
+        ): SslUntrustedCertDialog {
+            requireNotNull(cert) { "Trying to create instance with parameter cert == null" }
+            requireNotNull(error) { "Trying to create instance with parameter error == null" }
+            requireNotNull(handler) { "Trying to create instance with parameter handler == null" }
+
+            return SslUntrustedCertDialog().apply {
+                x509Certificate = cert
+                sslErrorHandler = handler
+                errorViewAdapter = SslErrorViewAdapter(error)
+                certificateViewAdapter = X509CertificateViewAdapter(cert)
+            }
+        }
+    }
+}