mirror of
				https://github.com/owncloud/android-library.git
				synced 2025-10-31 02:17:41 +00:00 
			
		
		
		
	Invalidate stored auth token directly from RemoteOperation when request results UNAUTHORIZED
This commit is contained in:
		
							parent
							
								
									0dc859c071
								
							
						
					
					
						commit
						a06af810d0
					
				| @ -81,4 +81,9 @@ public class OwnCloudBasicCredentials implements OwnCloudCredentials { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean authTokenCanBeRefreshed() { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -76,4 +76,9 @@ public class OwnCloudBearerCredentials implements OwnCloudCredentials { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean authTokenCanBeRefreshed() { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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(); | ||||
| 	 | ||||
| 	public String getAuthToken(); | ||||
| 	 | ||||
| 	public boolean authTokenExpires(); | ||||
| 	 | ||||
|     String getUsername(); | ||||
| 
 | ||||
|     String getAuthToken(); | ||||
| 
 | ||||
|     boolean authTokenExpires(); | ||||
| 
 | ||||
|     boolean authTokenCanBeRefreshed(); | ||||
| } | ||||
|  | ||||
| @ -76,6 +76,11 @@ public class OwnCloudCredentialsFactory { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public boolean authTokenCanBeRefreshed() { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public String getUsername() { | ||||
|             // no user name | ||||
|  | ||||
| @ -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 getAuthToken() { | ||||
| 		return mSessionCookie; | ||||
| 	} | ||||
|     @Override | ||||
|     public String getUsername() { | ||||
|         // not relevant for authentication, but relevant for informational purposes | ||||
|         return mUsername; | ||||
|     } | ||||
| 
 | ||||
| 	@Override | ||||
| 	public boolean authTokenExpires() { | ||||
| 		return true; | ||||
| 	} | ||||
|     @Override | ||||
|     public String getAuthToken() { | ||||
|         return mSessionCookie; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean authTokenExpires() { | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public boolean authTokenCanBeRefreshed() { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user