From a4386bb4fe50e9d97f5c34cc88cd0ecbd106bcd1 Mon Sep 17 00:00:00 2001
From: "David A. Velasco" <dvelasco@owncloud.com>
Date: Wed, 2 Aug 2017 18:43:07 +0200
Subject: [PATCH] Allow client objects to decide if RemoteOperations should
 silently refresh access token when expired

---
 .../android/lib/common/OwnCloudAccount.java   |   2 +
 .../android/lib/common/OwnCloudClient.java    |   4 +-
 .../lib/common/OwnCloudClientFactory.java     |   1 +
 .../lib/common/SingleSessionManager.java      |   1 +
 .../lib/common/accounts/AccountUtils.java     |  73 ++++++++++++-
 .../OwnCloudBasicCredentials.java             |   4 +-
 .../OwnCloudBearerCredentials.java            |   7 +-
 .../OwnCloudCredentials.java                  |   4 +-
 .../OwnCloudCredentialsFactory.java           |   4 +-
 .../OwnCloudSamlSsoCredentials.java           |   4 +-
 .../oauth}/BearerAuthScheme.java              |   4 +-
 .../oauth}/BearerCredentials.java             |   2 +-
 .../oauth/OAuth2ClientConfiguration.java      |   2 +-
 .../authentication/oauth/OAuth2Constants.java |   2 +-
 .../oauth/OAuth2GetAccessTokenOperation.java  |   6 +-
 ...Auth2GetRefreshedAccessTokenOperation.java |   6 +-
 .../authentication/oauth/OAuth2GrantType.java |   2 +-
 .../authentication/oauth/OAuth2Provider.java  |   2 +-
 .../oauth/OAuth2ProvidersRegistry.java        |   2 +-
 .../oauth/OAuth2QueryParser.java              |   2 +-
 .../oauth/OAuth2RequestBuilder.java           |   2 +-
 .../oauth/OwnCloudOAuth2Provider.java         |   2 +-
 .../oauth/OwnCloudOAuth2RequestBuilder.java   |   2 +-
 .../common/operations/RemoteOperation.java    | 101 ++++++++++--------
 .../users/GetRemoteUserQuotaOperation.java    |   2 +-
 25 files changed, 170 insertions(+), 73 deletions(-)
 rename src/com/owncloud/android/lib/common/{ => authentication}/OwnCloudBasicCredentials.java (96%)
 rename src/com/owncloud/android/lib/common/{ => authentication}/OwnCloudBearerCredentials.java (91%)
 rename src/com/owncloud/android/lib/common/{ => authentication}/OwnCloudCredentials.java (92%)
 rename src/com/owncloud/android/lib/common/{ => authentication}/OwnCloudCredentialsFactory.java (96%)
 rename src/com/owncloud/android/lib/common/{ => authentication}/OwnCloudSamlSsoCredentials.java (96%)
 rename src/com/owncloud/android/lib/common/{network => authentication/oauth}/BearerAuthScheme.java (98%)
 rename src/com/owncloud/android/lib/common/{network => authentication/oauth}/BearerCredentials.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2ClientConfiguration.java (96%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2Constants.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2GetAccessTokenOperation.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2GrantType.java (95%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2Provider.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2ProvidersRegistry.java (98%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2QueryParser.java (97%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OAuth2RequestBuilder.java (96%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OwnCloudOAuth2Provider.java (98%)
 rename src/com/owncloud/android/lib/common/{network => }/authentication/oauth/OwnCloudOAuth2RequestBuilder.java (98%)

diff --git a/src/com/owncloud/android/lib/common/OwnCloudAccount.java b/src/com/owncloud/android/lib/common/OwnCloudAccount.java
index af440e35..d6534e96 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;
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClient.java b/src/com/owncloud/android/lib/common/OwnCloudClient.java
index dc3ea126..378a864a 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudClient.java
+++ b/src/com/owncloud/android/lib/common/OwnCloudClient.java
@@ -47,7 +47,9 @@ import org.apache.commons.httpclient.params.HttpParams;
 
 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;
diff --git a/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java b/src/com/owncloud/android/lib/common/OwnCloudClientFactory.java
index 6109324e..c94c5c8a 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;
 
diff --git a/src/com/owncloud/android/lib/common/SingleSessionManager.java b/src/com/owncloud/android/lib/common/SingleSessionManager.java
index 589b33fd..fbc08374 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;
 
 /**
diff --git a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
index e77a868d..cfb03187 100644
--- a/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
+++ b/src/com/owncloud/android/lib/common/accounts/AccountUtils.java
@@ -37,9 +37,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;
 
@@ -280,6 +283,72 @@ public class AccountUtils {
         }
     }
 
+    /**
+     * Determines if credentials should be invalidated for the given account, according the to the result
+     * of an operation just run through the given client.
+     *
+     * @param result            Result of the last remote operation ran through 'client' below.
+     * @param client            {@link OwnCloudClient} that run last remote operation, resulting in 'result' above.
+     * @param account           {@link Account} storing credentials used to run the last operation.
+     * @param context           Android context, needed to access {@link AccountManager}; method will return false
+     *                          if NULL, since invalidation of credentials is just not possible if no context is
+     *                          available.
+     * @return                  'True' if credentials should and might be invalidated, 'false' if shouldn't or
+     *                          cannot be invalidated with the given arguments.
+     */
+    public static boolean shouldInvalidateAccountCredentials(
+        RemoteOperationResult result,
+        OwnCloudClient client,
+        Account account,
+        Context context) {
+
+        boolean should = RemoteOperationResult.ResultCode.UNAUTHORIZED.equals(result.getCode());
+            // invalid credentials
+
+        should &= (client.getCredentials() != null &&         // real credentials
+            !(client.getCredentials() instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
+
+        should &= (account != null && context != null);   // have all the needed to effectively invalidate
+
+        return should;
+    }
+
+    /**
+     * Invalidates credentials stored for the given account in the system  {@link AccountManager} and in
+     * current {@link OwnCloudClientManagerFactory#getDefaultSingleton()} instance.
+     *
+     * {@link AccountUtils#shouldInvalidateAccountCredentials(RemoteOperationResult, OwnCloudClient, Account, Context)}
+     * should be called first.
+     *
+     * @param client            {@link OwnCloudClient} that run last remote operation.
+     * @param account           {@link Account} storing credentials to invalidate.
+     * @param context           Android context, needed to access {@link AccountManager}.
+     *
+     * @return                  'True' if invalidation was successful, 'false' otherwise.
+     */
+    public static boolean invalidateAccountCredentials(
+        OwnCloudClient client,
+        Account account,
+        Context context
+    ) {
+        try {
+            OwnCloudAccount ocAccount = new OwnCloudAccount(account, context);
+            OwnCloudClientManagerFactory.getDefaultSingleton().
+                removeClientFor(ocAccount);    // to prevent nobody else is provided this client
+            AccountManager am = AccountManager.get(context);
+            am.invalidateAuthToken(
+                account.type,
+                client.getCredentials().getAuthToken()
+            );
+            am.clearPassword(account); // being strict, only needed for Basic Auth credentials
+            return true;
+
+        } catch (AccountUtils.AccountNotFoundException e) {
+            Log_OC.e(TAG, "Account was deleted from AccountManager, cannot invalidate its token", e);
+            return false;
+        }
+    }
+
     public static class AccountNotFoundException extends AccountsException {
 
         /**
diff --git a/src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudBasicCredentials.java
similarity index 96%
rename from src/com/owncloud/android/lib/common/OwnCloudBasicCredentials.java
rename to src/com/owncloud/android/lib/common/authentication/OwnCloudBasicCredentials.java
index da2f383c..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;
diff --git a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java
similarity index 91%
rename from src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java
rename to src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java
index 66115b6f..968d6703 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudBearerCredentials.java
+++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudBearerCredentials.java
@@ -21,7 +21,7 @@
  *   THE SOFTWARE.
  *
  */
-package com.owncloud.android.lib.common;
+package com.owncloud.android.lib.common.authentication;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -30,8 +30,9 @@ 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.network.BearerAuthScheme;
-import com.owncloud.android.lib.common.network.BearerCredentials;
+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 {
 
diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.java
similarity index 92%
rename from src/com/owncloud/android/lib/common/OwnCloudCredentials.java
rename to src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.java
index 4c2d9979..978dde8b 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudCredentials.java
+++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentials.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 interface OwnCloudCredentials {
 
diff --git a/src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudCredentialsFactory.java
similarity index 96%
rename from src/com/owncloud/android/lib/common/OwnCloudCredentialsFactory.java
rename to src/com/owncloud/android/lib/common/authentication/OwnCloudCredentialsFactory.java
index 65d017b7..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 {
 
diff --git a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java b/src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java
similarity index 96%
rename from src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java
rename to src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java
index 41968bbb..0f475428 100644
--- a/src/com/owncloud/android/lib/common/OwnCloudSamlSsoCredentials.java
+++ b/src/com/owncloud/android/lib/common/authentication/OwnCloudSamlSsoCredentials.java
@@ -21,13 +21,15 @@
  *   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;
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 98%
rename from src/com/owncloud/android/lib/common/network/BearerAuthScheme.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/BearerAuthScheme.java
index c87b19d3..c3991e07 100644
--- a/src/com/owncloud/android/lib/common/network/BearerAuthScheme.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/BearerAuthScheme.java
@@ -22,11 +22,10 @@
  *
  */
 
-package com.owncloud.android.lib.common.network;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import java.util.Map;
 
-import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.httpclient.Credentials;
 import org.apache.commons.httpclient.HttpMethod;
 import org.apache.commons.httpclient.auth.AuthChallengeParser;
@@ -34,7 +33,6 @@ import org.apache.commons.httpclient.auth.AuthScheme;
 import org.apache.commons.httpclient.auth.AuthenticationException;
 import org.apache.commons.httpclient.auth.InvalidCredentialsException;
 import org.apache.commons.httpclient.auth.MalformedChallengeException;
-import org.apache.commons.httpclient.util.EncodingUtil;
 
 import com.owncloud.android.lib.common.utils.Log_OC;
 
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/network/authentication/oauth/OAuth2ClientConfiguration.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java
similarity index 96%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2ClientConfiguration.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java
index 43db97e0..244b5da8 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2ClientConfiguration.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ClientConfiguration.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 public class OAuth2ClientConfiguration {
 
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Constants.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java
similarity index 97%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Constants.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java
index d10c88d6..ad48e5b1 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Constants.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Constants.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 /** 
  * Constant values for OAuth 2 protocol.
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetAccessTokenOperation.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java
similarity index 97%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetAccessTokenOperation.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java
index 23ef9876..2e2611de 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetAccessTokenOperation.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetAccessTokenOperation.java
@@ -24,13 +24,13 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import android.net.Uri;
 
-import com.owncloud.android.lib.common.OwnCloudBasicCredentials;
+import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials;
 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.operations.RemoteOperationResult.ResultCode;
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java
similarity index 97%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java
index 28f072dc..962b3619 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GetRefreshedAccessTokenOperation.java
@@ -19,13 +19,13 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import android.net.Uri;
 
-import com.owncloud.android.lib.common.OwnCloudBasicCredentials;
+import com.owncloud.android.lib.common.authentication.OwnCloudBasicCredentials;
 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.operations.RemoteOperationResult.ResultCode;
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GrantType.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java
similarity index 95%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GrantType.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java
index fdf98bcf..2d146958 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2GrantType.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2GrantType.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 public enum OAuth2GrantType {
     AUTHORIZATION_CODE("authorization_code"),
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Provider.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java
similarity index 97%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Provider.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java
index b750048c..d259d80e 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2Provider.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2Provider.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 public interface OAuth2Provider {
 
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2ProvidersRegistry.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java
similarity index 98%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2ProvidersRegistry.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java
index b827215f..32aac0ad 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2ProvidersRegistry.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2ProvidersRegistry.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 
 import java.util.HashMap;
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2QueryParser.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java
similarity index 97%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2QueryParser.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java
index 98b89fe8..24825d2a 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2QueryParser.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2QueryParser.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import com.owncloud.android.lib.common.utils.Log_OC;
 
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2RequestBuilder.java b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java
similarity index 96%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2RequestBuilder.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java
index bfb4b681..23b3cdb9 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OAuth2RequestBuilder.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OAuth2RequestBuilder.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import com.owncloud.android.lib.common.operations.RemoteOperation;
 
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2Provider.java b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java
similarity index 98%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2Provider.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java
index 75ab493b..63b43b3e 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2Provider.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2Provider.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import com.owncloud.android.lib.common.utils.Log_OC;
 
diff --git a/src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2RequestBuilder.java b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java
similarity index 98%
rename from src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2RequestBuilder.java
rename to src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java
index cfd05e1b..84c1bf85 100644
--- a/src/com/owncloud/android/lib/common/network/authentication/oauth/OwnCloudOAuth2RequestBuilder.java
+++ b/src/com/owncloud/android/lib/common/authentication/oauth/OwnCloudOAuth2RequestBuilder.java
@@ -24,7 +24,7 @@
  *
  */
 
-package com.owncloud.android.lib.common.network.authentication.oauth;
+package com.owncloud.android.lib.common.authentication.oauth;
 
 import android.net.Uri;
 
diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
index 8850348e..23fe299f 100644
--- a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
+++ b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java
@@ -25,7 +25,6 @@
 package com.owncloud.android.lib.common.operations;
 
 import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.accounts.AccountsException;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
@@ -35,9 +34,7 @@ import android.os.Handler;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
-import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
 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;
@@ -90,6 +87,16 @@ public abstract class RemoteOperation implements Runnable {
      */
     private Handler mListenerHandler = null;
 
+    /**
+     * When 'true', the operation tries to silently refresh credentials if fails due to lack of authorization.
+     *
+     * Valid for 'execute methods' receiving an {@link Account} instance as parameter, but not for those
+     * receiving an {@link OwnCloudClient}. This is, valid for:
+     *  -- {@link RemoteOperation#execute(Account, Context)}
+     *  -- {@link RemoteOperation#execute(Account, Context, OnRemoteOperationListener, Handler)}
+     */
+    private boolean mSilentRefreshOfAccountCredentials = false;
+
 
     /**
      * Counter to establish the number of times a failed operation will be repeated due to
@@ -135,7 +142,7 @@ public abstract class RemoteOperation implements Runnable {
             return new RemoteOperationResult(e);
         }
 
-        return runOperationRetryingItIfNeeded();
+        return runOperation();
     }
 
 
@@ -153,8 +160,9 @@ public abstract class RemoteOperation implements Runnable {
             throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
                     "OwnCloudClient");
         mClient = client;
+        mSilentRefreshOfAccountCredentials = false;
 
-        return runOperationRetryingItIfNeeded();
+        return runOperation();
     }
 
 
@@ -225,6 +233,8 @@ public abstract class RemoteOperation implements Runnable {
             mListenerHandler = listenerHandler;
         }
 
+        mSilentRefreshOfAccountCredentials = false;
+
         Thread runnerThread = new Thread(this);
         runnerThread.start();
         return runnerThread;
@@ -240,12 +250,13 @@ public abstract class RemoteOperation implements Runnable {
     @Override
     public final void run() {
 
+        final RemoteOperationResult resultToSend = runOperation();
+
         if (mAccount != null && mContext != null) {
             // Save Client Cookies
             AccountUtils.saveClient(mClient, mAccount, mContext);
         }
 
-        final RemoteOperationResult resultToSend = runOperationRetryingItIfNeeded();;
         if (mListenerHandler != null && mListener != null) {
             mListenerHandler.post(new Runnable() {
                 @Override
@@ -259,12 +270,15 @@ public abstract class RemoteOperation implements Runnable {
     }
 
     /**
-     * Run operation after asynchronous or synchronous executions. If the account credentials are
-     * invalidated, the operation will be retried with new valid credentials
+     * Run operation for asynchronous or synchronous 'execute' method.
      *
-     * @return remote operation result
+     * 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 runOperationRetryingItIfNeeded () {
+    private RemoteOperationResult runOperation() {
 
         RemoteOperationResult result;
         boolean repeat;
@@ -282,8 +296,18 @@ public abstract class RemoteOperation implements Runnable {
                 result = new RemoteOperationResult(e);
             }
 
-            if (shouldInvalidateAccountCredentials(result)) {
-                boolean invalidated = invalidateAccountCredentials();
+            if (mSilentRefreshOfAccountCredentials &&
+                AccountUtils.shouldInvalidateAccountCredentials(
+                    result,
+                    mClient,
+                    mAccount,
+                    mContext)
+                ) {
+                boolean invalidated = AccountUtils.invalidateAccountCredentials(
+                    mClient,
+                    mAccount,
+                    mContext
+                );
                 if (invalidated &&
                         mClient.getCredentials().authTokenCanBeRefreshed() &&
                         repeatCounter < MAX_REPEAT_COUNTER) {
@@ -322,37 +346,6 @@ public abstract class RemoteOperation implements Runnable {
         }
     }
 
-    private boolean shouldInvalidateAccountCredentials(RemoteOperationResult result) {
-
-        boolean should = ResultCode.UNAUTHORIZED.equals(result.getCode());  // invalid credentials
-
-        should &= (mClient.getCredentials() != null &&         // real credentials
-            !(mClient.getCredentials() instanceof OwnCloudCredentialsFactory.OwnCloudAnonymousCredentials));
-
-        should &= (mAccount != null && mContext != null);   // have all the needed to effectively invalidate
-
-        return should;
-    }
-
-    private boolean invalidateAccountCredentials() {
-        try {
-            OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
-            OwnCloudClientManagerFactory.getDefaultSingleton().
-                removeClientFor(ocAccount);    // to prevent nobody else is provided this client
-            AccountManager am = AccountManager.get(mContext);
-            am.invalidateAuthToken(
-                mAccount.type,
-                mClient.getCredentials().getAuthToken()
-            );
-            am.clearPassword(mAccount); // being strict, only needed for Basic Auth credentials
-            return true;
-
-        } catch (AccountUtils.AccountNotFoundException e) {
-            Log_OC.e(TAG, "Account was deleted from AccountManager, cannot invalidate its token", e);
-            return false;
-        }
-    }
-
     /**
      * Returns the current client instance to access the remote server.
      *
@@ -361,4 +354,26 @@ public abstract class RemoteOperation implements Runnable {
     public final OwnCloudClient getClient() {
         return mClient;
     }
+
+
+    /**
+     * Enables or disables silent refresh of credentials, if supported by credentials itself.
+     *
+     * Will have effect if called before:
+     *  -- {@link RemoteOperation#execute(Account, Context)}
+     *  -- {@link RemoteOperation#execute(Account, Context, OnRemoteOperationListener, Handler)}
+     *
+     * Will have NO effect if called before:
+     *  -- {@link RemoteOperation#execute(OwnCloudClient)}
+     *  -- {@link RemoteOperation#execute(OwnCloudClient, OnRemoteOperationListener, Handler)}
+     *
+     * @param silentRefreshOfAccountCredentials     'True' enables silent refresh, 'false' disables it.
+     */
+    public void setSilentRefreshOfAccountCredentials(boolean silentRefreshOfAccountCredentials) {
+        mSilentRefreshOfAccountCredentials = silentRefreshOfAccountCredentials;
+    }
+
+    public boolean getSilentRefreshOfAccountCredentials() {
+        return mSilentRefreshOfAccountCredentials;
+    }
 }
\ No newline at end of file
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;