From a1b1c2f9edb83520e596d2e929788f8825d18d52 Mon Sep 17 00:00:00 2001 From: davigonz Date: Fri, 14 Jul 2017 13:06:08 +0200 Subject: [PATCH] Invalidate the client token when access token expires and try the last operation [WIP] --- .../common/operations/RemoteOperation.java | 373 ++++++++++-------- 1 file changed, 213 insertions(+), 160 deletions(-) diff --git a/src/com/owncloud/android/lib/common/operations/RemoteOperation.java b/src/com/owncloud/android/lib/common/operations/RemoteOperation.java index bb8ea421..4e4fb773 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 @@ -45,58 +45,75 @@ 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 */ - 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 */ + /** + * 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; - - /** - * 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) @@ -108,54 +125,53 @@ public abstract class RemoteOperation implements Runnable { mAccount = account; mContext = context.getApplicationContext(); try { - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, mContext); + 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); } - - - /** - * 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); - } - + + /** + * 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 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 + @Deprecated public Thread execute(Account account, Context context, OnRemoteOperationListener listener, Handler listenerHandler, Activity callerActivity) { if (account == null) @@ -168,34 +184,34 @@ public abstract class RemoteOperation implements Runnable { mContext = context.getApplicationContext(); mCallerActivity = callerActivity; 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; - + mListenerHandler = listenerHandler; - + Thread runnerThread = new Thread(this); runnerThread.start(); return runnerThread; } - + /** * 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"); @@ -206,147 +222,185 @@ public abstract class RemoteOperation implements Runnable { mContext = context.getApplicationContext(); mCallerActivity = null; 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; - + 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 (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; - } + } - /** - * 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 - */ + 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 + */ @Override public final void run() { RemoteOperationResult result = null; boolean repeat = false; do { - try{ + try { if (mClient == 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) { mClient = OwnCloudClientFactory.createOwnCloudClient( - mAccount, mContext, mCallerActivity); + mAccount, mContext, mCallerActivity); } else { - /** EOF DEPRECATED */ - OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); + /** EOF DEPRECATED */ + OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, mContext); mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, mContext); + 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 && + + + 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 - AccountManager am = AccountManager.get(mContext); - if (cred.authTokenExpires()) { - am.invalidateAuthToken( - mAccount.type, - cred.getAuthToken() - ); - } else { - am.clearPassword(mAccount); + + 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 - repeat = true; + if (isOAuth) { + repeat = true; + } + result = null; } } /** EOF DEPRECATED BLOCK **/ } while (repeat); - + 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); } } @@ -354,11 +408,10 @@ public abstract class RemoteOperation implements Runnable { /** * 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