From: David A. Velasco Date: Thu, 1 Aug 2013 15:47:09 +0000 (+0200) Subject: Session cookie caught and saved to use in requests after successful SAML-based federa... X-Git-Tag: oc-android-1.4.6~18^2~14 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/52bc433bb1180fd2b2105403fe9f817f330da61c?ds=inline;hp=-c Session cookie caught and saved to use in requests after successful SAML-based federated SSO --- 52bc433bb1180fd2b2105403fe9f817f330da61c diff --git a/src/com/owncloud/android/authentication/AccountAuthenticator.java b/src/com/owncloud/android/authentication/AccountAuthenticator.java index 2b58d437..85e9a235 100644 --- a/src/com/owncloud/android/authentication/AccountAuthenticator.java +++ b/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(); } } diff --git a/src/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/com/owncloud/android/authentication/AuthenticatorActivity.java index f648b6ef..50360c0e 100644 --- a/src/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/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"); + } + } + } diff --git a/src/com/owncloud/android/authentication/SsoWebViewClient.java b/src/com/owncloud/android/authentication/SsoWebViewClient.java index ad5ea12d..475b88db 100644 --- a/src/com/owncloud/android/authentication/SsoWebViewClient.java +++ b/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 mListenerRef; private String mTargetUrl; - public SsoWebViewClient (Context context) { - mContext = context; + public SsoWebViewClient (Handler listenerHandler, SsoWebViewClientListener listener) { + mListenerHandler = listenerHandler; + mListenerRef = new WeakReference(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); + } + } + }); + } } } diff --git a/src/com/owncloud/android/network/OwnCloudClientUtils.java b/src/com/owncloud/android/network/OwnCloudClientUtils.java index 500cf9a2..17abf6f8 100644 --- a/src/com/owncloud/android/network/OwnCloudClientUtils.java +++ b/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 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 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); diff --git a/src/com/owncloud/android/operations/RemoteOperationResult.java b/src/com/owncloud/android/operations/RemoteOperationResult.java index 57d7b0ad..cc2ee67f 100644 --- a/src/com/owncloud/android/operations/RemoteOperationResult.java +++ b/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); } } } diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index 3c1a64f6..3d2c3deb 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/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; diff --git a/src/eu/alefzero/webdav/WebdavClient.java b/src/eu/alefzero/webdav/WebdavClient.java index b9e9dc8c..570ebb85 100644 --- a/src/eu/alefzero/webdav/WebdavClient.java +++ b/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; - } - + } + }