|
|
|
@@ -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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldInvalidateAccountCredentials(result)) {
|
|
|
|
|
boolean invalidated = invalidateAccountCredentials();
|
|
|
|
|
if (invalidated &&
|
|
|
|
|
mClient.getCredentials().authTokenCanBeRefreshed()) {
|
|
|
|
|
mClient = null;
|
|
|
|
|
|
|
|
|
|
// when repeated, the creation of a new OwnCloudClient after erasing the saved
|
|
|
|
|
// credentials will trigger the login activity
|
|
|
|
|
if (isOAuth) {
|
|
|
|
|
repeat = true;
|
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/** 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.
|
|
|
|
|