diff --git a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java index 7383dc0a..8e24c15e 100644 --- a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java +++ b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java @@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -80,7 +81,12 @@ public class MainActivity extends Activity implements OnRemoteOperationListener, Uri serverUri = Uri.parse(getString(R.string.server_base_url) + AccountUtils.WEBDAV_PATH_4_0); mClient = OwnCloudClientFactory.createOwnCloudClient(serverUri, this, true); - mClient.setBasicCredentials(getString(R.string.username), getString(R.string.password)); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + getString(R.string.username), + getString(R.string.password) + ) + ); mFilesAdapter = new FilesArrayAdapter(this, R.layout.file_in_list); ((ListView)findViewById(R.id.list_view)).setAdapter(mFilesAdapter); diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java new file mode 100644 index 00000000..cb30f093 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java @@ -0,0 +1,43 @@ +package com.owncloud.android.lib.common; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.UsernamePasswordCredentials; +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; + +public class OwnCloudBasicCredentials implements OwnCloudCredentials { + + private String mUsername; + private String mPassword; + + public OwnCloudBasicCredentials(String username, String password) { + mUsername = username != null ? username : ""; + mPassword = password != null ? password : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + List authPrefs = new ArrayList(1); + authPrefs.add(AuthPolicy.BASIC); + client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + client.getParams().setAuthenticationPreemptive(true); + client.getState().setCredentials( + AuthScope.ANY, + new UsernamePasswordCredentials(mUsername, mPassword) + ); + } + + @Override + public String getAuthToken() { + return mPassword; + } + + @Override + public boolean authTokenExpires() { + return false; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java new file mode 100644 index 00000000..2a9f4515 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java @@ -0,0 +1,45 @@ +package com.owncloud.android.lib.common; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; + +import com.owncloud.android.lib.common.network.BearerAuthScheme; +import com.owncloud.android.lib.common.network.BearerCredentials; + +public class OwnCloudBearerCredentials implements OwnCloudCredentials { + + private String mAccessToken; + + public OwnCloudBearerCredentials(String accessToken) { + mAccessToken = accessToken != null ? accessToken : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); + + List authPrefs = new ArrayList(1); + authPrefs.add(BearerAuthScheme.AUTH_POLICY); + client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + client.getParams().setAuthenticationPreemptive(true); + client.getState().setCredentials( + AuthScope.ANY, + new BearerCredentials(mAccessToken) + ); + } + + @Override + public String getAuthToken() { + return mAccessToken; + } + + @Override + public boolean authTokenExpires() { + return true; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java index caa2d707..615f1b20 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -27,11 +27,8 @@ package com.owncloud.android.lib.common; import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; import org.apache.commons.httpclient.Cookie; -import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpConnectionManager; @@ -40,17 +37,12 @@ import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.HttpMethodBase; import org.apache.commons.httpclient.HttpVersion; import org.apache.commons.httpclient.URI; -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; import org.apache.http.params.CoreProtocolPNames; -import com.owncloud.android.lib.common.network.BearerAuthScheme; -import com.owncloud.android.lib.common.network.BearerCredentials; import com.owncloud.android.lib.common.network.WebdavUtils; @@ -66,11 +58,12 @@ public class OwnCloudClient extends HttpClient { private static final boolean PARAM_SINGLE_COOKIE_HEADER_VALUE = true; private static byte[] sExhaustBuffer = new byte[1024]; - private static int sIntanceCounter = 0; + private static int sIntanceCounter = 0; private boolean mFollowRedirects = true; - private Credentials mCredentials = null; - private String mSsoSessionCookie = null; + //private Credentials mCredentials = null; + private OwnCloudCredentials mCredentials = null; + //private String mSsoSessionCookie = null; private int mInstanceNumber = 0; private Uri mUri; @@ -97,6 +90,17 @@ public class OwnCloudClient extends HttpClient { PARAM_SINGLE_COOKIE_HEADER_VALUE); } + + public void setCredentials(OwnCloudCredentials credentials) { + if (credentials != null) { + mCredentials = credentials; + mCredentials.applyTo(this); + } else { + clearCredentials(); + } + } + + /* public void setBearerCredentials(String accessToken) { AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); @@ -109,7 +113,9 @@ public class OwnCloudClient extends HttpClient { getState().setCredentials(AuthScope.ANY, mCredentials); mSsoSessionCookie = null; } + */ + /* public void setBasicCredentials(String username, String password) { List authPrefs = new ArrayList(1); authPrefs.add(AuthPolicy.BASIC); @@ -120,7 +126,9 @@ public class OwnCloudClient extends HttpClient { getState().setCredentials(AuthScope.ANY, mCredentials); mSsoSessionCookie = null; } + */ + /* public void setSsoSessionCookie(String accessToken) { Log.d(TAG + " #" + mInstanceNumber, "Setting session cookie: " + accessToken); Log.e(TAG + " #" + mInstanceNumber, "BASE URL: " + mUri); @@ -158,12 +166,12 @@ public class OwnCloudClient extends HttpClient { Log.e(TAG, "Setting access token " + accessToken); } } + */ public void clearCredentials() { - mCredentials = new UsernamePasswordCredentials("", ""); + mCredentials = null; getState().clearCredentials(); getState().clearCookies(); - mSsoSessionCookie = null; } /** @@ -342,13 +350,21 @@ public class OwnCloudClient extends HttpClient { return mUri; } + /* public final Credentials getCredentials() { return mCredentials; } + */ + public final OwnCloudCredentials getCredentials() { + return mCredentials; + } + + /* public final String getSsoSessionCookie() { return mSsoSessionCookie; } + */ public void setFollowRedirects(boolean followRedirects) { mFollowRedirects = followRedirects; diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java index 28e9ce3f..61ab218c 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java @@ -74,7 +74,7 @@ public class OwnCloudClientFactory { //Log_OC.d(TAG, "Creating OwnCloudClient associated to " + account.name); Uri webdavUri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); - Uri uri = Uri.parse(AccountUtils.constructBasicURLForAccount(appContext, account)); + Uri uri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account)); AccountManager am = AccountManager.get(appContext); boolean isOauth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here boolean isSamlSso = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; @@ -82,18 +82,37 @@ public class OwnCloudClientFactory { client.setBaseUri(uri); if (isOauth2) { - String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), false); - client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), + false); + + client.setCredentials( + OwnCloudCredentialsFactory.newBearerCredentials(accessToken) + ); } else if (isSamlSso) { // TODO avoid a call to getUserData here - String accessToken = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), false); - client.setSsoSessionCookie(accessToken); + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), + false); + + client.setCredentials( + OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken) + ); } else { String username = account.name.substring(0, account.name.lastIndexOf('@')); //String password = am.getPassword(account); - String password = am.blockingGetAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), false); - client.setBasicCredentials(username, password); + String password = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypePass(account.type), + false); + + client.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials(username, password) + ); + } // Restore cookies @@ -105,7 +124,7 @@ public class OwnCloudClientFactory { public static OwnCloudClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountNotFoundException { Uri webdavUri = Uri.parse(AccountUtils.constructFullURLForAccount(appContext, account)); - Uri uri = Uri.parse(AccountUtils.constructBasicURLForAccount(appContext, account)); + Uri uri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account)); AccountManager am = AccountManager.get(appContext); boolean isOauth2 = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null; // TODO avoid calling to getUserData here boolean isSamlSso = am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; @@ -113,27 +132,55 @@ public class OwnCloudClientFactory { client.setBaseUri(uri); if (isOauth2) { // TODO avoid a call to getUserData here - AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), null, currentActivity, null, null); + AccountManagerFuture future = am.getAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), + null, + currentActivity, + null, + null); + Bundle result = future.getResult(); String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); if (accessToken == null) throw new AuthenticatorException("WTF!"); - client.setBearerCredentials(accessToken); // TODO not assume that the access token is a bearer token + client.setCredentials( + OwnCloudCredentialsFactory.newBearerCredentials(accessToken) + ); } else if (isSamlSso) { // TODO avoid a call to getUserData here - AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), null, currentActivity, null, null); + AccountManagerFuture future = am.getAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), + 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); + client.setCredentials( + OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken) + ); + } else { String username = account.name.substring(0, account.name.lastIndexOf('@')); //String password = am.getPassword(account); //String password = am.blockingGetAuthToken(account, MainApp.getAuthTokenTypePass(), false); - AccountManagerFuture future = am.getAuthToken(account, AccountTypeUtils.getAuthTokenTypePass(account.type), null, currentActivity, null, null); + AccountManagerFuture future = am.getAuthToken( + account, + AccountTypeUtils.getAuthTokenTypePass(account.type), + null, + currentActivity, + null, + null); + Bundle result = future.getResult(); String password = result.getString(AccountManager.KEY_AUTHTOKEN); - client.setBasicCredentials(username, password); + client.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials(username, password) + ); } // Restore cookies diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManager.java b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java new file mode 100644 index 00000000..5372a272 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudClientManager.java @@ -0,0 +1,66 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2014 ownCloud Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common; + +import java.io.IOException; + +import android.accounts.Account; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; +import android.content.Context; +import android.net.Uri; + +import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; + + +/** + * Manager to create and reuse OwnCloudClient instances to access remote OC servers. + * + * @author David A. Velasco + * @author masensio + */ + +public interface OwnCloudClientManager { + + public OwnCloudClient getClientFor(Account savedAccount, Context context) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException; + + public OwnCloudClient getClientFor( + Uri serverBaseUri, OwnCloudCredentials credentials, Context context); + + public void saveClient(Account savedAccount, Context context) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException; + + public void saveAllClients(Context context, String accountType) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException; + + public OwnCloudClient removeClientFor(Account account, Context context) + throws AccountNotFoundException, AuthenticatorException, + IOException, OperationCanceledException; + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java new file mode 100644 index 00000000..78b6858e --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudClientManagerFactory.java @@ -0,0 +1,30 @@ +package com.owncloud.android.lib.common; + +public class OwnCloudClientManagerFactory { + + public static enum Policy { + ALWAYS_NEW_CLIENT, + SINGLE_SESSION_PER_ACCOUNT + } + + public final static Policy DEFAULT_POLICY = Policy.ALWAYS_NEW_CLIENT; + + public static OwnCloudClientManager newDefaultOwnCloudClientManager() { + return newOwnCloudClientManager(DEFAULT_POLICY); + } + + public static OwnCloudClientManager newOwnCloudClientManager(Policy policy) { + switch (policy) { + case ALWAYS_NEW_CLIENT: + return new SingleSessionManager(); + + case SINGLE_SESSION_PER_ACCOUNT: + return new SimpleFactoryManager(); + + default: + throw new IllegalArgumentException("Unknown policy"); + } + } + + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientMap.java b/src/com/owncloud/android/lib/common/OwnCloudClientMap.java deleted file mode 100644 index 03d6abf7..00000000 --- a/src/com/owncloud/android/lib/common/OwnCloudClientMap.java +++ /dev/null @@ -1,147 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2014 ownCloud Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ - -package com.owncloud.android.lib.common; - -import java.io.IOException; -import java.util.concurrent.ConcurrentMap; - -import org.apache.commons.httpclient.Cookie; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.content.Context; -import android.util.Log; -import android.net.Uri; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; -import com.owncloud.android.lib.common.accounts.AccountUtils.Constants; - -/** - * Map for {@link OwnCloudClient} instances associated to ownCloud {@link Account}s - * - * TODO check synchronization - * - * TODO consider converting into a non static object saved in the application context - * @author David A. Velasco - * @author masensio - */ - -public class OwnCloudClientMap { - - private static final String TAG = OwnCloudClientMap.class.getSimpleName(); - - private static ConcurrentMap mClients = - new java.util.concurrent.ConcurrentHashMap(); - - private static ConcurrentMap mAnonymousClient = - new java.util.concurrent.ConcurrentHashMap(); - - public static synchronized OwnCloudClient getClientFor(Account account, Context context) - throws OperationCanceledException, AuthenticatorException, - AccountNotFoundException, IOException { - - OwnCloudClient client = mClients.get(account.name); - if (client == null) { - client = OwnCloudClientFactory.createOwnCloudClient( - account, - context.getApplicationContext()); - mClients.putIfAbsent(account.name, client); - } - return client; - } - - - public static synchronized OwnCloudClient getAnonymousClientFor( - Uri baseUri, Context context, boolean followRedirects) { - OwnCloudClient client = mAnonymousClient.get(baseUri.toString()); - if (client == null) { - client = OwnCloudClientFactory.createOwnCloudClient( - baseUri, - context.getApplicationContext(), - followRedirects); - mAnonymousClient.putIfAbsent(baseUri.toString(), client); - } - return client; - } - - - public static synchronized OwnCloudClient removeClientFor(Account account) { - return mClients.remove(account.name); - } - - - public static synchronized void clearPool() { - mClients.clear(); - } - - public static synchronized void saveClient(Account account, Context context) { - - // Account Manager - AccountManager ac = AccountManager.get(context.getApplicationContext()); - - if (account.name != null) { - OwnCloudClient client = mClients.get(account.name); - - Cookie[] cookies =client.getState().getCookies(); - String cookiesString =""; - for (Cookie cookie: cookies) { - cookiesString = cookiesString + cookie.toString() + ";"; - - logCookie(cookie); - } - ac.setUserData(account, Constants.KEY_COOKIES, cookiesString); - Log.d(TAG, "Saving Cookies: "+ cookiesString ); - } - } - - public static synchronized void saveAllClients(Context context, String accountType) { - // Get all accounts - Account [] accounts = AccountManager.get(context.getApplicationContext()) - .getAccountsByType(accountType); - - // Save cookies for all accounts - for(Account account: accounts){ - saveClient(account, context.getApplicationContext()); - } - - } - - private static void logCookie(Cookie cookie) { - Log.d(TAG, "Cookie name: "+ cookie.getName() ); - Log.d(TAG, " value: "+ cookie.getValue() ); - Log.d(TAG, " domain: "+ cookie.getDomain()); - Log.d(TAG, " path: "+ cookie.getPath() ); - Log.d(TAG, " version: "+ cookie.getVersion() ); - Log.d(TAG, " expiryDate: " + - (cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--")); - Log.d(TAG, " comment: "+ cookie.getComment() ); - Log.d(TAG, " secure: "+ cookie.getSecure() ); - } - -} diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java new file mode 100644 index 00000000..deba8177 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudCredentials.java @@ -0,0 +1,28 @@ +/* ownCloud Android client application + * Copyright (C) 2014 ownCloud Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.owncloud.android.lib.common; + +public interface OwnCloudCredentials { + + public void applyTo(OwnCloudClient ownCloudClient); + + public String getAuthToken(); + + public boolean authTokenExpires(); + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java new file mode 100644 index 00000000..45ee53de --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java @@ -0,0 +1,47 @@ +package com.owncloud.android.lib.common; + +public class OwnCloudCredentialsFactory { + + private static OwnCloudAnonymousCredentials sAnonymousCredentials; + + public static OwnCloudCredentials newBasicCredentials(String username, String password) { + return new OwnCloudBasicCredentials(username, password); + } + + public static OwnCloudCredentials newBearerCredentials(String authToken) { + return new OwnCloudBearerCredentials(authToken); + } + + public static OwnCloudCredentials newSamlSsoCredentials(String sessionCookie) { + return new OwnCloudSamlSsoCredentials(sessionCookie); + } + + public static final OwnCloudCredentials getAnonymousCredentials() { + if (sAnonymousCredentials == null) { + sAnonymousCredentials = new OwnCloudAnonymousCredentials(); + } + return sAnonymousCredentials; + } + + public static final class OwnCloudAnonymousCredentials implements OwnCloudCredentials { + + protected OwnCloudAnonymousCredentials() { + } + + @Override + public void applyTo(OwnCloudClient client) { + client.clearCredentials(); + } + + @Override + public String getAuthToken() { + return ""; + } + + @Override + public boolean authTokenExpires() { + return false; + } + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java new file mode 100644 index 00000000..a50d3177 --- /dev/null +++ b/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java @@ -0,0 +1,52 @@ +package com.owncloud.android.lib.common; + +import org.apache.commons.httpclient.Cookie; +import org.apache.commons.httpclient.cookie.CookiePolicy; + +import android.net.Uri; + +public class OwnCloudSamlSsoCredentials implements OwnCloudCredentials { + + private String mSessionCookie; + + public OwnCloudSamlSsoCredentials(String sessionCookie) { + mSessionCookie = sessionCookie != null ? mSessionCookie : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + client.getParams().setAuthenticationPreemptive(false); + client.getParams().setCookiePolicy(CookiePolicy.IGNORE_COOKIES); + client.setFollowRedirects(false); + + Uri serverUri = client.getBaseUri(); + if (serverUri == null) { + // TODO fix the mess of Uris in OwnCloudClient + serverUri = client.getWebdavUri(); + } + + String[] cookies = mSessionCookie.split(";"); + if (cookies.length > 0) { + for (int i=0; i> mClientsPerServer = + new HashMap>(); + + + public static OwnCloudClientManager getInstance() { + if (mInstance == null) { + mInstance = new SingleSessionManager(); + } + return mInstance ; + } + + + @Override + public synchronized OwnCloudClient getClientFor(Account savedAccount, Context context) + throws OperationCanceledException, AuthenticatorException, AccountNotFoundException, + IOException { + + Uri serverBaseUri = + Uri.parse(AccountUtils.getBaseUrlForAccount(context, savedAccount)); + + OwnCloudCredentials credentials = + AccountUtils.getCredentialsForAccount(context, savedAccount); + + return getClientFor(serverBaseUri, credentials, context); + + } + + + @Override + public synchronized OwnCloudClient getClientFor( + Uri serverBaseUri, OwnCloudCredentials credentials, Context context) { + + Map clientsPerAccount = + mClientsPerServer.get(serverBaseUri.toString()); + + if (clientsPerAccount == null) { + clientsPerAccount = new HashMap(); + mClientsPerServer.put( + serverBaseUri.toString(), + clientsPerAccount); + } + + if (credentials == null) { + credentials = OwnCloudCredentialsFactory.getAnonymousCredentials(); + } + + /// TODO - CRITERIA FOR MATCH OF KEYS!!! + OwnCloudClient client = clientsPerAccount.get(credentials); + if (client == null) { + client = OwnCloudClientFactory.createOwnCloudClient( + serverBaseUri, + context.getApplicationContext(), + true); + + client.setCredentials(credentials); + clientsPerAccount.put(credentials, client); + } + + return client; + } + + + @Override + public synchronized OwnCloudClient removeClientFor(Account savedAccount, Context context) + throws AccountNotFoundException, OperationCanceledException, AuthenticatorException, IOException { + + Uri serverBaseUri = + Uri.parse(AccountUtils.getBaseUrlForAccount(context, savedAccount)); + + Map clientsPerAccount = + mClientsPerServer.get(serverBaseUri.toString()); + + if (clientsPerAccount != null) { + OwnCloudCredentials credentials = + AccountUtils.getCredentialsForAccount(context, savedAccount); + + return clientsPerAccount.remove(credentials); + } + return null; + } + + + @Override + public synchronized void saveClient(Account savedAccount, Context context) + throws AccountNotFoundException, AuthenticatorException, IOException, + OperationCanceledException { + + // Account Manager + AccountManager ac = AccountManager.get(context.getApplicationContext()); + + if (savedAccount != null) { + Uri serverBaseUri = + Uri.parse(AccountUtils.getBaseUrlForAccount(context, savedAccount)); + + Map clientsPerAccount = + mClientsPerServer.get(serverBaseUri.toString()); + + if (clientsPerAccount != null) { + OwnCloudCredentials credentials = + AccountUtils.getCredentialsForAccount(context, savedAccount); + + /// TODO - CRITERIA FOR MATCH OF KEYS!!! + OwnCloudClient client = clientsPerAccount.get(credentials); + + if (client != null) { + + Cookie[] cookies = client.getState().getCookies(); + String cookiesString =""; + for (Cookie cookie: cookies) { + cookiesString = cookiesString + cookie.toString() + ";"; + + logCookie(cookie); + } + ac.setUserData(savedAccount, Constants.KEY_COOKIES, cookiesString); + Log.d(TAG, "Saving Cookies: "+ cookiesString ); + } + } + } + + } + + @Override + public synchronized void saveAllClients(Context context, String accountType) + throws AccountNotFoundException, AuthenticatorException, IOException, + OperationCanceledException { + + // Get all accounts + Account [] accounts = AccountManager.get(context.getApplicationContext()) + .getAccountsByType(accountType); + + // Save cookies for all accounts + for(Account account: accounts){ + saveClient(account, context.getApplicationContext()); + } + + } + + private void logCookie(Cookie cookie) { + Log.d(TAG, "Cookie name: "+ cookie.getName() ); + Log.d(TAG, " value: "+ cookie.getValue() ); + Log.d(TAG, " domain: "+ cookie.getDomain()); + Log.d(TAG, " path: "+ cookie.getPath() ); + Log.d(TAG, " version: "+ cookie.getVersion() ); + Log.d(TAG, " expiryDate: " + + (cookie.getExpiryDate() != null ? cookie.getExpiryDate().toString() : "--")); + Log.d(TAG, " comment: "+ cookie.getComment() ); + Log.d(TAG, " secure: "+ cookie.getSecure() ); + } + + +} diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java index 84c9a6a9..8fddc4e6 100644 --- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java +++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java @@ -25,11 +25,17 @@ package com.owncloud.android.lib.common.accounts; +import java.io.IOException; + +import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountsException; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.content.Context; public class AccountUtils { @@ -94,13 +100,32 @@ public class AccountUtils { /** * Extracts url server from the account + * + * @deprecated This method will be removed in version 1.0. + * Use {@link #getBaseUrlForAccount(Context, Account)} + * instead. + * * @param context * @param account * @return url server or null on failure * @throws AccountNotFoundException When 'account' is unknown for the AccountManager */ - public static String constructBasicURLForAccount(Context context, Account account) throws AccountNotFoundException { - AccountManager ama = AccountManager.get(context); + @Deprecated + public static String constructBasicURLForAccount(Context context, Account account) + throws AccountNotFoundException { + return getBaseUrlForAccount(context, account); + } + + /** + * Extracts url server from the account + * @param context + * @param account + * @return url server or null on failure + * @throws AccountNotFoundException When 'account' is unknown for the AccountManager + */ + public static String getBaseUrlForAccount(Context context, Account account) + throws AccountNotFoundException { + AccountManager ama = AccountManager.get(context.getApplicationContext()); String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL); if (baseurl == null ) @@ -109,6 +134,58 @@ public class AccountUtils { return baseurl; } + + /** + * + * @return + * @throws IOException + * @throws AuthenticatorException + * @throws OperationCanceledException + */ + public static OwnCloudCredentials getCredentialsForAccount(Context context, Account account) + throws OperationCanceledException, AuthenticatorException, IOException { + + OwnCloudCredentials credentials = null; + AccountManager am = AccountManager.get(context); + + boolean isOauth2 = am.getUserData( + account, + AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null; + + boolean isSamlSso = am.getUserData( + account, + AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; + + if (isOauth2) { + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newBearerCredentials(accessToken); + + } else if (isSamlSso) { + String accessToken = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newSamlSsoCredentials(accessToken); + + } else { + String username = account.name.substring(0, account.name.lastIndexOf('@')); + String password = am.blockingGetAuthToken( + account, + AccountTypeUtils.getAuthTokenTypePass(account.type), + false); + + credentials = OwnCloudCredentialsFactory.newBasicCredentials(username, password); + } + + return credentials; + + } + public static class AccountNotFoundException extends AccountsException { @@ -165,4 +242,5 @@ public class AccountUtils { */ public static final String KEY_COOKIES = "oc_account_cookies"; } + } diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java index 859dac0e..fa89b24b 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java @@ -26,12 +26,10 @@ package com.owncloud.android.lib.common.operations; import java.io.IOException; -import org.apache.commons.httpclient.Credentials; - import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudClientMap; -import com.owncloud.android.lib.common.network.BearerCredentials; +import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.SingleSessionManager; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; @@ -106,7 +104,7 @@ public abstract class RemoteOperation implements Runnable { mAccount = account; mContext = context.getApplicationContext(); try { - mClient = OwnCloudClientMap.getClientFor(mAccount, mContext); + mClient = SingleSessionManager.getInstance().getClientFor(mAccount, mContext); } catch (Exception e) { Log.e(TAG, "Error while trying to access to " + mAccount.name, e); return new RemoteOperationResult(e); @@ -253,7 +251,8 @@ public abstract class RemoteOperation implements Runnable { mAccount, mContext, mCallerActivity); } else { /** EOF DEPRECATED */ - mClient = OwnCloudClientMap.getClientFor(mAccount, mContext); + mClient = SingleSessionManager.getInstance(). + getClientFor(mAccount, mContext); } } else { throw new IllegalStateException("Trying to run a remote operation asynchronously with no client instance or account"); @@ -279,17 +278,15 @@ public abstract class RemoteOperation implements Runnable { // (result.getCode() == ResultCode.UNAUTHORIZED || (result.isTemporalRedirection() && result.isIdPRedirection()))) { (result.getCode() == ResultCode.UNAUTHORIZED || result.isIdPRedirection())) { /// possible fail due to lack of authorization in an operation performed in foreground - Credentials cred = mClient.getCredentials(); - String ssoSessionCookie = mClient.getSsoSessionCookie(); - if (cred != null || ssoSessionCookie != null) { + OwnCloudCredentials cred = mClient.getCredentials(); + if (cred != null) { /// confirmed : unauthorized operation AccountManager am = AccountManager.get(mContext); - boolean bearerAuthorization = (cred != null && cred instanceof BearerCredentials); - boolean samlBasedSsoAuthorization = (cred == null && ssoSessionCookie != null); - if (bearerAuthorization) { - am.invalidateAuthToken(mAccount.type, ((BearerCredentials)cred).getAccessToken()); - } else if (samlBasedSsoAuthorization ) { - am.invalidateAuthToken(mAccount.type, ssoSessionCookie); + if (cred.authTokenExpires()) { + am.invalidateAuthToken( + mAccount.type, + cred.getAuthToken() + ); } else { am.clearPassword(mAccount); } diff --git a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java index 65771dab..1b5eefe4 100644 --- a/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java +++ b/test_client/src/com/owncloud/android/lib/test_project/TestActivity.java @@ -35,6 +35,7 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.resources.files.RemoteFile; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -113,7 +114,12 @@ public class TestActivity extends Activity { OwnCloudClientFactory.DEFAULT_CONNECTION_TIMEOUT); mClient.setWebdavUri(uri); mClient.setFollowRedirects(true); - mClient.setBasicCredentials(mUser, mPass); + mClient.setCredentials( + OwnCloudCredentialsFactory.newBasicCredentials( + mUser, + mPass + ) + ); mClient.setBaseUri(Uri.parse(mServerUri)); Log.v(TAG, "onCreate finished, ownCloud client ready");