Эх сурвалжийг харах

Session cookie caught and saved to use in requests after successful SAML-based federated SSO

David A. Velasco 11 жил өмнө
parent
commit
52bc433bb1

+ 2 - 1
src/com/owncloud/android/authentication/AccountAuthenticator.java

@@ -259,7 +259,8 @@ public class AccountAuthenticator extends AbstractAccountAuthenticator {
         if (!authTokenType.equals(AUTH_TOKEN_TYPE) &&
             !authTokenType.equals(AUTH_TOKEN_TYPE_PASSWORD) &&
             !authTokenType.equals(AUTH_TOKEN_TYPE_ACCESS_TOKEN) &&
-            !authTokenType.equals(AUTH_TOKEN_TYPE_REFRESH_TOKEN) ) {
+            !authTokenType.equals(AUTH_TOKEN_TYPE_REFRESH_TOKEN) &&
+            !authTokenType.equals(AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE)) {
             throw new UnsupportedAuthTokenTypeException();
         }
     }

+ 58 - 20
src/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -22,6 +22,7 @@ import com.owncloud.android.Log_OC;
 import com.owncloud.android.ui.dialog.SslValidatorDialog;
 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
 import com.owncloud.android.utils.OwnCloudVersion;
+import com.owncloud.android.authentication.SsoWebViewClient.SsoWebViewClientListener;
 import com.owncloud.android.network.OwnCloudClientUtils;
 import com.owncloud.android.operations.OwnCloudServerCheckOperation;
 import com.owncloud.android.operations.ExistenceCheckOperation;
@@ -79,7 +80,7 @@ import eu.alefzero.webdav.WebdavClient;
  * @author David A. Velasco
  */
 public class AuthenticatorActivity extends AccountAuthenticatorActivity
-implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener {
+implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener, OnEditorActionListener, SsoWebViewClientListener {
 
     private static final String TAG = AuthenticatorActivity.class.getSimpleName();
 
@@ -146,7 +147,6 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
     private EditText mPasswordInput;
     
     private CheckBox mOAuth2Check;
-    private String mOAuthAccessToken;
     
     private TextView mOAuthAuthEndpointText;
     private TextView mOAuthTokenEndpointText;
@@ -156,6 +156,8 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
     private SsoWebViewClient mWebViewClient;
     
     private View mOkButton;
+    
+    private String mAuthToken;
 
 
     /**
@@ -337,7 +339,7 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
         cookieManager.setAcceptCookie(true);
         //cookieManager.removeSessionCookie();        
 
-        mWebViewClient = new SsoWebViewClient(this);
+        mWebViewClient = new SsoWebViewClient(mHandler, this);
         mSsoWebView.setWebViewClient(mWebViewClient);
         WebSettings webSettings = mSsoWebView.getSettings();
         webSettings.setJavaScriptEnabled(true);
@@ -1026,11 +1028,11 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
             showDialog(DIALOG_LOGIN_PROGRESS);
 
             /// time to test the retrieved access token on the ownCloud server
-            mOAuthAccessToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);
-            Log_OC.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken);
+            mAuthToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Constants.KEY_ACCESS_TOKEN);
+            Log_OC.d(TAG, "Got ACCESS TOKEN: " + mAuthToken);
             mAuthCheckOperation = new ExistenceCheckOperation("", this, false);
             WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this, true);
-            client.setBearerCredentials(mOAuthAccessToken);
+            client.setBearerCredentials(mAuthToken);
             mAuthCheckOperation.execute(client, this, mHandler);
 
         } else {
@@ -1110,11 +1112,17 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
         Bundle response = new Bundle();
         response.putString(AccountManager.KEY_ACCOUNT_NAME, mAccount.name);
         response.putString(AccountManager.KEY_ACCOUNT_TYPE, mAccount.type);
-        boolean isOAuth = mOAuth2Check.isChecked();
-        if (isOAuth) {
-            response.putString(AccountManager.KEY_AUTHTOKEN, mOAuthAccessToken);
+        
+        if (AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(mCurrentAuthTokenType)) { 
+            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);
+            // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention
+            mAccountMgr.setAuthToken(mAccount, mCurrentAuthTokenType, mAuthToken);
+            
+        } else if (AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE.equals(mCurrentAuthTokenType)) {
+            response.putString(AccountManager.KEY_AUTHTOKEN, mAuthToken);
             // the next line is necessary; by now, notifications are calling directly to the AuthenticatorActivity to update, without AccountManager intervention
-            mAccountMgr.setAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken);
+            mAccountMgr.setAuthToken(mAccount, mCurrentAuthTokenType, mAuthToken);
+            
         } else {
             response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
             mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
@@ -1132,11 +1140,15 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
      */
     private void createAccount() {
         /// create and save new ownCloud account
-        boolean isOAuth = mOAuth2Check.isChecked();
+        boolean isOAuth = AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(mCurrentAuthTokenType);
+        boolean isSaml =  AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE.equals(mCurrentAuthTokenType);
 
         Uri uri = Uri.parse(mHostBaseUrl);
         String username = mUsernameInput.getText().toString().trim();
-        if (isOAuth) {
+        if (isSaml) {
+            username = mAccountNameInput.getText().toString().trim();
+            
+        } else if (isOAuth) {
             username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
         }            
         String accountName = username + "@" + uri.getHost();
@@ -1144,8 +1156,8 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
             accountName += ":" + uri.getPort();
         }
         mAccount = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);
-        if (isOAuth) {
-            mAccountMgr.addAccountExplicitly(mAccount, "", null);  // with our implementation, the password is never input in the app
+        if (isOAuth || isSaml) {
+            mAccountMgr.addAccountExplicitly(mAccount, "", null);  // with external authorizations, the password is never input in the app
         } else {
             mAccountMgr.addAccountExplicitly(mAccount, mPasswordInput.getText().toString(), null);
         }
@@ -1164,17 +1176,20 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
         final Intent intent = new Intent();       
         intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,    AccountAuthenticator.ACCOUNT_TYPE);
         intent.putExtra(AccountManager.KEY_ACCOUNT_NAME,    mAccount.name);
-        if (!isOAuth)
-            intent.putExtra(AccountManager.KEY_AUTHTOKEN,   AccountAuthenticator.ACCOUNT_TYPE); // TODO check this; not sure it's right; maybe
+        /*if (!isOAuth)
+            intent.putExtra(AccountManager.KEY_AUTHTOKEN,   AccountAuthenticator.ACCOUNT_TYPE); */
         intent.putExtra(AccountManager.KEY_USERDATA,        username);
-        if (isOAuth) {
-            mAccountMgr.setAuthToken(mAccount, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken);
+        if (isOAuth || isSaml) {
+            mAccountMgr.setAuthToken(mAccount, mCurrentAuthTokenType, mAuthToken);
         }
         /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA
         mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_VERSION,    mDiscoveredVersion.toString());
         mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_OC_BASE_URL,   mHostBaseUrl);
-        if (isOAuth)
-            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");  // TODO this flag should be unnecessary
+        if (isSaml) {
+            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO, "TRUE"); 
+        } else if (isOAuth) {
+            mAccountMgr.setUserData(mAccount, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE");  
+        }
 
         setAccountAuthenticatorResult(intent.getExtras());
         setResult(RESULT_OK, intent);
@@ -1482,4 +1497,27 @@ implements  OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeList
         public abstract boolean onDrawableTouch(final MotionEvent event);
     }
 
+
+    @Override
+    public void onSsoFinished(String sessionCookie) {
+        //Toast.makeText(this, "got cookies: " + sessionCookie, Toast.LENGTH_LONG).show();
+        
+        if (sessionCookie != null && sessionCookie.length() > 0) {
+            Log_OC.d(TAG, "Successful SSO - time to save the account");
+            mAuthToken = sessionCookie;
+            if (mAction == ACTION_CREATE) {
+                createAccount();
+
+            } else {
+                updateToken();
+            }
+
+            finish();
+
+        } else { 
+            // TODO - show fail
+            Log_OC.d(TAG, "SSO failed");
+        }
+    }
+
 }

+ 25 - 7
src/com/owncloud/android/authentication/SsoWebViewClient.java

@@ -17,13 +17,14 @@
 
 package com.owncloud.android.authentication;
 
-import android.content.Context;
+import java.lang.ref.WeakReference;
+
 import android.graphics.Bitmap;
+import android.os.Handler;
 import android.view.View;
 import android.webkit.CookieManager;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
-import android.widget.Toast;
 
 import com.owncloud.android.Log_OC;
 
