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

Invalidate the client token when access token expires and try the last operation [WIP]

This commit is contained in:
davigonz 2017-07-14 13:06:08 +02:00 committed by David A. Velasco
parent fe947194c5
commit a1b1c2f9ed

View File

@ -1,5 +1,5 @@
/* ownCloud Android Library is available under MIT license /* 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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -49,40 +49,57 @@ import java.io.IOException;
* Provides methods to execute the operation both synchronously or asynchronously. * 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 { public abstract class RemoteOperation implements Runnable {
private static final String TAG = RemoteOperation.class.getSimpleName(); 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"; 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"; 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; private Account mAccount = null;
/** Android Application context */ /**
* Android Application context
*/
private Context mContext = null; private Context mContext = null;
/** Object to interact with the remote server */ /**
private OwnCloudClient mClient = 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; * 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; * Handler to the thread where mListener methods will be called
*/
private Handler mListenerHandler = null;
/** Activity */ /**
* Activity
*/
private Activity mCallerActivity; private Activity mCallerActivity;
/** /**
* Abstract method to implement the operation in derived classes. * Abstract method to implement the operation in derived classes.
*/ */
protected abstract RemoteOperationResult run(OwnCloudClient client); protected abstract RemoteOperationResult run(OwnCloudClient client);
/** /**
@ -93,10 +110,10 @@ public abstract class RemoteOperation implements Runnable {
* This method should be used whenever an ownCloud account is available, instead of * This method should be used whenever an ownCloud account is available, instead of
* {@link #execute(OwnCloudClient)}. * {@link #execute(OwnCloudClient)}.
* *
* @param account ownCloud account in remote ownCloud server to reach during the * @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation. * execution of the operation.
* @param context Android context for the component calling the method. * @param context Android context for the component calling the method.
* @return Result of the operation. * @return Result of the operation.
*/ */
public RemoteOperationResult execute(Account account, Context context) { public RemoteOperationResult execute(Account account, Context context) {
if (account == null) if (account == null)
@ -108,9 +125,9 @@ public abstract class RemoteOperation implements Runnable {
mAccount = account; mAccount = account;
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
try { try {
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, mContext); getClientFor(ocAccount, mContext);
} catch (Exception e) { } catch (Exception e) {
Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e); Log_OC.e(TAG, "Error while trying to access to " + mAccount.name, e);
return new RemoteOperationResult(e); return new RemoteOperationResult(e);
@ -119,22 +136,22 @@ public abstract class RemoteOperation implements Runnable {
} }
/** /**
* Synchronously executes the remote operation * Synchronously executes the remote operation
* *
* Do not call this method from the main thread. * Do not call this method from the main thread.
* *
* @param client Client object to reach an ownCloud server during the execution of * @param client Client object to reach an ownCloud server during the execution of
* the operation. * the operation.
* @return Result of the operation. * @return Result of the operation.
*/ */
public RemoteOperationResult execute(OwnCloudClient client) { public RemoteOperationResult execute(OwnCloudClient client) {
if (client == null) if (client == null)
throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " + throw new IllegalArgumentException("Trying to execute a remote operation with a NULL " +
"OwnCloudClient"); "OwnCloudClient");
mClient = client; mClient = client;
return run(client); return run(client);
} }
/** /**
@ -143,19 +160,18 @@ public abstract class RemoteOperation implements Runnable {
* This method should be used whenever an ownCloud account is available, instead of * This method should be used whenever an ownCloud account is available, instead of
* {@link #execute(OwnCloudClient)}. * {@link #execute(OwnCloudClient)}.
* *
* @deprecated This method will be removed in version 1.0. * @param account ownCloud account in remote ownCloud server to reach during
* Use {@link #execute(Account, Context, OnRemoteOperationListener, * the execution of the operation.
* Handler)} instead. * @param context Android context for the component calling the method.
* * @param listener Listener to be notified about the execution of the operation.
* @param account ownCloud account in remote ownCloud server to reach during * @param listenerHandler Handler associated to the thread where the methods of the listener
* the execution of the operation. * objects must be called.
* @param context Android context for the component calling the method. * @return Thread were the remote operation is executed.
* @param listener Listener to be notified about the execution of the operation. * @deprecated This method will be removed in version 1.0.
* @param listenerHandler Handler associated to the thread where the methods of the listener * Use {@link #execute(Account, Context, OnRemoteOperationListener,
* objects must be called. * Handler)} instead.
* @return Thread were the remote operation is executed.
*/ */
@Deprecated @Deprecated
public Thread execute(Account account, Context context, OnRemoteOperationListener listener, public Thread execute(Account account, Context context, OnRemoteOperationListener listener,
Handler listenerHandler, Activity callerActivity) { Handler listenerHandler, Activity callerActivity) {
if (account == null) if (account == null)
@ -168,7 +184,7 @@ public abstract class RemoteOperation implements Runnable {
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
mCallerActivity = callerActivity; mCallerActivity = callerActivity;
mClient = null; // the client instance will be created from mAccount mClient = null; // the client instance will be created from mAccount
// and mContext in the runnerThread to create below // and mContext in the runnerThread to create below
mListener = listener; mListener = listener;
mListenerHandler = listenerHandler; mListenerHandler = listenerHandler;
@ -185,13 +201,13 @@ public abstract class RemoteOperation implements Runnable {
* 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))}. * instead of {@link #execute(OwnCloudClient, OnRemoteOperationListener, Handler))}.
* *
* @param account ownCloud account in remote ownCloud server to reach during the * @param account ownCloud account in remote ownCloud server to reach during the
* execution of the operation. * execution of the operation.
* @param context Android context for the component calling the method. * @param context Android context for the component calling the method.
* @param listener Listener to be notified about the execution of the operation. * @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 * @param listenerHandler Handler associated to the thread where the methods of the listener
* objects must be called. * objects must be called.
* @return Thread were the remote operation is executed. * @return Thread were the remote operation is executed.
*/ */
public Thread execute(Account account, Context context, public Thread execute(Account account, Context context,
OnRemoteOperationListener listener, Handler listenerHandler) { OnRemoteOperationListener listener, Handler listenerHandler) {
@ -206,7 +222,7 @@ public abstract class RemoteOperation implements Runnable {
mContext = context.getApplicationContext(); mContext = context.getApplicationContext();
mCallerActivity = null; mCallerActivity = null;
mClient = null; // the client instance will be created from mClient = null; // the client instance will be created from
// mAccount and mContext in the runnerThread to create below // mAccount and mContext in the runnerThread to create below
mListener = listener; mListener = listener;
@ -218,67 +234,67 @@ public abstract class RemoteOperation implements Runnable {
} }
/** /**
* Asynchronously executes the remote operation * Asynchronously executes the remote operation
* *
* @param client Client object to reach an ownCloud server * @param client Client object to reach an ownCloud server
* during the execution of the operation. * during the execution of the operation.
* @param listener Listener to be notified about 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 * @param listenerHandler Handler, if passed in, associated to the thread where the methods of
* the listener objects must be called. * the listener objects must be called.
* @return Thread were the remote operation is executed. * @return Thread were the remote operation is executed.
*/ */
public Thread execute(OwnCloudClient client, public Thread execute(OwnCloudClient client,
OnRemoteOperationListener listener, Handler listenerHandler) { OnRemoteOperationListener listener, Handler listenerHandler) {
if (client == null) { if (client == null) {
throw new IllegalArgumentException throw new IllegalArgumentException
("Trying to execute a remote operation with a NULL OwnCloudClient"); ("Trying to execute a remote operation with a NULL OwnCloudClient");
} }
mClient = client; mClient = client;
if (listener == null) { if (listener == null) {
throw new IllegalArgumentException throw new IllegalArgumentException
("Trying to execute a remote operation asynchronously " + ("Trying to execute a remote operation asynchronously " +
"without a listener to notiy the result"); "without a listener to notiy the result");
} }
mListener = listener; mListener = listener;
if (listenerHandler != null) { if (listenerHandler != null) {
mListenerHandler = listenerHandler; mListenerHandler = listenerHandler;
} }
Thread runnerThread = new Thread(this); Thread runnerThread = new Thread(this);
runnerThread.start(); runnerThread.start();
return runnerThread; return runnerThread;
} }
/** /**
* Asynchronous execution of the operation * Asynchronous execution of the operation
* started by {@link RemoteOperation#execute(OwnCloudClient, * started by {@link RemoteOperation#execute(OwnCloudClient,
* OnRemoteOperationListener, Handler)}, * OnRemoteOperationListener, Handler)},
* and result posting. * and result posting.
* *
* TODO refactor && clean the code; now it's a mess * TODO refactor && clean the code; now it's a mess
*/ */
@Override @Override
public final void run() { public final void run() {
RemoteOperationResult result = null; RemoteOperationResult result = null;
boolean repeat = false; boolean repeat = false;
do { do {
try{ try {
if (mClient == null) { if (mClient == null) {
if (mAccount != null && mContext != null) { if (mAccount != null && mContext != null) {
/** DEPRECATED BLOCK - will be removed at version 1.0 */ /** DEPRECATED BLOCK - will be removed at version 1.0 */
if (mCallerActivity != null) { if (mCallerActivity != null) {
mClient = OwnCloudClientFactory.createOwnCloudClient( mClient = OwnCloudClientFactory.createOwnCloudClient(
mAccount, mContext, mCallerActivity); mAccount, mContext, mCallerActivity);
} else { } else {
/** EOF DEPRECATED */ /** EOF DEPRECATED */
OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext);
mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). mClient = OwnCloudClientManagerFactory.getDefaultSingleton().
getClientFor(ocAccount, mContext); getClientFor(ocAccount, mContext);
} }
} else { } else {
@ -302,30 +318,69 @@ public abstract class RemoteOperation implements Runnable {
result = run(mClient); result = run(mClient);
repeat = false; 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 && 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() && !result.isSuccess() &&
ResultCode.UNAUTHORIZED.equals(result.getCode()) ResultCode.UNAUTHORIZED.equals(result.getCode())
) { ) {
/// possible fail due to lack of authorization /// possible fail due to lack of authorization
// in an operation performed in foreground // in an operation performed in foreground
OwnCloudCredentials cred = mClient.getCredentials(); OwnCloudCredentials cred = mClient.getCredentials();
if (cred != null) { if (cred != null) {
/// confirmed : unauthorized operation /// confirmed : unauthorized operation
AccountManager am = AccountManager.get(mContext);
if (cred.authTokenExpires()) { OwnCloudClient client;
am.invalidateAuthToken( OwnCloudAccount ocAccount;
mAccount.type,
cred.getAuthToken() try {
); /// Step 1: Invalidate credentials of current account
} else { ocAccount = new OwnCloudAccount(mAccount, mContext);
am.clearPassword(mAccount); 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; mClient = null;
// when repeated, the creation of a new OwnCloudClient after erasing the saved // when repeated, the creation of a new OwnCloudClient after erasing the saved
// credentials will trigger the login activity // credentials will trigger the login activity
repeat = true; if (isOAuth) {
repeat = true;
}
result = null; result = null;
} }
} }
@ -333,20 +388,19 @@ public abstract class RemoteOperation implements Runnable {
} while (repeat); } while (repeat);
if (mAccount != null && mContext != null) { if (mAccount != null && mContext != null) {
// Save Client Cookies // Save Client Cookies
AccountUtils.saveClient(mClient, mAccount, mContext); AccountUtils.saveClient(mClient, mAccount, mContext);
} }
final RemoteOperationResult resultToSend = result; final RemoteOperationResult resultToSend = result;
if (mListenerHandler != null && mListener != null) { if (mListenerHandler != null && mListener != null) {
mListenerHandler.post(new Runnable() { mListenerHandler.post(new Runnable() {
@Override @Override
public void run() { public void run() {
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
} }
}); });
} } else if (mListener != null) {
else if(mListener != null) {
mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend); mListener.onRemoteOperationFinish(RemoteOperation.this, resultToSend);
} }
} }
@ -355,10 +409,9 @@ public abstract class RemoteOperation implements Runnable {
/** /**
* Returns the current client instance to access the remote server. * 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() { public final OwnCloudClient getClient() {
return mClient; return mClient;
} }
} }