1
0
mirror of https://github.com/owncloud/android-library.git synced 2025-06-07 16:06:08 +00:00

Invalidate stored auth token directly from RemoteOperation when request results UNAUTHORIZED

This commit is contained in:
David A. Velasco 2017-07-25 09:35:16 +02:00
parent 0dc859c071
commit a06af810d0
6 changed files with 125 additions and 186 deletions

View File

@ -81,4 +81,9 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials {
return false;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
}

View File

@ -76,4 +76,9 @@ public class OwnCloudBearerCredentials implements OwnCloudCredentials {
return true;
}
@Override
public boolean authTokenCanBeRefreshed() {
return true;
}
}

View File

@ -26,12 +26,13 @@ package com.owncloud.android.lib.common;
public interface OwnCloudCredentials {
public void applyTo(OwnCloudClient ownCloudClient);
void applyTo(OwnCloudClient ownCloudClient);
public String getUsername();
String getUsername();
public String getAuthToken();
String getAuthToken();
public boolean authTokenExpires();
boolean authTokenExpires();
boolean authTokenCanBeRefreshed();
}

View File

@ -76,6 +76,11 @@ public class OwnCloudCredentialsFactory {
return false;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
@Override
public String getUsername() {
// no user name

View File

@ -30,54 +30,59 @@ import android.net.Uri;
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<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);
}
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 getUsername() {
// not relevant for authentication, but relevant for informational purposes
return mUsername;
}
@Override
public String getAuthToken() {
return mSessionCookie;
}
@Override
public String getAuthToken() {
return mSessionCookie;
}
@Override
public boolean authTokenExpires() {
return true;
}
@Override
public boolean authTokenExpires() {
return true;
}
@Override
public boolean authTokenCanBeRefreshed() {
return false;
}
}

View File

@ -27,6 +27,8 @@ 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;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
@ -36,6 +38,7 @@ 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.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;
@ -90,11 +93,6 @@ public abstract class RemoteOperation implements Runnable {
*/
private Handler mListenerHandler = null;
/**
* Activity
*/
private Activity mCallerActivity;
/**
* Abstract method to implement the operation in derived classes.
@ -154,47 +152,6 @@ public abstract class RemoteOperation implements Runnable {
}
/**
* Asynchronously executes the remote operation
*
* 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.
* @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.
* @deprecated This method will be removed in version 1.0.
* Use {@link #execute(Account, Context, OnRemoteOperationListener,
* Handler)} instead.
*/
@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;
}
/**
* Asynchronously executes the remote operation
*
@ -220,7 +177,6 @@ public abstract class RemoteOperation implements Runnable {
("Trying to execute a remote operation with a NULL Context");
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
@ -275,8 +231,6 @@ public abstract class RemoteOperation implements Runnable {
* started by {@link RemoteOperation#execute(OwnCloudClient,
* OnRemoteOperationListener, Handler)},
* and result posting.
*
* TODO refactor && clean the code; now it's a mess
*/
@Override
public final void run() {
@ -284,107 +238,27 @@ public abstract class RemoteOperation implements Runnable {
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);
}
grantOwnCloudClient();
result = run(mClient);
} 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) {
} catch (AccountsException | IOException 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;
AccountManager mAccountManager = AccountManager.get(mContext);
String isOAuthStr = mAccountManager.getUserData(mAccount,
AccountUtils.Constants.KEY_SUPPORTS_OAUTH2);
Boolean isOAuth = Boolean.valueOf(isOAuthStr);
/** DEPRECATED BLOCK - will be removed at version 1.0 ; don't trust in this code
* to trigger authentication update */
if (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
OwnCloudClient client;
OwnCloudAccount ocAccount;
try {
/// Step 1: Invalidate credentials of current account
ocAccount = new OwnCloudAccount(mAccount, mContext);
client = (OwnCloudClientManagerFactory.getDefaultSingleton().
removeClientFor(ocAccount));
if (client != null) {
AccountManager am = AccountManager.get(mContext);
if (cred.authTokenExpires()) {
am.invalidateAuthToken( // SAML & OAuth
mAccount.type,
cred.getAuthToken()
);
} else { //Basic
am.clearPassword(mAccount);
}
}
/// Step 2: Get new access token using refresh token
// Params needed: clientId, clientSecret (switch credentials), grantype and
// refresh token
String refreshToken = mAccountManager.getUserData(mAccount,
AccountUtils.Constants.KEY_OAUTH2_REFRESH_TOKEN);
// mClient.executeMethod(postMethod);
/// Step 3: Save access token in account manager
} catch (AccountUtils.AccountNotFoundException e) {
e.printStackTrace();
}
mClient = null;
// when repeated, the creation of a new OwnCloudClient after erasing the saved
// credentials will trigger the login activity
if (isOAuth) {
if (shouldInvalidateAccountCredentials(result)) {
boolean invalidated = invalidateAccountCredentials();
if (invalidated &&
mClient.getCredentials().authTokenCanBeRefreshed()) {
mClient = null;
repeat = true;
}
result = null;
// this will result in a new loop, and grantOwnCloudClient() will
// create a new instance for mClient, refreshing the token via the account
// manager
}
// else: operation will finish with ResultCode.UNAUTHORIZED
}
/** EOF DEPRECATED BLOCK **/
} while (repeat);
if (mAccount != null && mContext != null) {
@ -405,6 +279,50 @@ public abstract class RemoteOperation implements Runnable {
}
}
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)");
}
}
}
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.