@@ -40,11 +41,17 @@ public class SsoWebViewClient extends WebViewClient {
         
     private static final String TAG = SsoWebViewClient.class.getSimpleName();
     
-    private Context mContext;
+    public interface SsoWebViewClientListener {
+        public void onSsoFinished(String sessionCookie);
+    }
+    
+    private Handler mListenerHandler;
+    private WeakReference<SsoWebViewClientListener> mListenerRef;
     private String mTargetUrl;
     
-    public SsoWebViewClient (Context context) {
-        mContext = context;
+    public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) {
+        mListenerHandler = listenerHandler;
+        mListenerRef = new WeakReference<SsoWebViewClient.SsoWebViewClientListener>(listener);
         mTargetUrl = "fake://url.to.be.set";
     }
     
@@ -62,8 +69,19 @@ public class SsoWebViewClient extends WebViewClient {
         if (url.startsWith(mTargetUrl)) {
             view.setVisibility(View.GONE);
             CookieManager cookieManager = CookieManager.getInstance();
-            String cookies = cookieManager.getCookie(url);
-            Toast.makeText(mContext, "got cookies: " + cookies, Toast.LENGTH_LONG).show();
+            final String cookies = cookieManager.getCookie(url);
+            if (mListenerHandler != null && mListenerRef != null) {
+                // this is good idea because onPageStarted is not running in the UI thread
+                mListenerHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        SsoWebViewClientListener listener = mListenerRef.get();
+                        if (listener != null) {
+                            listener.onSsoFinished(cookies);
+                        }
+                    }
+                });
+            }
         }
     }
     

+ 12 - 2
src/com/owncloud/android/network/OwnCloudClientUtils.java

@@ -96,6 +96,10 @@ public class OwnCloudClientUtils {
             String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
             client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
         
+        } else if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null) {    // TODO avoid a call to getUserData here
+            String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE, false);
+            client.setSsoSessionCookie(accessToken);
+            
         } else {
             String username = account.name.substring(0, account.name.lastIndexOf('@'));
             //String password = am.getPassword(account);
@@ -115,10 +119,16 @@ public class OwnCloudClientUtils {
             AccountManagerFuture<Bundle> future =  am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, null, currentActivity, null, null);
             Bundle result = future.getResult();
             String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
-            //String accessToken = am.blockingGetAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, false);
             if (accessToken == null) throw new AuthenticatorException("WTF!");
             client.setBearerCredentials(accessToken);   // TODO not assume that the access token is a bearer token
-            
+
+        } else if (am.getUserData(account, AccountAuthenticator.KEY_SUPPORTS_SAML_WEB_SSO) != null) {    // TODO avoid a call to getUserData here
+            AccountManagerFuture<Bundle> future =  am.getAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_SAML_WEB_SSO_SESSION_COOKIE, null, currentActivity, null, null);
+            Bundle result = future.getResult();
+            String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN);
+            if (accessToken == null) throw new AuthenticatorException("WTF!");
+            client.setSsoSessionCookie(accessToken);
+
         } else {
             String username = account.name.substring(0, account.name.lastIndexOf('@'));
             //String password = am.getPassword(account);

+ 1 - 1
src/com/owncloud/android/operations/RemoteOperationResult.java

@@ -126,7 +126,7 @@ public class RemoteOperationResult implements Serializable {
                 break;
             default:
                 mCode = ResultCode.UNHANDLED_HTTP_CODE;
-                Log_OC.d(TAG, "RemoteOperationResult has prcessed UNHANDLED_HTTP_CODE: " + httpCode);
+                Log_OC.d(TAG, "RemoteOperationResult has processed UNHANDLED_HTTP_CODE: " + httpCode);
             }
         }
     }

+ 13 - 4
src/com/owncloud/android/operations/SynchronizeFolderOperation.java

@@ -208,9 +208,9 @@ public class SynchronizeFolderOperation extends RemoteOperation {
                         } else {
                             mFailsInFavouritesFound++;
                             if (contentsResult.getException() != null) {
-                                Log_OC.d(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
+                                Log_OC.e(TAG, "Error while synchronizing favourites : " +  contentsResult.getLogMessage(), contentsResult.getException());
                             } else {
-                                Log_OC.d(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
+                                Log_OC.e(TAG, "Error while synchronizing favourites : " + contentsResult.getLogMessage());
                             }
                         }
                     }   // won't let these fails break the synchronization process
@@ -247,16 +247,25 @@ public class SynchronizeFolderOperation extends RemoteOperation {
             } else {
                 result = new RemoteOperationResult(false, status);
             }
-            Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+            
             
             
         } catch (Exception e) {
             result = new RemoteOperationResult(e);
-            Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+            
 
         } finally {
             if (query != null)
                 query.releaseConnection();  // let the connection available for other methods
+            if (result.isSuccess()) {
+                Log_OC.i(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+            } else {
+                if (result.isException()) {
+                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage(), result.getException());
+                } else {
+                    Log_OC.e(TAG, "Synchronizing " + mAccount.name + ", folder " + mRemotePath + ": " + result.getLogMessage());
+                }
+            }
         }
         
         return result;

