1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012  Bartek Przybylski 
   3  *   Copyright (C) 2012-2013 ownCloud Inc. 
   5  *   This program is free software: you can redistribute it and/or modify 
   6  *   it under the terms of the GNU General Public License version 2, 
   7  *   as published by the Free Software Foundation. 
   9  *   This program is distributed in the hope that it will be useful, 
  10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  *   GNU General Public License for more details. 
  14  *   You should have received a copy of the GNU General Public License 
  15  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  19 package com
.owncloud
.android
.authentication
; 
  21 import com
.owncloud
.android
.MainApp
; 
  22 import com
.owncloud
.android
.R
; 
  24 import android
.accounts
.*; 
  25 import android
.content
.Context
; 
  26 import android
.content
.Intent
; 
  27 import android
.os
.Bundle
; 
  28 import android
.os
.Handler
; 
  29 import android
.widget
.Toast
; 
  31 import com
.owncloud
.android
.oc_framework
.accounts
.AccountTypeUtils
; 
  32 import com
.owncloud
.android
.utils
.Log_OC
; 
  36  *  Authenticator for ownCloud accounts. 
  38  *  Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system. 
  40  *  TODO - better separation in operations for OAuth-capable and regular ownCloud accounts. 
  41  *  TODO - review completeness  
  43  * @author David A. Velasco 
  45 public class AccountAuthenticator 
