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 31cf1c79..3cb43f72 100644 --- a/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java +++ b/sample_client/src/com/owncloud/android/lib/sampleclient/MainActivity.java @@ -35,7 +35,7 @@ import java.util.List; 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.authentication.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; diff --git a/src/com/owncloud/android/lib/common/OwnCloudAccount.java b/src/com/owncloud/android/lib/common/OwnCloudAccount.java index af440e35..533247df 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudAccount.java +++ b/src/com/owncloud/android/lib/common/OwnCloudAccount.java @@ -29,6 +29,8 @@ import java.io.IOException; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import android.accounts.Account; import android.accounts.AccountManager; @@ -122,10 +124,10 @@ public class OwnCloudAccount { throw new IllegalArgumentException("Parameter 'context' cannot be null"); } - if (mSavedAccount != null) { - mCredentials = AccountUtils.getCredentialsForAccount(context, mSavedAccount); - } - } + if (mSavedAccount != null) { + mCredentials = AccountUtils.getCredentialsForAccount(context, mSavedAccount); + } + } public Uri getBaseUri() { return mBaseUri; diff --git a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java deleted file mode 100644 index f0bb13a7..00000000 --- a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java +++ /dev/null @@ -1,75 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2016 ownCloud GmbH. - * - * 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.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.getParams().setCredentialCharset(OwnCloudCredentialsFactory.CREDENTIAL_CHARSET); - client.getState().setCredentials( - AuthScope.ANY, - new BearerCredentials(mAccessToken) - ); - } - - @Override - public String getUsername() { - // its unknown - return null; - } - - @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 dc3ea126..60c84851 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClient.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2016 ownCloud GmbH. + * Copyright (C) 2017 ownCloud GmbH. * Copyright (C) 2012 Bartek Przybylski * * Permission is hereby granted, free of charge, to any person obtaining a copy @@ -33,7 +33,6 @@ import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HostConfiguration; 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.HttpStatus; @@ -41,23 +40,27 @@ import org.apache.commons.httpclient.HttpVersion; import org.apache.commons.httpclient.URI; import org.apache.commons.httpclient.URIException; import org.apache.commons.httpclient.cookie.CookiePolicy; -import org.apache.commons.httpclient.methods.HeadMethod; import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.httpclient.params.HttpParams; +import android.accounts.AccountManager; +import android.accounts.AccountsException; +import android.content.Context; import android.net.Uri; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.network.RedirectionPath; -import com.owncloud.android.lib.common.network.WebdavUtils; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; public class OwnCloudClient extends HttpClient { private static final String TAG = OwnCloudClient.class.getSimpleName(); - public static final int MAX_REDIRECTIONS_COUNT = 3; + private static final int MAX_REDIRECTIONS_COUNT = 3; + private static final int MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS = 1; private static final String PARAM_SINGLE_COOKIE_HEADER = "http.protocol.single-cookie-header"; private static final boolean PARAM_SINGLE_COOKIE_HEADER_VALUE = true; private static final String PARAM_PROTOCOL_VERSION = "http.protocol.version"; @@ -73,6 +76,25 @@ public class OwnCloudClient extends HttpClient { private OwnCloudVersion mVersion = null; + /// next too attributes are a very ugly dependency, added to grant silent retry of OAuth token when needed ; + /// see #shouldInvalidateCredentials and #invalidateCredentials for more details + private Context mContext; + private OwnCloudAccount mAccount; + + /** + * {@link @OwnCloudClientManager} holding a reference to this object and delivering it to callers; might be + * NULL + */ + private OwnCloudClientManager mOwnCloudClientManager = null; + + /** + * When 'true', the method {@link #executeMethod(HttpMethod)} tries to silently refresh credentials + * if fails due to lack of authorization, if credentials support authorization refresh. + */ + private boolean mSilentRefreshOfAccountCredentials = true; + + private String mRedirectedLocation; + /** * Constructor */ @@ -142,28 +164,6 @@ public class OwnCloudClient extends HttpClient { mCredentials.applyTo(this); } - /** - * Check if a file exists in the OC server - * - * @return 'true' if the file exists; 'false' it doesn't exist - * @throws Exception When the existence could not be determined - * @deprecated Use ExistenceCheckOperation instead - */ - @Deprecated - public boolean existsFile(String path) throws IOException, HttpException { - HeadMethod head = new HeadMethod(getWebdavUri() + WebdavUtils.encodePath(path)); - try { - int status = executeMethod(head); - Log_OC.d(TAG, "HEAD to " + path + " finished with HTTP status " + status + - ((status != HttpStatus.SC_OK) ? "(FAIL)" : "")); - exhaustResponse(head.getResponseBodyAsStream()); - return (status == HttpStatus.SC_OK); - - } finally { - head.releaseConnection(); // let the connection available for other methods - } - } - /** * Requests the received method with the received timeout (milliseconds). * @@ -207,25 +207,40 @@ public class OwnCloudClient extends HttpClient { */ @Override public int executeMethod(HttpMethod method) throws IOException { - // Update User Agent - HttpParams params = method.getParams(); - String userAgent = OwnCloudClientManagerFactory.getUserAgent(); - params.setParameter(HttpMethodParams.USER_AGENT, userAgent); - preventCrashDueToInvalidPort(method); + boolean repeatWithFreshCredentials; + int repeatCounter = 0; + int status; - Log_OC.d(TAG + " #" + mInstanceNumber, "REQUEST " + + do { + // Update User Agent + HttpParams params = method.getParams(); + String userAgent = OwnCloudClientManagerFactory.getUserAgent(); + params.setParameter(HttpMethodParams.USER_AGENT, userAgent); + + preventCrashDueToInvalidPort(method); + + Log_OC.d(TAG + " #" + mInstanceNumber, "REQUEST " + method.getName() + " " + method.getPath()); - //logCookiesAtRequest(method.getRequestHeaders(), "before"); - //logCookiesAtState("before"); - method.setFollowRedirects(false); + //logCookiesAtRequest(method.getRequestHeaders(), "before"); + //logCookiesAtState("before"); + method.setFollowRedirects(false); - int status = super.executeMethod(method); + status = super.executeMethod(method); - if (mFollowRedirects) { - status = followRedirection(method).getLastStatus(); - } + checkFirstRedirection(method); + + if (mFollowRedirects) { + status = followRedirection(method).getLastStatus(); + } + + repeatWithFreshCredentials = checkUnauthorizedAccess(status, repeatCounter); + if (repeatWithFreshCredentials) { + repeatCounter++; + } + + } while (repeatWithFreshCredentials); //logCookiesAtRequest(method.getRequestHeaders(), "after"); //logCookiesAtState("after"); @@ -234,6 +249,17 @@ public class OwnCloudClient extends HttpClient { return status; } + private void checkFirstRedirection(HttpMethod method) { + Header[] httpHeaders = method.getResponseHeaders(); + + for (Header httpHeader : httpHeaders) { + + if ("location".equals(httpHeader.getName().toLowerCase())) { + mRedirectedLocation = httpHeader.getValue(); + break; + } + } + } /** * Fix for https://github.com/owncloud/android/issues/1847#issuecomment-267558274 @@ -266,6 +292,7 @@ public class OwnCloudClient extends HttpClient { int redirectionsCount = 0; int status = method.getStatusCode(); RedirectionPath result = new RedirectionPath(status, MAX_REDIRECTIONS_COUNT); + while (redirectionsCount < MAX_REDIRECTIONS_COUNT && (status == HttpStatus.SC_MOVED_PERMANENTLY || status == HttpStatus.SC_MOVED_TEMPORARILY || @@ -277,10 +304,11 @@ public class OwnCloudClient extends HttpClient { location = method.getResponseHeader("location"); } if (location != null) { - Log_OC.d(TAG + " #" + mInstanceNumber, - "Location to redirect: " + location.getValue()); - String locationStr = location.getValue(); + + Log_OC.d(TAG + " #" + mInstanceNumber, + "Location to redirect: " + locationStr); + result.addLocation(locationStr); // Release the connection to avoid reach the max number of connections per host @@ -468,4 +496,138 @@ public class OwnCloudClient extends HttpClient { public OwnCloudVersion getOwnCloudVersion() { return mVersion; } + + public void setContext(Context context) { + this.mContext = context; + } + + public Context getContext() { + return mContext; + } + + public void setAccount(OwnCloudAccount account) { + this.mAccount = account; + } + + public OwnCloudAccount getAccount() { + return mAccount; + } + + /** + * Enables or disables silent refresh of credentials, if supported by credentials themselves. + */ + public void setSilentRefreshOfAccountCredentials(boolean silentRefreshOfAccountCredentials) { + mSilentRefreshOfAccountCredentials = silentRefreshOfAccountCredentials; + } + + public boolean getSilentRefreshOfAccountCredentials() { + return mSilentRefreshOfAccountCredentials; + } + + + /** + * Checks the status code of an execution and decides if should be repeated with fresh credentials. + * + * Invalidates current credentials if the request failed as anauthorized. + * + * Refresh current credentials if possible, and marks a retry. + * + * @param status + * @param repeatCounter + * @return + */ + private boolean checkUnauthorizedAccess(int status, int repeatCounter) { + boolean credentialsWereRefreshed = false; + + if (shouldInvalidateAccountCredentials(status)) { + boolean invalidated = invalidateAccountCredentials(); + + if (invalidated) { + if (getCredentials().authTokenCanBeRefreshed() && + repeatCounter < MAX_REPEAT_COUNT_WITH_FRESH_CREDENTIALS) { + + try { + mAccount.loadCredentials(mContext); + // if mAccount.getCredentials().length() == 0 --> refresh failed + setCredentials(mAccount.getCredentials()); + credentialsWereRefreshed = true; + + } catch (AccountsException | IOException e) { + Log_OC.e( + TAG, + "Error while trying to refresh auth token for " + mAccount.getSavedAccount().name, + e + ); + } + } + + if (!credentialsWereRefreshed && mOwnCloudClientManager != null) { + // if credentials are not refreshed, client must be removed + // from the OwnCloudClientManager to prevent it is reused once and again + mOwnCloudClientManager.removeClientFor(mAccount); + } + } + // else: execute will finish with status 401 + } + + return credentialsWereRefreshed; + } + + /** + * Determines if credentials should be invalidated according the to the HTTPS status + * of a network request just performed. + * + * @param httpStatusCode Result of the last request ran with the 'credentials' belows. + + * @return 'True' if credentials should and might be invalidated, 'false' if shouldn't or + * cannot be invalidated with the given arguments. + */ + private boolean shouldInvalidateAccountCredentials(int httpStatusCode) { + + boolean should = (httpStatusCode == HttpStatus.SC_UNAUTHORIZED || isIdPRedirection()); // invalid credentials + + should &= (mCredentials != null && // real credentials + !(mCredentials instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials)); + + // test if have all the needed to effectively invalidate ... + should &= (mAccount != null && mAccount.getSavedAccount() != null && mContext != null); + + return should; + } + + /** + * Invalidates credentials stored for the given account in the system {@link AccountManager} and in + * current {@link OwnCloudClientManagerFactory#getDefaultSingleton()} instance. + * + * {@link #shouldInvalidateAccountCredentials(int)} should be called first. + * + * @return 'True' if invalidation was successful, 'false' otherwise. + */ + private boolean invalidateAccountCredentials() { + AccountManager am = AccountManager.get(mContext); + am.invalidateAuthToken( + mAccount.getSavedAccount().type, + mCredentials.getAuthToken() + ); + am.clearPassword(mAccount.getSavedAccount()); // being strict, only needed for Basic Auth credentials + return true; + } + + public OwnCloudClientManager getOwnCloudClientManager() { + return mOwnCloudClientManager; + } + + void setOwnCloudClientManager(OwnCloudClientManager clientManager) { + mOwnCloudClientManager = clientManager; + } + + /** + * Check if the redirection is to an identity provider such as SAML or wayf + * @return true if the redirection location includes SAML or wayf, false otherwise + */ + private boolean isIdPRedirection() { + return (mRedirectedLocation != null && + (mRedirectedLocation.toUpperCase().contains("SAML") || + mRedirectedLocation.toLowerCase().contains("wayf"))); + } } diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java index 5d01e487..ff9e32b0 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java +++ b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java @@ -41,6 +41,7 @@ import com.owncloud.android.lib.common.accounts.AccountTypeUtils; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; import com.owncloud.android.lib.common.network.NetworkUtils; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -57,11 +58,12 @@ public class OwnCloudClientFactory { /** * Creates a OwnCloudClient setup for an ownCloud account - * + * * Do not call this method from the main thread. - * + * * @param account The ownCloud account * @param appContext Android application context + * @param currentActivity Caller {@link Activity} * @return A OwnCloudClient object ready to be used * @throws AuthenticatorException If the authenticator failed to get the authorization * token for the account. @@ -71,69 +73,7 @@ public class OwnCloudClientFactory { * authorization token for the account. * @throws AccountNotFoundException If 'account' is unknown for the AccountManager * - * @deprecated : Will be deleted in version 1.0. - * Use {@link #createOwnCloudClient(Account, Context, Activity)} instead. */ - @Deprecated - public static OwnCloudClient createOwnCloudClient (Account account, Context appContext) - throws OperationCanceledException, AuthenticatorException, IOException, - AccountNotFoundException { - //Log_OC.d(TAG, "Creating OwnCloudClient associated to " + account.name); - Uri baseUri = Uri.parse(AccountUtils.getBaseUrlForAccount(appContext, account)); - AccountManager am = AccountManager.get(appContext); - // TODO avoid calling to getUserData here - boolean isOauth2 = - am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_OAUTH2) != null; - boolean isSamlSso = - am.getUserData(account, AccountUtils.Constants.KEY_SUPPORTS_SAML_WEB_SSO) != null; - OwnCloudClient client = createOwnCloudClient(baseUri, appContext, !isSamlSso); - - String username = AccountUtils.getUsernameForAccount(account); - if (isOauth2) { - 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.setCredentials( - OwnCloudCredentialsFactory.newSamlSsoCredentials(username, accessToken) - ); - - } else { - //String password = am.getPassword(account); - String password = am.blockingGetAuthToken( - account, - AccountTypeUtils.getAuthTokenTypePass(account.type), - false); - - OwnCloudVersion version = AccountUtils.getServerVersionForAccount(account, appContext); - client.setCredentials( - OwnCloudCredentialsFactory.newBasicCredentials( - username, - password, - (version != null && version.isPreemptiveAuthenticationPreferred()) - ) - ); - - } - - // Restore cookies - AccountUtils.restoreCookies(account, client, appContext); - - return client; - } - - public static OwnCloudClient createOwnCloudClient (Account account, Context appContext, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, @@ -156,12 +96,12 @@ public class OwnCloudClientFactory { currentActivity, null, null); - + Bundle result = future.getResult(); String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); if (accessToken == null) throw new AuthenticatorException("WTF!"); client.setCredentials( - OwnCloudCredentialsFactory.newBearerCredentials(accessToken) + OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken) ); } else if (isSamlSso) { // TODO avoid a call to getUserData here @@ -172,7 +112,7 @@ public class OwnCloudClientFactory { currentActivity, null, null); - + Bundle result = future.getResult(); String accessToken = result.getString(AccountManager.KEY_AUTHTOKEN); if (accessToken == null) throw new AuthenticatorException("WTF!"); @@ -193,7 +133,7 @@ public class OwnCloudClientFactory { null, null ); - + Bundle result = future.getResult(); String password = result.getString(AccountManager.KEY_AUTHTOKEN); OwnCloudVersion version = AccountUtils.getServerVersionForAccount(account, appContext); @@ -205,13 +145,13 @@ public class OwnCloudClientFactory { ) ); } - + // Restore cookies AccountUtils.restoreCookies(account, client, appContext); - + return client; } - + /** * Creates a OwnCloudClient to access a URL and sets the desired parameters for ownCloud * client connections. @@ -236,6 +176,7 @@ public class OwnCloudClientFactory { OwnCloudClient client = new OwnCloudClient(uri, NetworkUtils.getMultiThreadedConnManager()); client.setDefaultTimeouts(DEFAULT_DATA_TIMEOUT, DEFAULT_CONNECTION_TIMEOUT); client.setFollowRedirects(followRedirects); + client.setContext(context); return client; } diff --git a/src/com/owncloud/android/lib/common/SimpleFactoryManager.java b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java index 362d2a6e..b955c517 100644 --- a/src/com/owncloud/android/lib/common/SimpleFactoryManager.java +++ b/src/com/owncloud/android/lib/common/SimpleFactoryManager.java @@ -36,44 +36,47 @@ import com.owncloud.android.lib.common.utils.Log_OC; import java.io.IOException; public class SimpleFactoryManager implements OwnCloudClientManager { - - private static final String TAG = SimpleFactoryManager.class.getSimpleName(); - @Override - public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) - throws AccountNotFoundException, OperationCanceledException, AuthenticatorException, - IOException { + private static final String TAG = SimpleFactoryManager.class.getSimpleName(); - Log_OC.d(TAG, "getClientFor(OwnCloudAccount ... : "); + @Override + public OwnCloudClient getClientFor(OwnCloudAccount account, Context context) + throws AccountNotFoundException, OperationCanceledException, AuthenticatorException, + IOException { - OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient( - account.getBaseUri(), - context.getApplicationContext(), - true); + Log_OC.d(TAG, "getClientFor(OwnCloudAccount ... : "); - Log_OC.v(TAG, " new client {" + - (account.getName() != null ? - account.getName() : - AccountUtils.buildAccountName(account.getBaseUri(), "") + OwnCloudClient client = OwnCloudClientFactory.createOwnCloudClient( + account.getBaseUri(), + context.getApplicationContext(), + true); - ) + ", " + client.hashCode() + "}"); + Log_OC.v(TAG, " new client {" + + (account.getName() != null ? + account.getName() : + AccountUtils.buildAccountName(account.getBaseUri(), "") + + ) + ", " + client.hashCode() + "}"); if (account.getCredentials() == null) { account.loadCredentials(context); } client.setCredentials(account.getCredentials()); - return client; - } + client.setAccount(account); + client.setContext(context); + client.setOwnCloudClientManager(this); + return client; + } - @Override - public OwnCloudClient removeClientFor(OwnCloudAccount account) { - // nothing to do - not taking care of tracking instances! - return null; - } + @Override + public OwnCloudClient removeClientFor(OwnCloudAccount account) { + // nothing to do - not taking care of tracking instances! + return null; + } - @Override - public void saveAllClients(Context context, String accountType) { - // nothing to do - not taking care of tracking instances! - } + @Override + public void saveAllClients(Context context, String accountType) { + // nothing to do - not taking care of tracking instances! + } } diff --git a/src/com/owncloud/android/lib/common/SingleSessionManager.java b/src/com/owncloud/android/lib/common/SingleSessionManager.java index 589b33fd..05396480 100644 --- a/src/com/owncloud/android/lib/common/SingleSessionManager.java +++ b/src/com/owncloud/android/lib/common/SingleSessionManager.java @@ -40,6 +40,7 @@ import android.util.Log; import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; import com.owncloud.android.lib.common.utils.Log_OC; /** @@ -115,8 +116,11 @@ public class SingleSessionManager implements OwnCloudClientManager { context.getApplicationContext(), true); // TODO remove dependency on OwnCloudClientFactory client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); - // enable cookie tracking + client.setAccount(account); + client.setContext(context); + client.setOwnCloudClientManager(this); + // enable cookie tracking AccountUtils.restoreCookies(account.getSavedAccount(), client, context); account.loadCredentials(context); diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java index b575d31d..f59cfd59 100644 --- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java +++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java @@ -28,6 +28,7 @@ package com.owncloud.android.lib.common.accounts; import java.io.IOException; import org.apache.commons.httpclient.Cookie; +import org.apache.commons.httpclient.HttpStatus; import android.accounts.Account; import android.accounts.AccountManager; @@ -37,9 +38,12 @@ import android.accounts.OperationCanceledException; import android.content.Context; import android.net.Uri; +import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudCredentials; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; @@ -53,45 +57,25 @@ public class AccountUtils { /** * Constructs full url to host and webdav resource basing on host version * - * @param context - * @param account - * @return url or null on failure + * @param context Valid Android {@link Context}, needed to access the {@link AccountManager} + * @param account A stored ownCloud {@link Account} + * @return Full URL to WebDAV endpoint in the server corresponding to 'account'. * @throws AccountNotFoundException When 'account' is unknown for the AccountManager - * @deprecated To be removed in release 1.0. */ - @Deprecated - public static String constructFullURLForAccount(Context context, Account account) throws AccountNotFoundException { - AccountManager ama = AccountManager.get(context); - String baseurl = ama.getUserData(account, Constants.KEY_OC_BASE_URL); - if (baseurl == null) { - throw new AccountNotFoundException(account, "Account not found", null); - } - return baseurl + WEBDAV_PATH_4_0; - } - - /** - * 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 - * @deprecated This method will be removed in version 1.0. - * Use {@link #getBaseUrlForAccount(Context, Account)} - * instead. - */ - @Deprecated - public static String constructBasicURLForAccount(Context context, Account account) + public static String getWebDavUrlForAccount(Context context, Account account) throws AccountNotFoundException { - return getBaseUrlForAccount(context, account); + + return getBaseUrlForAccount(context, account) + WEBDAV_PATH_4_0; } + /** * Extracts url server from the account * - * @param context - * @param account - * @return url server or null on failure + * @param context Valid Android {@link Context}, needed to access the {@link AccountManager} + * @param account A stored ownCloud {@link Account} + * @return Full URL to the server corresponding to 'account', ending in the base path + * common to all API endpoints. * @throws AccountNotFoundException When 'account' is unknown for the AccountManager */ public static String getBaseUrlForAccount(Context context, Account account) @@ -171,7 +155,7 @@ public class AccountUtils { AccountTypeUtils.getAuthTokenTypeAccessToken(account.type), false); - credentials = OwnCloudCredentialsFactory.newBearerCredentials(accessToken); + credentials = OwnCloudCredentialsFactory.newBearerCredentials(username, accessToken); } else if (isSamlSso) { String accessToken = am.blockingGetAuthToken( @@ -300,15 +284,6 @@ public class AccountUtils { public static class Constants { - /** - * Value under this key should handle path to webdav php script. Will be - * removed and usage should be replaced by combining - * {@link #KEY_OC_BASE_URL } and - * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion} - * - * @deprecated - */ - public static final String KEY_OC_URL = "oc_url"; /** * Version should be 3 numbers separated by dot so it can be parsed by * {@link com.owncloud.android.lib.resources.status.OwnCloudVersion} @@ -327,12 +302,6 @@ public class AccountUtils { * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on. */ public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso"; - /** - * Flag signaling if the ownCloud server supports Share API" - * - * @deprecated - */ - public static final String KEY_SUPPORTS_SHARE_API = "oc_supports_share_api"; /** * OC account cookies */ @@ -348,6 +317,11 @@ public class AccountUtils { */ public static final String KEY_DISPLAY_NAME = "oc_display_name"; + /** + * OAuth2 refresh token + **/ + public static final String KEY_OAUTH2_REFRESH_TOKEN = "oc_oauth2_refresh_token"; + } } diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudBasicCredentials.java similarity index 87% rename from src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java rename to src/com/owncloud/android/lib/common/authentication/OwnCloudBasicCredentials.java index 4f7434a2..96ed4568 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java +++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudBasicCredentials.java @@ -21,7 +21,9 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.common; +package com.owncloud.android.lib.common.authentication; + +import com.owncloud.android.lib.common.OwnCloudClient; import java.util.ArrayList; import java.util.List; @@ -29,6 +31,8 @@ import java.util.List; 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.auth.AuthState; +import org.apache.commons.httpclient.auth.BasicScheme; public class OwnCloudBasicCredentials implements OwnCloudCredentials { @@ -50,6 +54,8 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials { @Override public void applyTo(OwnCloudClient client) { + AuthPolicy.registerAuthScheme(AuthState.PREEMPTIVE_AUTH_SCHEME, BasicScheme.class); + List authPrefs = new ArrayList(1); authPrefs.add(AuthPolicy.BASIC); client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); @@ -77,4 +83,9 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials { return false; } + @Override + public boolean authTokenCanBeRefreshed() { + return false; + } + } diff --git a/src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java new file mode 100644 index 00000000..968d6703 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java @@ -0,0 +1,85 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2016 ownCloud GmbH. + * + * 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.authentication; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.httpclient.auth.AuthPolicy; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.auth.AuthState; + +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.authentication.oauth.BearerAuthScheme; +import com.owncloud.android.lib.common.authentication.oauth.BearerCredentials; + +public class OwnCloudBearerCredentials implements OwnCloudCredentials { + + private String mUsername; + private String mAccessToken; + + public OwnCloudBearerCredentials(String username, String accessToken) { + mUsername = username != null ? username : ""; + mAccessToken = accessToken != null ? accessToken : ""; + } + + @Override + public void applyTo(OwnCloudClient client) { + AuthPolicy.registerAuthScheme(BearerAuthScheme.AUTH_POLICY, BearerAuthScheme.class); + AuthPolicy.registerAuthScheme(AuthState.PREEMPTIVE_AUTH_SCHEME, BearerAuthScheme.class); + + List authPrefs = new ArrayList<>(1); + authPrefs.add(BearerAuthScheme.AUTH_POLICY); + client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); + + client.getParams().setAuthenticationPreemptive(true); // true enforces BASIC AUTH ; library is stupid + client.getParams().setCredentialCharset(OwnCloudCredentialsFactory.CREDENTIAL_CHARSET); + client.getState().setCredentials( + AuthScope.ANY, + new BearerCredentials(mAccessToken) + ); + } + + @Override + public String getUsername() { + // not relevant for authentication, but relevant for informational purposes + return mUsername; + } + + @Override + public String getAuthToken() { + return mAccessToken; + } + + @Override + public boolean authTokenExpires() { + return true; + } + + @Override + public boolean authTokenCanBeRefreshed() { + return true; + } + +} diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.java similarity index 81% rename from src/com/owncloud/android/lib/common/OwnCloudCredentials.java rename to src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.java index 0a148682..978dde8b 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java +++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.java @@ -22,16 +22,19 @@ * */ -package com.owncloud.android.lib.common; +package com.owncloud.android.lib.common.authentication; + +import com.owncloud.android.lib.common.OwnCloudClient; public interface OwnCloudCredentials { - public void applyTo(OwnCloudClient ownCloudClient); + void applyTo(OwnCloudClient ownCloudClient); - public String getUsername(); - - public String getAuthToken(); - - public boolean authTokenExpires(); - + String getUsername(); + + String getAuthToken(); + + boolean authTokenExpires(); + + boolean authTokenCanBeRefreshed(); } diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentialsFactory.java similarity index 90% rename from src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java rename to src/com/owncloud/android/lib/common/authentication/OwnCloudCredentialsFactory.java index e7cf12fd..8643aaea 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java +++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentialsFactory.java @@ -22,7 +22,9 @@ * */ -package com.owncloud.android.lib.common; +package com.owncloud.android.lib.common.authentication; + +import com.owncloud.android.lib.common.OwnCloudClient; public class OwnCloudCredentialsFactory { @@ -40,8 +42,8 @@ public class OwnCloudCredentialsFactory { return new OwnCloudBasicCredentials(username, password, preemptiveMode); } - public static OwnCloudCredentials newBearerCredentials(String authToken) { - return new OwnCloudBearerCredentials(authToken); + public static OwnCloudCredentials newBearerCredentials(String username, String authToken) { + return new OwnCloudBearerCredentials(username, authToken); } public static OwnCloudCredentials newSamlSsoCredentials(String username, String sessionCookie) { @@ -76,6 +78,11 @@ public class OwnCloudCredentialsFactory { return false; } + @Override + public boolean authTokenCanBeRefreshed() { + return false; + } + @Override public String getUsername() { // no user name diff --git a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java similarity index 54% rename from src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java rename to src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java index 5a5b28b7..0f475428 100644 --- a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java +++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java @@ -21,63 +21,70 @@ * THE SOFTWARE. * */ -package com.owncloud.android.lib.common; +package com.owncloud.android.lib.common.authentication; import org.apache.commons.httpclient.Cookie; import org.apache.commons.httpclient.cookie.CookiePolicy; import android.net.Uri; +import com.owncloud.android.lib.common.OwnCloudClient; + public class OwnCloudSamlSsoCredentials implements OwnCloudCredentials { - private String mUsername; - private String mSessionCookie; + private String mUsername; + private String mSessionCookie; - public OwnCloudSamlSsoCredentials(String username, String sessionCookie) { - mUsername = username != null ? username : ""; - mSessionCookie = sessionCookie != null ? sessionCookie : ""; - } + public OwnCloudSamlSsoCredentials(String username, String sessionCookie) { + mUsername = username != null ? username : ""; + mSessionCookie = sessionCookie != null ? sessionCookie : ""; + } - @Override - public void applyTo(OwnCloudClient client) { + @Override + public void applyTo(OwnCloudClient client) { client.getParams().setAuthenticationPreemptive(false); client.getParams().setCredentialCharset(OwnCloudCredentialsFactory.CREDENTIAL_CHARSET); client.getParams().setCookiePolicy(CookiePolicy.BROWSER_COMPATIBILITY); client.setFollowRedirects(false); - - Uri serverUri = client.getBaseUri(); - + + Uri serverUri = client.getBaseUri(); + String[] cookies = mSessionCookie.split(";"); if (cookies.length > 0) { - Cookie cookie = null; - for (int i=0; i= 0) { - cookie = new Cookie(); - cookie.setName(cookies[i].substring(0, equalPos)); - cookie.setValue(cookies[i].substring(equalPos + 1)); - cookie.setDomain(serverUri.getHost()); // VERY IMPORTANT - cookie.setPath(serverUri.getPath()); // VERY IMPORTANT - client.getState().addCookie(cookie); - } + Cookie cookie = null; + for (int i = 0; i < cookies.length; i++) { + int equalPos = cookies[i].indexOf('='); + if (equalPos >= 0) { + cookie = new Cookie(); + cookie.setName(cookies[i].substring(0, equalPos)); + cookie.setValue(cookies[i].substring(equalPos + 1)); + cookie.setDomain(serverUri.getHost()); // VERY IMPORTANT + cookie.setPath(serverUri.getPath()); // VERY IMPORTANT + client.getState().addCookie(cookie); + } } } - } + } - @Override - public String getUsername() { - // not relevant for authentication, but relevant for informational purposes - return mUsername; - } - - @Override - public String getAuthToken() { - return mSessionCookie; - } + @Override + public String getUsername() { + // not relevant for authentication, but relevant for informational purposes + return mUsername; + } - @Override - public boolean authTokenExpires() { - return true; - } + @Override + public String getAuthToken() { + return mSessionCookie; + } + + @Override + public boolean authTokenExpires() { + return true; + } + + @Override + public boolean authTokenCanBeRefreshed() { + return false; + } } diff --git a/src/com/owncloud/android/lib/common/network/BearerAuthScheme.java b/src/com/owncloud/android/lib/common/authentication/oauth/BearerAuthScheme.java similarity index 92% rename from src/com/owncloud/android/lib/common/network/BearerAuthScheme.java rename to src/com/owncloud/android/lib/common/authentication/oauth/BearerAuthScheme.java index 75a5cc75..b0104342 100644 --- a/src/com/owncloud/android/lib/common/network/BearerAuthScheme.java +++ b/src/com/owncloud/android/lib/common/authentication/oauth/BearerAuthScheme.java @@ -22,7 +22,7 @@ * */ -package com.owncloud.android.lib.common.network; +package com.owncloud.android.lib.common.authentication.oauth; import java.util.Map; @@ -34,8 +34,6 @@ import org.apache.commons.httpclient.auth.AuthenticationException; import org.apache.commons.httpclient.auth.InvalidCredentialsException; import org.apache.commons.httpclient.auth.MalformedChallengeException; -import com.owncloud.android.lib.common.utils.Log_OC; - /** @@ -71,8 +69,6 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { * @param challenge Authentication challenge * * @throws MalformedChallengeException Thrown if the authentication challenge is malformed - * - * @deprecated Use parameterless constructor and {@link AuthScheme#processChallenge(String)} method */ public BearerAuthScheme(final String challenge) throws MalformedChallengeException { processChallenge(challenge); @@ -125,13 +121,9 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { * for this authentication scheme * @throws AuthenticationException If authorization string cannot be generated due to an authentication failure * @return A bearer authorization string - * - * @deprecated Use {@link #authenticate(Credentials, HttpMethod)} */ public String authenticate(Credentials credentials, String method, String uri) throws AuthenticationException { - Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, String, String)"); - - BearerCredentials bearer = null; + BearerCredentials bearer; try { bearer = (BearerCredentials) credentials; } catch (ClassCastException e) { @@ -164,8 +156,6 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { * @return a basic authorization string */ public String authenticate(Credentials credentials, HttpMethod method) throws AuthenticationException { - Log_OC.d(TAG, "enter BearerScheme.authenticate(Credentials, HttpMethod)"); - if (method == null) { throw new IllegalArgumentException("Method may not be null"); } @@ -183,9 +173,7 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { } /** - * @deprecated Use {@link #authenticate(BearerCredentials, String)} - * - * Returns a bearer Authorization header value for the given + * Returns a bearer Authorization header value for the given * {@link BearerCredentials}. * * @param credentials The credentials to encode. @@ -208,7 +196,6 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { * @since 3.0 */ public static String authenticate(BearerCredentials credentials, String charset) { - Log_OC.d(TAG, "enter BearerAuthScheme.authenticate(BearerCredentials, String)"); if (credentials == null) { throw new IllegalArgumentException("Credentials may not be null"); @@ -218,8 +205,7 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { } StringBuffer buffer = new StringBuffer(); buffer.append(credentials.getAccessToken()); - - //return "Bearer " + EncodingUtil.getAsciiString(EncodingUtil.getBytes(buffer.toString(), charset)); + return "Bearer " + buffer.toString(); } @@ -239,8 +225,6 @@ public class BearerAuthScheme implements AuthScheme /*extends RFC2617Scheme*/ { * This method simply returns the realm for the challenge. * * @return String a String identifying the authentication challenge. - * - * @deprecated no longer used */ @Override public String getID() { diff --git a/src/com/owncloud/android/lib/common/network/BearerCredentials.java b/src/com/owncloud/android/lib/common/authentication/oauth/BearerCredentials.java similarity index 97% rename from src/com/owncloud/android/lib/common/network/BearerCredentials.java rename to src/com/owncloud/android/lib/common/authentication/oauth/BearerCredentials.java index 8de3ec76..c815bc4c 100644 --- a/src/com/owncloud/android/lib/common/network/BearerCredentials.java +++ b/src/com/owncloud/android/lib/common/authentication/oauth/BearerCredentials.java @@ -22,7 +22,7 @@ * */ -package com.owncloud.android.lib.common.network; +package com.owncloud.android.lib.common.authentication.oauth; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.util.LangUtils; diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java new file mode 100644 index 00000000..24814167 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java @@ -0,0 +1,66 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +public class OAuth2ClientConfiguration { + + private String mClientId; + + private String mClientSecret; + + private String mRedirectUri; + + public OAuth2ClientConfiguration(String clientId, String clientSecret, String redirectUri) { + mClientId = (clientId == null) ? "" : clientId; + mClientSecret = (clientSecret == null) ? "" : clientSecret; + mRedirectUri = (redirectUri == null) ? "" : redirectUri; + } + + public String getClientId() { + return mClientId; + } + + public void setClientId(String clientId) { + mClientId = (clientId == null) ? "" : clientId; + } + + public String getClientSecret() { + return mClientSecret; + } + + public void setClientSecret(String clientSecret) { + mClientSecret = (clientSecret == null) ? "" : clientSecret; + } + + public String getRedirectUri() { + return mRedirectUri; + } + + public void setRedirectUri(String redirectUri) { + this.mRedirectUri = (redirectUri == null) ? "" : redirectUri; + } +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java new file mode 100644 index 00000000..ad48e5b1 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java @@ -0,0 +1,68 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +/** + * Constant values for OAuth 2 protocol. + * + * Includes required and optional parameter NAMES used in the 'authorization code' grant type. + */ + +public class OAuth2Constants { + + /// Parameters to send to the Authorization Endpoint + public static final String KEY_RESPONSE_TYPE = "response_type"; + public static final String KEY_REDIRECT_URI = "redirect_uri"; + public static final String KEY_CLIENT_ID = "client_id"; + public static final String KEY_SCOPE = "scope"; + public static final String KEY_STATE = "state"; + + /// Additional parameters to send to the Token Endpoint + public static final String KEY_GRANT_TYPE = "grant_type"; + public static final String KEY_CODE = "code"; + + // Used to get the Access Token using Refresh Token + public static final String OAUTH2_REFRESH_TOKEN_GRANT_TYPE = "refresh_token"; + + /// Parameters received in an OK response from the Token Endpoint + public static final String KEY_ACCESS_TOKEN = "access_token"; + public static final String KEY_TOKEN_TYPE = "token_type"; + public static final String KEY_EXPIRES_IN = "expires_in"; + public static final String KEY_REFRESH_TOKEN = "refresh_token"; + + /// Parameters in an ERROR response + public static final String KEY_ERROR = "error"; + public static final String KEY_ERROR_DESCRIPTION = "error_description"; + public static final String KEY_ERROR_URI = "error_uri"; + public static final String VALUE_ERROR_ACCESS_DENIED = "access_denied"; + + /// Extra not standard + public static final String KEY_USER_ID = "user_id"; + + /// Depends on oauth2 grant type + public static final String OAUTH2_RESPONSE_TYPE_CODE = "code"; +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java new file mode 100644 index 00000000..33b3c8b5 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java @@ -0,0 +1,148 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import android.net.Uri; + +import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; + +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.httpclient.methods.PostMethod; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + + +public class OAuth2GetAccessTokenOperation extends RemoteOperation { + + private String mGrantType; + private String mCode; + private String mClientId; + private String mClientSecret; + private String mRedirectUri; + private final String mAccessTokenEndpointPath; + + private final OAuth2ResponseParser mResponseParser; + + + public OAuth2GetAccessTokenOperation( + String grantType, + String code, + String clientId, + String secretId, + String redirectUri, + String accessTokenEndpointPath + ) { + mClientId = clientId; + mClientSecret = secretId; + mRedirectUri = redirectUri; + mGrantType = grantType; + mCode = code; + + mAccessTokenEndpointPath = + accessTokenEndpointPath != null ? + accessTokenEndpointPath : + OwnCloudOAuth2Provider.ACCESS_TOKEN_ENDPOINT_PATH + ; + + mResponseParser = new OAuth2ResponseParser(); + } + + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + RemoteOperationResult result = null; + PostMethod postMethod = null; + + try { + NameValuePair[] nameValuePairs = new NameValuePair[4]; + nameValuePairs[0] = new NameValuePair(OAuth2Constants.KEY_GRANT_TYPE, mGrantType); + nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CODE, mCode); + nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REDIRECT_URI, mRedirectUri); + nameValuePairs[3] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId); + + Uri.Builder uriBuilder = client.getBaseUri().buildUpon(); + uriBuilder.appendEncodedPath(mAccessTokenEndpointPath); + + postMethod = new PostMethod(uriBuilder.build().toString()); + postMethod.setRequestBody(nameValuePairs); + + OwnCloudCredentials oauthCredentials = new OwnCloudBasicCredentials( + mClientId, + mClientSecret + ); + OwnCloudCredentials oldCredentials = switchClientCredentials(oauthCredentials); + + client.executeMethod(postMethod); + switchClientCredentials(oldCredentials); + + String response = postMethod.getResponseBodyAsString(); + if (response != null && response.length() > 0) { + JSONObject tokenJson = new JSONObject(response); + Map accessTokenResult = + mResponseParser.parseAccessTokenResult(tokenJson); + if (accessTokenResult.get(OAuth2Constants.KEY_ERROR) != null || + accessTokenResult.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) { + result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR); + + } else { + result = new RemoteOperationResult(true, postMethod); + ArrayList data = new ArrayList<>(); + data.add(accessTokenResult); + result.setData(data); + } + + } else { + result = new RemoteOperationResult(false, postMethod); + client.exhaustResponse(postMethod.getResponseBodyAsStream()); + } + + } catch (Exception e) { + result = new RemoteOperationResult(e); + + } finally { + if (postMethod != null) + postMethod.releaseConnection(); // let the connection available for other methods + } + + return result; + } + + private OwnCloudCredentials switchClientCredentials(OwnCloudCredentials newCredentials) { + OwnCloudCredentials previousCredentials = getClient().getCredentials(); + getClient().setCredentials(newCredentials); + return previousCredentials; + } + +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java new file mode 100644 index 00000000..b9923994 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java @@ -0,0 +1,46 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +public enum OAuth2GrantType { + AUTHORIZATION_CODE("authorization_code"), + IMPLICIT("implicit"), + PASSWORD("password"), + CLIENT_CREDENTIAL("client_credentials"), + REFRESH_TOKEN("refresh_token") // not a grant type conceptually, but used as such to refresh access tokens + ; + + private String mValue; + + OAuth2GrantType(String value) { + mValue = value; + } + + public String getValue() { + return mValue; + } +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java new file mode 100644 index 00000000..d259d80e --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java @@ -0,0 +1,66 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +public interface OAuth2Provider { + + /** + * {@link OAuth2RequestBuilder} implementation for this provider. + * + * @return {@link OAuth2RequestBuilder} implementation. + */ + OAuth2RequestBuilder getOperationBuilder(); + + + /** + * Set configuration of the client that will use this {@link OAuth2Provider} + * @param oAuth2ClientConfiguration Configuration of the client that will use this {@link OAuth2Provider} + */ + void setClientConfiguration(OAuth2ClientConfiguration oAuth2ClientConfiguration); + + /** + * Configuration of the client that is using this {@link OAuth2Provider} + * return Configuration of the client that is usinng this {@link OAuth2Provider} + */ + OAuth2ClientConfiguration getClientConfiguration(); + + + /** + * Set base URI to authorization server. + * + * @param authorizationServerUri Set base URL to authorization server. + */ + void setAuthorizationServerUri(String authorizationServerUri); + + /** + * base URI to authorization server. + * + * @return Base URL to authorization server. + */ + String getAuthorizationServerUri(); + +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java new file mode 100644 index 00000000..32aac0ad --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java @@ -0,0 +1,122 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + + +import java.util.HashMap; +import java.util.Map; + +public class OAuth2ProvidersRegistry { + + private Map mProviders = new HashMap<>(); + + private OAuth2Provider mDefaultProvider = null; + + private OAuth2ProvidersRegistry () { + } + + /** + * See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom + */ + private static class LazyHolder { + private static final OAuth2ProvidersRegistry INSTANCE = new OAuth2ProvidersRegistry(); + } + + /** + * Singleton accesor. + * + * @return Singleton isntance of {@link OAuth2ProvidersRegistry} + */ + public static OAuth2ProvidersRegistry getInstance() { + return LazyHolder.INSTANCE; + } + + /** + * Register an {@link OAuth2Provider} with the name passed as parameter. + * + * @param name Name to bind 'oAuthProvider' in the registry. + * @param oAuth2Provider An {@link OAuth2Provider} instance to keep in the registry. + * @throws IllegalArgumentException if 'name' or 'oAuthProvider' are null. + */ + public void registerProvider(String name, OAuth2Provider oAuth2Provider) { + if (name == null) { + throw new IllegalArgumentException("Name must not be NULL"); + } + if (oAuth2Provider == null) { + throw new IllegalArgumentException("oAuth2Provider must not be NULL"); + } + + mProviders.put(name, oAuth2Provider); + if (mProviders.size() == 1) { + mDefaultProvider = oAuth2Provider; + } + } + + public OAuth2Provider unregisterProvider(String name) { + OAuth2Provider unregisteredProvider = mProviders.remove(name); + if (mProviders.size() == 0) { + mDefaultProvider = null; + } else if (unregisteredProvider != null && unregisteredProvider == mDefaultProvider) { + mDefaultProvider = mProviders.values().iterator().next(); + } + return unregisteredProvider; + } + + /** + * Get default {@link OAuth2Provider}. + * + * @return Default provider, or NULL if there is no provider. + */ + public OAuth2Provider getProvider() { + return mDefaultProvider; + } + + /** + * Get {@link OAuth2Provider} registered with the name passed as parameter. + * + * @param name Name used to register the desired {@link OAuth2Provider} + * @return {@link OAuth2Provider} registered with the name 'name' + */ + public OAuth2Provider getProvider(String name) { + return mProviders.get(name); + } + + /** + * Sets the {@link OAuth2Provider} registered with the name passed as parameter as the default provider + * + * @param name Name used to register the {@link OAuth2Provider} to set as default. + * @return {@link OAuth2Provider} set as default, or NULL if no provider was registered with 'name'. + */ + public OAuth2Provider setDefaultProvider(String name) { + OAuth2Provider toDefault = mProviders.get(name); + if (toDefault != null) { + mDefaultProvider = toDefault; + } + return toDefault; + } + +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java new file mode 100644 index 00000000..24825d2a --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java @@ -0,0 +1,73 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import com.owncloud.android.lib.common.utils.Log_OC; + +import java.util.HashMap; +import java.util.Map; + + +public class OAuth2QueryParser { + + private static final String TAG = OAuth2QueryParser.class.getName(); + + private Map mOAuth2ParsedAuthorizationResponse; + + public OAuth2QueryParser() { + mOAuth2ParsedAuthorizationResponse = new HashMap<>(); + } + + public Map parse(String query) { + mOAuth2ParsedAuthorizationResponse.clear(); + + String[] pairs = query.split("&"); + int i = 0; + String key = ""; + String value; + while (pairs.length > i) { + int j = 0; + String[] part = pairs[i].split("="); + while (part.length > j) { + String p = part[j]; + if (j == 0) { + key = p; + } else if (j == 1) { + value = p; + mOAuth2ParsedAuthorizationResponse.put(key, value); + } + + Log_OC.v(TAG, "[" + i + "," + j + "] = " + p); + j++; + } + i++; + } + + return mOAuth2ParsedAuthorizationResponse; + } + +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RefreshAccessTokenOperation.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RefreshAccessTokenOperation.java new file mode 100644 index 00000000..cbdb48b1 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RefreshAccessTokenOperation.java @@ -0,0 +1,146 @@ +/** + * ownCloud Android client application + * + * @author David González Verdugo + * + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import android.net.Uri; + +import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials; +import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; +import com.owncloud.android.lib.common.utils.Log_OC; + +import org.apache.commons.httpclient.NameValuePair; +import org.apache.commons.httpclient.methods.PostMethod; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.Map; + +public class OAuth2RefreshAccessTokenOperation extends RemoteOperation { + + private static final String TAG = OAuth2RefreshAccessTokenOperation.class.getSimpleName(); + + private String mClientId; + private String mClientSecret; + private String mRefreshToken; + + private final String mAccessTokenEndpointPath; + + private final OAuth2ResponseParser mResponseParser; + + public OAuth2RefreshAccessTokenOperation( + String clientId, + String secretId, + String refreshToken, + String accessTokenEndpointPath + ) { + + mClientId = clientId; + mClientSecret = secretId; + mRefreshToken = refreshToken; + + mAccessTokenEndpointPath = + accessTokenEndpointPath != null ? + accessTokenEndpointPath : + OwnCloudOAuth2Provider.ACCESS_TOKEN_ENDPOINT_PATH + ; + + mResponseParser = new OAuth2ResponseParser(); + } + + @Override + protected RemoteOperationResult run(OwnCloudClient client) { + + RemoteOperationResult result = null; + PostMethod postMethod = null; + + try { + NameValuePair[] nameValuePairs = new NameValuePair[3]; + nameValuePairs[0] = new NameValuePair( + OAuth2Constants.KEY_GRANT_TYPE, + OAuth2GrantType.REFRESH_TOKEN.getValue() // always for this operation + ); + nameValuePairs[1] = new NameValuePair(OAuth2Constants.KEY_CLIENT_ID, mClientId); + nameValuePairs[2] = new NameValuePair(OAuth2Constants.KEY_REFRESH_TOKEN, mRefreshToken); + + Uri.Builder uriBuilder = client.getBaseUri().buildUpon(); + uriBuilder.appendEncodedPath(mAccessTokenEndpointPath); + + postMethod = new PostMethod(uriBuilder.build().toString()); + postMethod.setRequestBody(nameValuePairs); + + OwnCloudCredentials oauthCredentials = new OwnCloudBasicCredentials( + mClientId, + mClientSecret + ); + + OwnCloudCredentials oldCredentials = switchClientCredentials(oauthCredentials); + + client.executeMethod(postMethod); + + switchClientCredentials(oldCredentials); + + String response = postMethod.getResponseBodyAsString(); + Log_OC.d(TAG, "OAUTH2: raw response from POST TOKEN: " + response); + + if (response != null && response.length() > 0) { + JSONObject tokenJson = new JSONObject(response); + Map accessTokenResult = + mResponseParser.parseAccessTokenResult(tokenJson); + if (accessTokenResult.get(OAuth2Constants.KEY_ERROR) != null || + accessTokenResult.get(OAuth2Constants.KEY_ACCESS_TOKEN) == null) { + result = new RemoteOperationResult(ResultCode.OAUTH2_ERROR); + + } else { + result = new RemoteOperationResult(true, postMethod); + ArrayList data = new ArrayList<>(); + data.add(accessTokenResult); + result.setData(data); + } + + } else { + result = new RemoteOperationResult(false, postMethod); + client.exhaustResponse(postMethod.getResponseBodyAsStream()); + } + + } catch (Exception e) { + result = new RemoteOperationResult(e); + + } finally { + if (postMethod != null) { + postMethod.releaseConnection(); // let the connection available for other methods + } + } + + return result; + } + + private OwnCloudCredentials switchClientCredentials(OwnCloudCredentials newCredentials) { + OwnCloudCredentials previousCredentials = getClient().getCredentials(); + getClient().setCredentials(newCredentials); + return previousCredentials; + } + +} \ No newline at end of file diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java new file mode 100644 index 00000000..23b3cdb9 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java @@ -0,0 +1,48 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import com.owncloud.android.lib.common.operations.RemoteOperation; + +public interface OAuth2RequestBuilder { + + enum OAuthRequest { + GET_AUTHORIZATION_CODE, CREATE_ACCESS_TOKEN, REFRESH_ACCESS_TOKEN + } + + void setRequest(OAuthRequest operation); + + void setGrantType(OAuth2GrantType grantType); + + void setAuthorizationCode(String code); + + void setRefreshToken(String refreshToken); + + RemoteOperation buildOperation(); + + String buildUri(); +} \ No newline at end of file diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ResponseParser.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ResponseParser.java new file mode 100644 index 00000000..068165f2 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ResponseParser.java @@ -0,0 +1,77 @@ +/** + * ownCloud Android client application + * + * @author David A. Velasco + * + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + + +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.Map; + +class OAuth2ResponseParser { + + Map parseAccessTokenResult(JSONObject tokenJson) throws JSONException { + Map resultTokenMap = new HashMap<>(); + + if (tokenJson.has(OAuth2Constants.KEY_ACCESS_TOKEN)) { + resultTokenMap.put(OAuth2Constants.KEY_ACCESS_TOKEN, tokenJson. + getString(OAuth2Constants.KEY_ACCESS_TOKEN)); + } + if (tokenJson.has(OAuth2Constants.KEY_TOKEN_TYPE)) { + resultTokenMap.put(OAuth2Constants.KEY_TOKEN_TYPE, tokenJson. + getString(OAuth2Constants.KEY_TOKEN_TYPE)); + } + if (tokenJson.has(OAuth2Constants.KEY_EXPIRES_IN)) { + resultTokenMap.put(OAuth2Constants.KEY_EXPIRES_IN, tokenJson. + getString(OAuth2Constants.KEY_EXPIRES_IN)); + } + if (tokenJson.has(OAuth2Constants.KEY_REFRESH_TOKEN)) { + resultTokenMap.put(OAuth2Constants.KEY_REFRESH_TOKEN, tokenJson. + getString(OAuth2Constants.KEY_REFRESH_TOKEN)); + } + if (tokenJson.has(OAuth2Constants.KEY_SCOPE)) { + resultTokenMap.put(OAuth2Constants.KEY_SCOPE, tokenJson. + getString(OAuth2Constants.KEY_SCOPE)); + } + if (tokenJson.has(OAuth2Constants.KEY_ERROR)) { + resultTokenMap.put(OAuth2Constants.KEY_ERROR, tokenJson. + getString(OAuth2Constants.KEY_ERROR)); + } + if (tokenJson.has(OAuth2Constants.KEY_ERROR_DESCRIPTION)) { + resultTokenMap.put(OAuth2Constants.KEY_ERROR_DESCRIPTION, tokenJson. + getString(OAuth2Constants.KEY_ERROR_DESCRIPTION)); + } + if (tokenJson.has(OAuth2Constants.KEY_ERROR_URI)) { + resultTokenMap.put(OAuth2Constants.KEY_ERROR_URI, tokenJson. + getString(OAuth2Constants.KEY_ERROR_URI)); + } + + if (tokenJson.has(OAuth2Constants.KEY_USER_ID)) { // not standard + resultTokenMap.put(OAuth2Constants.KEY_USER_ID, tokenJson. + getString(OAuth2Constants.KEY_USER_ID)); + } + + return resultTokenMap; + } + +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java new file mode 100644 index 00000000..63b43b3e --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java @@ -0,0 +1,94 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import com.owncloud.android.lib.common.utils.Log_OC; + +public class OwnCloudOAuth2Provider implements OAuth2Provider { + + public static final String NAME = OAuth2Provider.class.getName(); + + public static final String ACCESS_TOKEN_ENDPOINT_PATH = "index.php/apps/oauth2/api/v1/token"; + private static final String AUTHORIZATION_CODE_ENDPOINT_PATH = "index.php/apps/oauth2/authorize"; + + private String mAuthorizationServerUrl = ""; + private String mAccessTokenEndpointPath = ACCESS_TOKEN_ENDPOINT_PATH; + private String mAuthorizationCodeEndpointPath = AUTHORIZATION_CODE_ENDPOINT_PATH; + + private OAuth2ClientConfiguration mClientConfiguration; + + @Override + public OAuth2RequestBuilder getOperationBuilder() { + return new OwnCloudOAuth2RequestBuilder(this); + } + + @Override + public void setClientConfiguration(OAuth2ClientConfiguration oAuth2ClientConfiguration) { + mClientConfiguration = oAuth2ClientConfiguration; + } + + @Override + public OAuth2ClientConfiguration getClientConfiguration() { + return mClientConfiguration; + } + + @Override + public void setAuthorizationServerUri(String authorizationServerUri) { + mAuthorizationServerUrl = authorizationServerUri; + } + + @Override + public String getAuthorizationServerUri() { + return mAuthorizationServerUrl; + } + + public String getAccessTokenEndpointPath() { + return mAccessTokenEndpointPath; + } + + public void setAccessTokenEndpointPath(String accessTokenEndpointPath) { + if (accessTokenEndpointPath == null || accessTokenEndpointPath.length() <= 0) { + Log_OC.w(NAME, "Setting invalid access token endpoint path, going on with default"); + mAccessTokenEndpointPath = ACCESS_TOKEN_ENDPOINT_PATH; + } else { + mAccessTokenEndpointPath = accessTokenEndpointPath; + } + } + + public String getAuthorizationCodeEndpointPath() { + return mAuthorizationCodeEndpointPath; + } + + public void setAuthorizationCodeEndpointPath(String authorizationCodeEndpointPath) { + if (authorizationCodeEndpointPath == null || authorizationCodeEndpointPath.length() <= 0) { + Log_OC.w(NAME, "Setting invalid authorization code endpoint path, going on with default"); + mAuthorizationCodeEndpointPath = AUTHORIZATION_CODE_ENDPOINT_PATH; + } else { + mAuthorizationCodeEndpointPath = authorizationCodeEndpointPath; + } + } +} diff --git a/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java new file mode 100644 index 00000000..18933824 --- /dev/null +++ b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java @@ -0,0 +1,156 @@ +/* ownCloud Android Library is available under MIT license + * + * @author David A. Velasco + * Copyright (C) 2017 ownCloud GmbH. + * + * 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.authentication.oauth; + +import android.net.Uri; + +import com.owncloud.android.lib.common.operations.RemoteOperation; + +public class OwnCloudOAuth2RequestBuilder implements OAuth2RequestBuilder { + + private OwnCloudOAuth2Provider mOAuth2Provider; + + private OAuthRequest mRequest; + private OAuth2GrantType mGrantType = OAuth2GrantType.AUTHORIZATION_CODE; + private String mCode; + private String mRefreshToken; + + public OwnCloudOAuth2RequestBuilder(OwnCloudOAuth2Provider ownCloudOAuth2Provider) { + mOAuth2Provider = ownCloudOAuth2Provider; + } + + @Override + public void setRequest(OAuthRequest request) { + mRequest = request; + } + + @Override + public void setGrantType(OAuth2GrantType grantType) { + mGrantType = grantType; + } + + @Override + public void setAuthorizationCode(String code) { + mCode = code; + } + + @Override + public void setRefreshToken(String refreshToken) { + mRefreshToken = refreshToken; + } + + @Override + public RemoteOperation buildOperation() { + if (mGrantType != OAuth2GrantType.AUTHORIZATION_CODE && + mGrantType != OAuth2GrantType.REFRESH_TOKEN) { + throw new UnsupportedOperationException( + "Unsupported grant type. Only " + + OAuth2GrantType.AUTHORIZATION_CODE.getValue() + " and " + + OAuth2GrantType.REFRESH_TOKEN + " are supported" + ); + } + OAuth2ClientConfiguration clientConfiguration = mOAuth2Provider.getClientConfiguration(); + + switch(mRequest) { + case CREATE_ACCESS_TOKEN: + return new OAuth2GetAccessTokenOperation( + mGrantType.getValue(), + mCode, + clientConfiguration.getClientId(), + clientConfiguration.getClientSecret(), + clientConfiguration.getRedirectUri(), + mOAuth2Provider.getAccessTokenEndpointPath() + ); + + case REFRESH_ACCESS_TOKEN: + return new OAuth2RefreshAccessTokenOperation( + clientConfiguration.getClientId(), + clientConfiguration.getClientSecret(), + mRefreshToken, + mOAuth2Provider.getAccessTokenEndpointPath() + ); + default: + throw new UnsupportedOperationException( + "Unsupported request" + ); + } + } + + @Override + public String buildUri() { + if (OAuth2GrantType.AUTHORIZATION_CODE != mGrantType) { + throw new UnsupportedOperationException( + "Unsupported grant type. Only " + + OAuth2GrantType.AUTHORIZATION_CODE.getValue() + " is supported by this provider" + ); + } + OAuth2ClientConfiguration clientConfiguration = mOAuth2Provider.getClientConfiguration(); + Uri uri; + Uri.Builder uriBuilder; + switch(mRequest) { + case GET_AUTHORIZATION_CODE: + uri = Uri.parse(mOAuth2Provider.getAuthorizationServerUri()); + uriBuilder = uri.buildUpon(); + uriBuilder.appendEncodedPath(mOAuth2Provider.getAuthorizationCodeEndpointPath()); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_RESPONSE_TYPE, OAuth2Constants.OAUTH2_RESPONSE_TYPE_CODE + ); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_REDIRECT_URI, clientConfiguration.getRedirectUri() + ); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_CLIENT_ID, clientConfiguration.getClientId() + ); + + uri = uriBuilder.build(); + return uri.toString(); + + case CREATE_ACCESS_TOKEN: + uri = Uri.parse(mOAuth2Provider.getAuthorizationServerUri()); + uriBuilder = uri.buildUpon(); + uriBuilder.appendEncodedPath(mOAuth2Provider.getAccessTokenEndpointPath()); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_RESPONSE_TYPE, OAuth2Constants.OAUTH2_RESPONSE_TYPE_CODE + ); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_REDIRECT_URI, clientConfiguration.getRedirectUri() + ); + uriBuilder.appendQueryParameter( + OAuth2Constants.KEY_CLIENT_ID, clientConfiguration.getClientId() + ); + + uri = uriBuilder.build(); + return uri.toString(); + + default: + throw new UnsupportedOperationException( + "Unsupported request" + ); + } + } + +} diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java index bb8ea421..a8de3938 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java @@ -1,5 +1,5 @@ /* ownCloud Android Library is available under MIT license - * Copyright (C) 2016 ownCloud GmbH. + * Copyright (C) 2017 ownCloud GmbH. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,19 +25,16 @@ package com.owncloud.android.lib.common.operations; import android.accounts.Account; -import android.accounts.AccountManager; import android.accounts.AccountsException; -import android.app.Activity; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.content.Context; import android.os.Handler; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.OwnCloudCredentials; import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; import java.io.IOException; @@ -45,58 +42,69 @@ import java.io.IOException; /** * Operation which execution involves one or several interactions with an ownCloud server. - * + * * Provides methods to execute the operation both synchronously or asynchronously. - * - * @author David A. Velasco + * + * @author David A. Velasco + * @author David González Verdugo */ public abstract class RemoteOperation implements Runnable { - + private static final String TAG = RemoteOperation.class.getSimpleName(); - /** OCS API header name */ + /** + * OCS API header name + */ public static final String OCS_API_HEADER = "OCS-APIREQUEST"; - /** OCS API header value */ + /** + * OCS API header value + */ public static final String OCS_API_HEADER_VALUE = "true"; - /** ownCloud account in the remote ownCloud server to operate */ + /** + * ownCloud account in the remote ownCloud server to operate + */ private Account mAccount = null; - - /** Android Application context */ + + /** + * Android Application context + */ private Context mContext = null; - - /** Object to interact with the remote server */ - private OwnCloudClient mClient = null; - - /** Callback object to notify about the execution of the remote operation */ - private OnRemoteOperationListener mListener = null; - - /** Handler to the thread where mListener methods will be called */ - private Handler mListenerHandler = null; - /** Activity */ - private Activity mCallerActivity; + /** + * Object to interact with the remote server + */ + private OwnCloudClient mClient = null; + + /** + * Callback object to notify about the execution of the remote operation + */ + private OnRemoteOperationListener mListener = null; + + /** + * Handler to the thread where mListener methods will be called + */ + private Handler mListenerHandler = null; + + /** + * Abstract method to implement the operation in derived classes. + */ + protected abstract RemoteOperationResult run(OwnCloudClient client); - - /** - * Abstract method to implement the operation in derived classes. - */ - protected abstract RemoteOperationResult run(OwnCloudClient client); - /** * Synchronously executes the remote operation on the received ownCloud account. - * + * * Do not call this method from the main thread. - * + * * This method should be used whenever an ownCloud account is available, instead of * {@link #execute(OwnCloudClient)}. - * - * @param account ownCloud account in remote ownCloud server to reach during the - * execution of the operation. - * @param context Android context for the component calling the method. - * @return Result of the operation. + * + * @param account ownCloud account in remote ownCloud server to reach during the + * execution of the operation. + * @param context Android context for the component calling the method. + * @return Result of the operation. */ public RemoteOperationResult execute(Account account, Context context) { if (account == null) @@ -107,258 +115,187 @@ public abstract class RemoteOperation implements Runnable { "Context"); mAccount = account; mContext = context.getApplicationContext(); - try { - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, mContext); - } catch (Exception e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); - return new RemoteOperationResult(e); - } - return run(mClient); + + return runOperation(); } - - - /** - * Synchronously executes the remote operation - * + + + /** + * Synchronously executes the remote operation + * * Do not call this method from the main thread. - * - * @param client Client object to reach an ownCloud server during the execution of - * the operation. - * @return Result of the operation. - */ - public RemoteOperationResult execute(OwnCloudClient client) { - if (client == null) - throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " + - "OwnCloudClient"); - mClient = client; - return run(client); - } - - - /** - * Asynchronously executes the remote operation - * - * This method should be used whenever an ownCloud account is available, instead of - * {@link #execute(OwnCloudClient)}. - * - * @deprecated This method will be removed in version 1.0. - * Use {@link #execute(Account, Context, OnRemoteOperationListener, - * Handler)} instead. - * - * @param account ownCloud account in remote ownCloud server to reach during - * the execution of the operation. - * @param context Android context for the component calling the method. - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener - * objects must be called. - * @return Thread were the remote operation is executed. + * + * @param client Client object to reach an ownCloud server during the execution of + * the operation. + * @return Result of the operation. */ - @Deprecated - public Thread execute(Account account, Context context, OnRemoteOperationListener listener, - Handler listenerHandler, Activity callerActivity) { - if (account == null) - throw new IllegalArgumentException - ("Trying to execute a remote operation with a NULL Account"); - if (context == null) - throw new IllegalArgumentException - ("Trying to execute a remote operation with a NULL Context"); - mAccount = account; - mContext = context.getApplicationContext(); - mCallerActivity = callerActivity; - mClient = null; // the client instance will be created from mAccount - // and mContext in the runnerThread to create below - mListener = listener; - - mListenerHandler = listenerHandler; - - Thread runnerThread = new Thread(this); - runnerThread.start(); - return runnerThread; + public RemoteOperationResult execute(OwnCloudClient client) { + if (client == null) + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " + + "OwnCloudClient"); + mClient = client; + if (client.getAccount() != null) { + mAccount = client.getAccount().getSavedAccount(); + } + mContext = client.getContext(); + + return runOperation(); } - + /** * Asynchronously executes the remote operation - * - * This method should be used whenever an ownCloud account is available, + * + * This method should be used whenever an ownCloud account is available, * instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}. - * - * @param account ownCloud account in remote ownCloud server to reach during the - * execution of the operation. - * @param context Android context for the component calling the method. - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler associated to the thread where the methods of the listener - * objects must be called. - * @return Thread were the remote operation is executed. + * + * @param account ownCloud account in remote ownCloud server to reach during the + * execution of the operation. + * @param context Android context for the component calling the method. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler associated to the thread where the methods of the listener + * objects must be called. + * @return Thread were the remote operation is executed. */ public Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler) { - + if (account == null) throw new IllegalArgumentException ("Trying to execute a remote operation with a NULL Account"); if (context == null) throw new IllegalArgumentException ("Trying to execute a remote operation with a NULL Context"); + // mAccount and mContext in the runnerThread to create below mAccount = account; mContext = context.getApplicationContext(); - mCallerActivity = null; mClient = null; // the client instance will be created from - // mAccount and mContext in the runnerThread to create below - + mListener = listener; - + mListenerHandler = listenerHandler; - + Thread runnerThread = new Thread(this); runnerThread.start(); return runnerThread; } - - /** - * Asynchronously executes the remote operation - * - * @param client Client object to reach an ownCloud server - * during the execution of the operation. - * @param listener Listener to be notified about the execution of the operation. - * @param listenerHandler Handler, if passed in, associated to the thread where the methods of - * the listener objects must be called. - * @return Thread were the remote operation is executed. - */ - public Thread execute(OwnCloudClient client, + + /** + * Asynchronously executes the remote operation + * + * @param client Client object to reach an ownCloud server + * during the execution of the operation. + * @param listener Listener to be notified about the execution of the operation. + * @param listenerHandler Handler, if passed in, associated to the thread where the methods of + * the listener objects must be called. + * @return Thread were the remote operation is executed. + */ + public Thread execute(OwnCloudClient client, OnRemoteOperationListener listener, Handler listenerHandler) { - if (client == null) { - throw new IllegalArgumentException + if (client == null) { + throw new IllegalArgumentException ("Trying to execute a remote operation with a NULL OwnCloudClient"); - } - mClient = client; - - if (listener == null) { - throw new IllegalArgumentException + } + mClient = client; + if (client.getAccount() != null) { + mAccount = client.getAccount().getSavedAccount(); + } + mContext = client.getContext(); + + if (listener == null) { + throw new IllegalArgumentException ("Trying to execute a remote operation asynchronously " + "without a listener to notiy the result"); - } - mListener = listener; - - if (listenerHandler != null) { + } + mListener = listener; + + if (listenerHandler != null) { mListenerHandler = listenerHandler; - } + } - - Thread runnerThread = new Thread(this); - runnerThread.start(); - return runnerThread; - } + Thread runnerThread = new Thread(this); + runnerThread.start(); + return runnerThread; + } - /** - * Asynchronous execution of the operation - * started by {@link RemoteOperation#execute(OwnCloudClient, - * OnRemoteOperationListener, Handler)}, - * and result posting. - * - * TODO refactor && clean the code; now it's a mess - */ + /** + * Asynchronous execution of the operation + * started by {@link RemoteOperation#execute(OwnCloudClient, + * OnRemoteOperationListener, Handler)}, + * and result posting. + */ @Override public final void run() { - RemoteOperationResult result = null; - boolean repeat = false; - do { - try{ - if (mClient == null) { - if (mAccount != null && mContext != null) { - /** DEPRECATED BLOCK - will be removed at version 1.0 */ - if (mCallerActivity != null) { - mClient = OwnCloudClientFactory.createOwnCloudClient( - mAccount, mContext, mCallerActivity); - } else { - /** EOF DEPRECATED */ - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); - mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, mContext); - } - - } else { - throw new IllegalStateException("Trying to run a remote operation " + - "asynchronously with no client instance or account"); - } - } - - } catch (IOException e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, - new AccountsException("I/O exception while trying to authorize the account", - e)); - result = new RemoteOperationResult(e); - - } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); - result = new RemoteOperationResult(e); - } - - if (result == null) - result = run(mClient); - - repeat = false; - /** DEPRECATED BLOCK - will be removed at version 1.0 ; don't trust in this code - * to trigger authentication update */ - if (mCallerActivity != null && mAccount != null && mContext != null && - !result.isSuccess() && - ResultCode.UNAUTHORIZED.equals(result.getCode()) - ) { - /// possible fail due to lack of authorization - // in an operation performed in foreground - OwnCloudCredentials cred = mClient.getCredentials(); - if (cred != null) { - /// confirmed : unauthorized operation - AccountManager am = AccountManager.get(mContext); - if (cred.authTokenExpires()) { - am.invalidateAuthToken( - mAccount.type, - cred.getAuthToken() - ); - } else { - am.clearPassword(mAccount); - } - mClient = null; - // when repeated, the creation of a new OwnCloudClient after erasing the saved - // credentials will trigger the login activity - repeat = true; - result = null; - } - } - /** EOF DEPRECATED BLOCK **/ - } while (repeat); - + + final RemoteOperationResult resultToSend = runOperation(); + if (mAccount != null && mContext != null) { - // Save Client Cookies + // Save Client Cookies AccountUtils.saveClient(mClient, mAccount, mContext); } - - final RemoteOperationResult resultToSend = result; + if (mListenerHandler != null && mListener != null) { - mListenerHandler.post(new Runnable() { + mListenerHandler.post(new Runnable() { @Override public void run() { mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); } }); - } - else if(mListener != null) { + } else if (mListener != null) { mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); } } + /** + * Run operation for asynchronous or synchronous 'execute' method. + * + * Considers and performs silent refresh of account credentials if possible, and if + * {@link RemoteOperation#setSilentRefreshOfAccountCredentials(boolean)} was called with + * parameter 'true' before the execution. + * + * @return Remote operation result + */ + private RemoteOperationResult runOperation() { + + RemoteOperationResult result; + + try { + grantOwnCloudClient(); + result = run(mClient); + + } catch (AccountsException | IOException e) { + Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); + result = new RemoteOperationResult(e); + } + + return result; + } + + + private void grantOwnCloudClient() throws + AccountUtils.AccountNotFoundException, OperationCanceledException, AuthenticatorException, IOException { + if (mClient == null) { + if (mAccount != null && mContext != null) { + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); + mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, mContext); + + } else { + throw new IllegalStateException("Trying to run a remote operation " + + "asynchronously with no client and no chance to create one (no account)"); + } + } + } /** * Returns the current client instance to access the remote server. - * - * @return Current client instance to access the remote server. + * + * @return Current client instance to access the remote server. */ public final OwnCloudClient getClient() { return mClient; } -} +} \ No newline at end of file diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java index ea60f379..e8699e7d 100644 --- a/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java +++ b/src/com/owncloud/android/lib/common/operations/RemoteOperationResult.java @@ -129,7 +129,7 @@ public class RemoteOperationResult implements Serializable { private Exception mException = null; private ResultCode mCode = ResultCode.UNKNOWN_ERROR; private String mRedirectedLocation; - private String mAuthenticate; + private ArrayList mAuthenticate = new ArrayList<>(); private String mLastPermanentLocation = null; private ArrayList mData; @@ -313,16 +313,13 @@ public class RemoteOperationResult implements Serializable { public RemoteOperationResult(boolean success, int httpCode, String httpPhrase, Header[] httpHeaders) { this(success, httpCode, httpPhrase); if (httpHeaders != null) { - Header current; for (Header httpHeader : httpHeaders) { - current = httpHeader; - if ("location".equals(current.getName().toLowerCase())) { - mRedirectedLocation = current.getValue(); + if ("location".equals(httpHeader.getName().toLowerCase())) { + mRedirectedLocation = httpHeader.getValue(); continue; } - if ("www-authenticate".equals(current.getName().toLowerCase())) { - mAuthenticate = current.getValue(); - break; + if ("www-authenticate".equals(httpHeader.getName().toLowerCase())) { + mAuthenticate.add(httpHeader.getValue().toLowerCase()); } } } @@ -562,7 +559,7 @@ public class RemoteOperationResult implements Serializable { return (mRedirectedLocation != null && !(mRedirectedLocation.toLowerCase().startsWith("https://"))); } - public String getAuthenticateHeader() { + public ArrayList getAuthenticateHeaders() { return mAuthenticate; } diff --git a/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java b/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java index e5036104..63a95c3a 100644 --- a/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java +++ b/src/com/owncloud/android/lib/resources/files/ExistenceCheckRemoteOperation.java @@ -71,19 +71,6 @@ public class ExistenceCheckRemoteOperation extends RemoteOperation { mSuccessIfAbsent = successIfAbsent; } - /** - * Full constructor. Success of the operation will depend upon the value of successIfAbsent. - * - * @param remotePath Path to append to the URL owned by the client instance. - * @param context Android application context. - * @param successIfAbsent When 'true', the operation finishes in success if the path does - * NOT exist in the remote server (HTTP 404). - * @deprecated - */ - public ExistenceCheckRemoteOperation(String remotePath, Context context, boolean successIfAbsent) { - this(remotePath, successIfAbsent); - } - @Override protected RemoteOperationResult run(OwnCloudClient client) { RemoteOperationResult result = null; diff --git a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java index e1cfd9c8..92ccae41 100644 --- a/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java +++ b/src/com/owncloud/android/lib/resources/files/RenameRemoteFileOperation.java @@ -24,6 +24,8 @@ package com.owncloud.android.lib.resources.files; +import android.os.RemoteException; + import java.io.File; import org.apache.jackrabbit.webdav.client.methods.DavMethodBase; @@ -101,8 +103,7 @@ public class RenameRemoteFileOperation extends RemoteOperation { return new RemoteOperationResult(ResultCode.OK); } - // check if a file with the new name already exists - if (client.existsFile(mNewRemotePath)) { + if (targetPathIsUsed(client)) { return new RemoteOperationResult(ResultCode.INVALID_OVERWRITE); } @@ -134,6 +135,18 @@ public class RenameRemoteFileOperation extends RemoteOperation { return result; } + /** + * Checks if a file with the new name already exists. + * + * @return 'True' if the target path is already used by an existing file. + */ + private boolean targetPathIsUsed(OwnCloudClient client) { + ExistenceCheckRemoteOperation existenceCheckRemoteOperation = + new ExistenceCheckRemoteOperation(mNewRemotePath, false); + RemoteOperationResult exists = existenceCheckRemoteOperation.run(client); + return exists.isSuccess(); + } + /** * Move operation */ diff --git a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java index c69d761c..0bd66036 100644 --- a/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java +++ b/src/com/owncloud/android/lib/resources/status/OwnCloudVersion.java @@ -63,14 +63,6 @@ public class OwnCloudVersion implements Comparable { private int mVersion; private boolean mIsValid; - /** - * @deprecated Will be removed in version 1.0 of the library. - */ - private OwnCloudVersion(int version) { - mVersion = version; - mIsValid = true; - } - public OwnCloudVersion(String version) { mVersion = 0; mIsValid = false; diff --git a/src/com/owncloud/android/lib/resources/users/GetRemoteUserQuotaOperation.java b/src/com/owncloud/android/lib/resources/users/GetRemoteUserQuotaOperation.java index b821d3ef..a1e64aca 100644 --- a/src/com/owncloud/android/lib/resources/users/GetRemoteUserQuotaOperation.java +++ b/src/com/owncloud/android/lib/resources/users/GetRemoteUserQuotaOperation.java @@ -28,7 +28,7 @@ package com.owncloud.android.lib.resources.users; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; diff --git a/test_client/res/values/setup.xml b/test_client/res/values/setup.xml index 0f4c5c70..3fc22fe1 100644 --- a/test_client/res/values/setup.xml +++ b/test_client/res/values/setup.xml @@ -25,9 +25,9 @@ - - - - + https://qa.oc.solidgear.es + https://qa2.oc.solidgear.es + android-library-test + letitgo,letitgo,thatperfectappisgone Mozilla/5.0 (Android) ownCloud test project 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 4bc107fe..9b9ecc3d 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 @@ -42,7 +42,7 @@ import android.view.Menu; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/CopyFileTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/CopyFileTest.java index 013c2489..4c9e0d97 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/CopyFileTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/CopyFileTest.java @@ -31,7 +31,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetCapabilitiesTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetCapabilitiesTest.java index 9893ec27..bbdd37d0 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetCapabilitiesTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetCapabilitiesTest.java @@ -38,7 +38,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.status.GetRemoteCapabilitiesOperation; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetShareesTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetShareesTest.java index 2c00d87b..e9d25fd2 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetShareesTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/GetShareesTest.java @@ -37,7 +37,7 @@ import org.json.JSONObject; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.shares.GetRemoteShareesOperation; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java index de17eb36..9ac235ce 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/MoveFileTest.java @@ -35,7 +35,7 @@ import org.apache.commons.httpclient.protocol.ProtocolSocketFactory; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java index 8bc64e3c..8210c3de 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/OwnCloudClientTest.java @@ -43,9 +43,9 @@ import android.test.AndroidTestCase; import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudCredentials; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentials; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.test_project.R; import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; @@ -130,7 +130,7 @@ public class OwnCloudClientTest extends AndroidTestCase { client.setCredentials(credentials); assertEquals("Basic credentials not set", credentials, client.getCredentials()); - credentials = OwnCloudCredentialsFactory.newBearerCredentials("bearerToken"); + credentials = OwnCloudCredentialsFactory.newBearerCredentials("user", "bearerToken"); client.setCredentials(credentials); assertEquals("Bearer credentials not set", credentials, client.getCredentials()); @@ -294,7 +294,7 @@ public class OwnCloudClientTest extends AndroidTestCase { public void testGetWebdavUri() { OwnCloudClient client = new OwnCloudClient(mServerUri, NetworkUtils.getMultiThreadedConnManager()); - client.setCredentials(OwnCloudCredentialsFactory.newBearerCredentials("fakeToken")); + client.setCredentials(OwnCloudCredentialsFactory.newBearerCredentials("user", "fakeToken")); Uri webdavUri = client.getWebdavUri(); assertTrue("WebDAV URI does not point to the right entry point", webdavUri.getPath().endsWith(AccountUtils.WEBDAV_PATH_4_0)); diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java index 3627e1b5..de70c7ed 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SimpleFactoryManagerTest.java @@ -33,8 +33,8 @@ import android.test.AndroidTestCase; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.SimpleFactoryManager; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.test_project.R; import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java index be6d4738..bcc3fb82 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/SingleSessionManagerTest.java @@ -33,8 +33,8 @@ import android.test.AndroidTestCase; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.SingleSessionManager; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.test_project.R; import com.owncloud.android.lib.test_project.SelfSignedConfidentSslSocketFactory; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePrivateShareTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePrivateShareTest.java index fd8eae04..2f1155dd 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePrivateShareTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePrivateShareTest.java @@ -40,7 +40,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; diff --git a/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePublicShareTest.java b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePublicShareTest.java index bfb100ad..a7792b84 100644 --- a/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePublicShareTest.java +++ b/test_client/tests/src/com/owncloud/android/lib/test_project/test/UpdatePublicShareTest.java @@ -41,7 +41,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudCredentialsFactory; +import com.owncloud.android.lib.common.authentication.OwnCloudCredentialsFactory; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;