+ 31 - 4
src/eu/alefzero/webdav/WebdavClient.java

@@ -27,11 +27,13 @@ import org.apache.commons.httpclient.Credentials;
 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpConnectionManager;
 import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethod;
 import org.apache.commons.httpclient.HttpMethodBase;
 import org.apache.commons.httpclient.HttpVersion;
 import org.apache.commons.httpclient.UsernamePasswordCredentials;
 import org.apache.commons.httpclient.auth.AuthPolicy;
 import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.cookie.CookiePolicy;
 import org.apache.commons.httpclient.methods.HeadMethod;
 import org.apache.commons.httpclient.params.HttpMethodParams;
 import org.apache.http.HttpStatus;
@@ -48,6 +50,7 @@ public class WebdavClient extends HttpClient {
     private Uri mUri;
     private Credentials mCredentials;
     private boolean mFollowRedirects;
+    private String mSsoSessionCookie;
     final private static String TAG = "WebdavClient";
     public static final String USER_AGENT = "Android-ownCloud";
     
@@ -62,6 +65,7 @@ public class WebdavClient extends HttpClient {
         getParams().setParameter(HttpMethodParams.USER_AGENT, USER_AGENT);
         getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
         mFollowRedirects = true;
+        mSsoSessionCookie = null;
     }
 
     public void setBearerCredentials(String accessToken) {
@@ -73,6 +77,7 @@ public class WebdavClient extends HttpClient {
         
         mCredentials = new BearerCredentials(accessToken);
         getState().setCredentials(AuthScope.ANY, mCredentials);
+        mSsoSessionCookie = null;
     }
 
     public void setBasicCredentials(String username, String password) {
@@ -83,8 +88,17 @@ public class WebdavClient extends HttpClient {
         getParams().setAuthenticationPreemptive(true);
         mCredentials = new UsernamePasswordCredentials(username, password);
         getState().setCredentials(AuthScope.ANY, mCredentials);
+        mSsoSessionCookie = null;
     }
     
+    public void setSsoSessionCookie(String accessToken) {
+        getParams().setAuthenticationPreemptive(false);
+        getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES);
+        mSsoSessionCookie = accessToken;
+        mCredentials = null;
+    }
+    
+    
     /**
      * Check if a file exists in the OC server
      * 
@@ -96,7 +110,6 @@ public class WebdavClient extends HttpClient {
     public boolean existsFile(String path) throws IOException, HttpException {
         HeadMethod head = new HeadMethod(mUri.toString() + WebdavUtils.encodePath(path));
         try {
-            head.setFollowRedirects(mFollowRedirects);
             int status = executeMethod(head);
             Log_OC.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + ((status != HttpStatus.SC_OK)?"(FAIL)":""));
             exhaustResponse(head.getResponseBodyAsStream());
@@ -131,13 +144,27 @@ public class WebdavClient extends HttpClient {
             if (connectionTimeout >= 0) {
                 getHttpConnectionManager().getParams().setConnectionTimeout(connectionTimeout);
             }
-            method.setFollowRedirects(mFollowRedirects);
             return executeMethod(method);
         } finally {
             getParams().setSoTimeout(oldSoTimeout);
             getHttpConnectionManager().getParams().setConnectionTimeout(oldConnectionTimeout);
         }
     }
+    
+    
+    @Override
+    public int executeMethod(HttpMethod method) throws IOException, HttpException {
+        try {
+            method.setFollowRedirects(mFollowRedirects);
+        } catch (Exception e) {
+            
+        }
+        if (mSsoSessionCookie != null && mSsoSessionCookie.length() > 0) {
+            method.setRequestHeader("Cookie", mSsoSessionCookie);
+        }
+        return super.executeMethod(method);
+    }
+
 
     /**
      * Exhausts a not interesting HTTP response. Encouraged by HttpClient documentation.
@@ -182,6 +209,6 @@ public class WebdavClient extends HttpClient {
 
     public void setFollowRedirects(boolean followRedirects) {
         mFollowRedirects = followRedirects;
-    }
-
+    }
+
 }