extends AbstractAccountAuthenticator 
{ 
  48      * Is used by android system to assign accounts to authenticators. Should be 
  49      * used by application and all extensions. 
  51     public static final String KEY_AUTH_TOKEN_TYPE 
= "authTokenType"; 
  52     public static final String KEY_REQUIRED_FEATURES 
= "requiredFeatures"; 
  53     public static final String KEY_LOGIN_OPTIONS 
= "loginOptions"; 
  54     public static final String KEY_ACCOUNT 
= "account"; 
  56     private static final String TAG 
= AccountAuthenticator
.class.getSimpleName(); 
  58     private Context mContext
; 
  60     private Handler mHandler
; 
  62     public AccountAuthenticator(Context context
) { 
  65         mHandler 
= new Handler(); 
  72     public Bundle 
addAccount(AccountAuthenticatorResponse response
, 
  73             String accountType
, String authTokenType
, 
  74             String
[] requiredFeatures
, Bundle options
) 
  75             throws NetworkErrorException 
{ 
  76         Log_OC
.i(TAG
, "Adding account with type " + accountType
 
  77                 + " and auth token " + authTokenType
); 
  79         final Bundle bundle 
= new Bundle(); 
  81         AccountManager accountManager 
= AccountManager
.get(mContext
); 
  82         Account
[] accounts 
= accountManager
.getAccountsByType(MainApp
.getAccountType()); 
  84         if (mContext
.getResources().getBoolean(R
.bool
.multiaccount_support
) || accounts
.length 
< 1) { 
  86                 validateAccountType(accountType
); 
  87             } catch (AuthenticatorException e
) { 
  88                 Log_OC
.e(TAG
, "Failed to validate account type " + accountType 
+ ": " 
  91                 return e
.getFailureBundle(); 
  94             final Intent intent 
= new Intent(mContext
, AuthenticatorActivity
.class); 
  95             intent
.putExtra(AccountManager
.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
, response
); 
  96             intent
.putExtra(KEY_AUTH_TOKEN_TYPE
, authTokenType
); 
  97             intent
.putExtra(KEY_REQUIRED_FEATURES
, requiredFeatures
); 
  98             intent
.putExtra(KEY_LOGIN_OPTIONS
, options
); 
  99             intent
.putExtra(AuthenticatorActivity
.EXTRA_ACTION
, AuthenticatorActivity
.ACTION_CREATE
); 
 101             setIntentFlags(intent
); 
 103             bundle
.putParcelable(AccountManager
.KEY_INTENT
, intent
); 
 108             bundle
.putInt(AccountManager
.KEY_ERROR_CODE
, AccountManager
.ERROR_CODE_UNSUPPORTED_OPERATION
); 
 109             final String message 
= String
.format(mContext
.getString(R
.string
.auth_unsupported_multiaccount
), mContext
.getString(R
.string
.app_name
));  
 110             bundle
.putString(AccountManager
.KEY_ERROR_MESSAGE
, message
); 
 112             mHandler
.post(new Runnable() { 
 116                     Toast
.makeText(mContext
, message
, Toast
.LENGTH_SHORT
).show(); 
 129     public Bundle 
confirmCredentials(AccountAuthenticatorResponse response
, 
 130             Account account
, Bundle options
) throws NetworkErrorException 
{ 
 132             validateAccountType(account
.type
); 
 133         } catch (AuthenticatorException e
) { 
 134             Log_OC
.e(TAG
, "Failed to validate account type " + account
.type 
+ ": " 
 137             return e
.getFailureBundle(); 
 139         Intent intent 
= new Intent(mContext
, AuthenticatorActivity
.class); 
 140         intent
.putExtra(AccountManager
.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
, 
 142         intent
.putExtra(KEY_ACCOUNT
, account
); 
 143         intent
.putExtra(KEY_LOGIN_OPTIONS
, options
); 
 145         setIntentFlags(intent
); 
 147         Bundle resultBundle 
= new Bundle(); 
 148         resultBundle
.putParcelable(AccountManager
.KEY_INTENT
, intent
); 
 153     public Bundle 
editProperties(AccountAuthenticatorResponse response
, 
 154             String accountType
) { 
 162     public Bundle 
getAuthToken(AccountAuthenticatorResponse response
, 
 163             Account account
, String authTokenType
, Bundle options
) 
 164             throws NetworkErrorException 
{ 
 165         /// validate parameters 
 167             validateAccountType(account
.type
); 
 168             validateAuthTokenType(authTokenType
); 
 169         } catch (AuthenticatorException e
) { 
 170             Log_OC
.e(TAG
, "Failed to validate account type " + account
.type 
+ ": " 
 173             return e
.getFailureBundle(); 
 176         /// check if required token is stored 
 177         final AccountManager am 
= AccountManager
.get(mContext
); 
 179         if (authTokenType
.equals(AccountTypeUtils
.getAuthTokenTypePass(MainApp
.getAccountType()))) { 
 180             accessToken 
= am
.getPassword(account
); 
 182             accessToken 
= am
.peekAuthToken(account
, authTokenType
); 
 184         if (accessToken 
!= null
) { 
 185             final Bundle result 
= new Bundle(); 
 186             result
.putString(AccountManager
.KEY_ACCOUNT_NAME
, account
.name
); 
 187             result
.putString(AccountManager
.KEY_ACCOUNT_TYPE
, MainApp
.getAccountType()); 
 188             result
.putString(AccountManager
.KEY_AUTHTOKEN
, accessToken
); 
 192         /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account 
 193         final Intent intent 
= new Intent(mContext
, AuthenticatorActivity
.class); 
 194         intent
.putExtra(AccountManager
.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
, response
); 
 195         intent
.putExtra(KEY_AUTH_TOKEN_TYPE
, authTokenType
); 
 196         intent
.putExtra(KEY_LOGIN_OPTIONS
, options
); 
 197         intent
.putExtra(AuthenticatorActivity
.EXTRA_ACCOUNT
, account
); 
 198         intent
.putExtra(AuthenticatorActivity
.EXTRA_ENFORCED_UPDATE
, true
); 
 199         intent
.putExtra(AuthenticatorActivity
.EXTRA_ACTION
, AuthenticatorActivity
.ACTION_UPDATE_TOKEN
); 
 202         final Bundle bundle 
= new Bundle(); 
 203         bundle
.putParcelable(AccountManager
.KEY_INTENT
, intent
); 
 208     public String 
getAuthTokenLabel(String authTokenType
) { 
 213     public Bundle 
hasFeatures(AccountAuthenticatorResponse response
, 
 214             Account account
, String
[] features
) throws NetworkErrorException 
{ 
 215         final Bundle result 
= new Bundle(); 
 216         result
.putBoolean(AccountManager
.KEY_BOOLEAN_RESULT
, true
); 
 221     public Bundle 
updateCredentials(AccountAuthenticatorResponse response
, 
 222             Account account
, String authTokenType
, Bundle options
) 
 223             throws NetworkErrorException 
{ 
 224         final Intent intent 
= new Intent(mContext
, AuthenticatorActivity
.class); 
 225         intent
.putExtra(AccountManager
.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE
, 
 227         intent
.putExtra(KEY_ACCOUNT
, account
); 
 228         intent
.putExtra(KEY_AUTH_TOKEN_TYPE
, authTokenType
); 
 229         intent
.putExtra(KEY_LOGIN_OPTIONS
, options
); 
 230         setIntentFlags(intent
); 
 232         final Bundle bundle 
= new Bundle(); 
 233         bundle
.putParcelable(AccountManager
.KEY_INTENT
, intent
); 
 238     public Bundle 
getAccountRemovalAllowed( 
 239             AccountAuthenticatorResponse response
, Account account
) 
 240             throws NetworkErrorException 
{ 
 241         return super.getAccountRemovalAllowed(response
, account
); 
 244     private void setIntentFlags(Intent intent
) { 
 245         intent
.addFlags(Intent
.FLAG_ACTIVITY_NEW_TASK
); 
 246         intent
.addFlags(Intent
.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
); 
 247         intent
.addFlags(Intent
.FLAG_FROM_BACKGROUND
); 
 250     private void validateAccountType(String type
) 
 251             throws UnsupportedAccountTypeException 
{ 
 252         if (!type
.equals(MainApp
.getAccountType())) { 
 253             throw new UnsupportedAccountTypeException(); 
 257     private void validateAuthTokenType(String authTokenType
) 
 258             throws UnsupportedAuthTokenTypeException 
{ 
 259         if (!authTokenType
.equals(MainApp
.getAuthTokenType()) && 
 260             !authTokenType
.equals(AccountTypeUtils
.getAuthTokenTypePass(MainApp
.getAccountType())) && 
 261             !authTokenType
.equals(AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType())) && 
 262             !authTokenType
.equals(AccountTypeUtils
.getAuthTokenTypeRefreshToken(MainApp
.getAccountType())) && 
 263             !authTokenType
.equals(AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()))) { 
 264             throw new UnsupportedAuthTokenTypeException(); 
 268     public static class AuthenticatorException 
extends Exception 
{ 
 269         private static final long serialVersionUID 
= 1L; 
 270         private Bundle mFailureBundle
; 
 272         public AuthenticatorException(int code
, String errorMsg
) { 
 273             mFailureBundle 
= new Bundle(); 
 274             mFailureBundle
.putInt(AccountManager
.KEY_ERROR_CODE
, code
); 
 276                     .putString(AccountManager
.KEY_ERROR_MESSAGE
, errorMsg
); 
 279         public Bundle 
getFailureBundle() { 
 280             return mFailureBundle
; 
 284     public static class UnsupportedAccountTypeException 
extends 
 285             AuthenticatorException 
{ 
 286         private static final long serialVersionUID 
= 1L; 
 288         public UnsupportedAccountTypeException() { 
 289             super(AccountManager
.ERROR_CODE_UNSUPPORTED_OPERATION
, 
 290                     "Unsupported account type"); 
 294     public static class UnsupportedAuthTokenTypeException 
extends 
 295             AuthenticatorException 
{ 
 296         private static final long serialVersionUID 
= 1L; 
 298         public UnsupportedAuthTokenTypeException() { 
 299             super(AccountManager
.ERROR_CODE_UNSUPPORTED_OPERATION
, 
 300                     "Unsupported auth token type"); 
 304     public static class UnsupportedFeaturesException 
extends 
 305             AuthenticatorException 
{ 
 306         public static final long serialVersionUID 
= 1L; 
 308         public UnsupportedFeaturesException() { 
 309             super(AccountManager
.ERROR_CODE_UNSUPPORTED_OPERATION
, 
 310                     "Unsupported features"); 
 314     public static class AccessDeniedException 
extends AuthenticatorException 
{ 
 315         public AccessDeniedException(int code
, String errorMsg
) { 
 316             super(AccountManager
.ERROR_CODE_INVALID_RESPONSE
, "Access Denied"); 
 319         private static final long serialVersionUID 
= 1L;