Added new dialog for SSL errors showing information saved in SslCertificate
[pub/Android/ownCloud.git] / src / com / owncloud / android / authentication / SsoWebViewClient.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012-2013 ownCloud Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 */
17
18 package com.owncloud.android.authentication;
19
20 import java.io.ByteArrayInputStream;
21 import java.lang.ref.WeakReference;
22 import java.security.cert.Certificate;
23 import java.security.cert.CertificateException;
24 import java.security.cert.CertificateFactory;
25 import java.security.cert.X509Certificate;
26
27 import com.actionbarsherlock.app.SherlockFragmentActivity;
28 import com.owncloud.android.R;
29 import com.owncloud.android.lib.common.network.NetworkUtils;
30 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog;
31 import com.owncloud.android.ui.dialog.SslUntrustedCertDialogABSTRACT;
32 import com.owncloud.android.ui.dialog.SslUntrustedCertDialog.OnSslUntrustedCertListener;
33 import com.owncloud.android.utils.Log_OC;
34
35 import android.app.AlertDialog;
36 import android.content.Context;
37 import android.content.DialogInterface;
38 import android.graphics.Bitmap;
39 import android.net.http.SslCertificate;
40 import android.net.http.SslError;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.Message;
44 import android.support.v4.app.FragmentActivity;
45 import android.support.v4.app.FragmentManager;
46 import android.support.v4.app.FragmentTransaction;
47 import android.view.KeyEvent;
48 import android.view.View;
49 import android.webkit.CookieManager;
50 import android.webkit.HttpAuthHandler;
51 import android.webkit.SslErrorHandler;
52 import android.webkit.WebResourceResponse;
53 import android.webkit.WebView;
54 import android.webkit.WebViewClient;
55
56
57 /**
58 * Custom {@link WebViewClient} client aimed to catch the end of a single-sign-on process
59 * running in the {@link WebView} that is attached to.
60 *
61 * Assumes that the single-sign-on is kept thanks to a cookie set at the end of the
62 * authentication process.
63 *
64 * @author David A. Velasco
65 */
66 public class SsoWebViewClient extends WebViewClient implements OnSslUntrustedCertListener {
67
68 private static final String TAG = SsoWebViewClient.class.getSimpleName();
69
70 public final static String DIALOG_UNTRUSTED_CERT = "UNTRUSTED CERT";
71
72 public interface SsoWebViewClientListener {
73 public void onSsoFinished(String sessionCookie);
74 }
75
76 private Context mContext;
77 private Handler mListenerHandler;
78 private WeakReference<SsoWebViewClientListener> mListenerRef;
79 private String mTargetUrl;
80 private String mLastReloadedUrlAtError;
81
82 public SsoWebViewClient (Context context, Handler listenerHandler, SsoWebViewClientListener listener) {
83 mContext = context;
84 mListenerHandler = listenerHandler;
85 mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
86 mTargetUrl = "fake://url.to.be.set";
87 mLastReloadedUrlAtError = null;
88 }
89
90 public String getTargetUrl() {
91 return mTargetUrl;
92 }
93
94 public void setTargetUrl(String targetUrl) {
95 mTargetUrl = targetUrl;
96 }
97
98 @Override
99 public void onPageStarted (WebView view, String url, Bitmap favicon) {
100 Log_OC.d(TAG, "onPageStarted : " + url);
101 super.onPageStarted(view, url, favicon);
102 }
103
104 @Override
105 public void onFormResubmission (WebView view, Message dontResend, Message resend) {
106 Log_OC.d(TAG, "onFormResubMission ");
107
108 // necessary to grant reload of last page when device orientation is changed after sending a form
109 resend.sendToTarget();
110 }
111
112 @Override
113 public boolean shouldOverrideUrlLoading(WebView view, String url) {
114 return false;
115 }
116
117 @Override
118 public void onReceivedError (WebView view, int errorCode, String description, String failingUrl) {
119 Log_OC.e(TAG, "onReceivedError : " + failingUrl + ", code " + errorCode + ", description: " + description);
120 if (!failingUrl.equals(mLastReloadedUrlAtError)) {
121 view.reload();
122 mLastReloadedUrlAtError = failingUrl;
123 } else {
124 mLastReloadedUrlAtError = null;
125 super.onReceivedError(view, errorCode, description, failingUrl);
126 }
127 }
128
129 @Override
130 public void onPageFinished (WebView view, String url) {
131 Log_OC.d(TAG, "onPageFinished : " + url);
132 mLastReloadedUrlAtError = null;
133 if (url.startsWith(mTargetUrl)) {
134 view.setVisibility(View.GONE);
135 CookieManager cookieManager = CookieManager.getInstance();
136 final String cookies = cookieManager.getCookie(url);
137 Log_OC.d(TAG, "Cookies: " + cookies);
138 if (mListenerHandler != null && mListenerRef != null) {
139 // this is good idea because onPageFinished is not running in the UI thread
140 mListenerHandler.post(new Runnable() {
141 @Override
142 public void run() {
143 SsoWebViewClientListener listener = mListenerRef.get();
144 if (listener != null) {
145 // Send Cookies to the listener
146 listener.onSsoFinished(cookies);
147 }
148 }
149 });
150 }
151 }
152 }
153
154
155 @Override
156 public void doUpdateVisitedHistory (WebView view, String url, boolean isReload) {
157 Log_OC.d(TAG, "doUpdateVisitedHistory : " + url);
158 }
159
160 @Override
161 public void onReceivedSslError (final WebView view, final SslErrorHandler handler, SslError error) {
162 Log_OC.d(TAG, "onReceivedSslError : " + error);
163 // Test 1
164 X509Certificate x509Certificate = getX509CertificateFromError(error);
165 boolean isKnownServer = false;
166
167 if (x509Certificate != null) {
168 Log_OC.d(TAG, "------>>>>> x509Certificate " + x509Certificate.toString());
169
170 try {
171 isKnownServer = NetworkUtils.isCertInKnownServersStore((Certificate) x509Certificate, mContext);
172 } catch (Exception e) {
173 Log_OC.e(TAG, "Exception: " + e.getMessage());
174 }
175 }
176
177 if (isKnownServer) {
178 handler.proceed();
179 } else if (x509Certificate != null) {
180 // Show a dialog with all the certificate info
181 SslUntrustedCertDialog dialog = SslUntrustedCertDialog.newInstance(mContext, x509Certificate, this, handler);
182 FragmentManager fm = ((SherlockFragmentActivity)mContext).getSupportFragmentManager();
183 FragmentTransaction ft = fm.beginTransaction();
184 dialog.show(ft, DIALOG_UNTRUSTED_CERT);
185 handler.cancel();
186 } else {
187 // Show a dialog with the certificate information available in SslError (not full)
188 SslUntrustedCertDialogABSTRACT dialog = SslUntrustedCertDialogABSTRACT.newInstanceForEmptySslError(error, handler);
189 FragmentManager fm = ((SherlockFragmentActivity)mContext).getSupportFragmentManager();
190 FragmentTransaction ft = fm.beginTransaction();
191 dialog.show(ft, DIALOG_UNTRUSTED_CERT);
192 // let's forward the handler, and see what happens...
193 }
194 }
195
196 /**
197 * Obtain the X509Certificate from SslError
198 * @param error SslError
199 * @return X509Certificate from error
200 */
201 public X509Certificate getX509CertificateFromError (SslError error) {
202 Bundle bundle = SslCertificate.saveState(error.getCertificate());
203 X509Certificate x509Certificate;
204 byte[] bytes = bundle.getByteArray("x509-certificate");
205 if (bytes == null) {
206 x509Certificate = null;
207 } else {
208 try {
209 CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
210 Certificate cert = certFactory.generateCertificate(new ByteArrayInputStream(bytes));
211 x509Certificate = (X509Certificate) cert;
212 } catch (CertificateException e) {
213 x509Certificate = null;
214 }
215 }
216 return x509Certificate;
217 }
218
219 @Override
220 public void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm) {
221 Log_OC.d(TAG, "onReceivedHttpAuthRequest : " + host);
222 }
223
224 @Override
225 public WebResourceResponse shouldInterceptRequest (WebView view, String url) {
226 Log_OC.d(TAG, "shouldInterceptRequest : " + url);
227 return null;
228 }
229
230 @Override
231 public void onLoadResource (WebView view, String url) {
232 Log_OC.d(TAG, "onLoadResource : " + url);
233 }
234
235 @Override
236 public void onReceivedLoginRequest (WebView view, String realm, String account, String args) {
237 Log_OC.d(TAG, "onReceivedLoginRequest : " + realm + ", " + account + ", " + args);
238 }
239
240 @Override
241 public void onScaleChanged (WebView view, float oldScale, float newScale) {
242 Log_OC.d(TAG, "onScaleChanged : " + oldScale + " -> " + newScale);
243 super.onScaleChanged(view, oldScale, newScale);
244 }
245
246 @Override
247 public void onUnhandledKeyEvent (WebView view, KeyEvent event) {
248 Log_OC.d(TAG, "onUnhandledKeyEvent : " + event);
249 }
250
251 @Override
252 public boolean shouldOverrideKeyEvent (WebView view, KeyEvent event) {
253 Log_OC.d(TAG, "shouldOverrideKeyEvent : " + event);
254 return false;
255 }
256
257 @Override
258 public void onFailedSavingCertificate() {
259 AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
260 builder.setMessage(mContext.getString(R.string.ssl_validator_not_saved));
261 builder.setCancelable(false);
262 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
263 @Override
264 public void onClick(DialogInterface dialog, int which) {
265 dialog.dismiss();
266 };
267 });
268 builder.create().show();
269
270 }
271
272 }