AccountAuthenticator.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. /**
  2. * ownCloud Android client application
  3. *
  4. * @author David A. Velasco
  5. * Copyright (C) 2012 Bartek Przybylski
  6. * Copyright (C) 2015 ownCloud Inc.
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License version 2,
  10. * as published by the Free Software Foundation.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. package com.owncloud.android.authentication;
  22. import com.owncloud.android.MainApp;
  23. 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;
  30. import com.owncloud.android.lib.common.accounts.AccountTypeUtils;
  31. import com.owncloud.android.lib.common.utils.Log_OC;
  32. /**
  33. * Authenticator for ownCloud accounts.
  34. *
  35. * Controller class accessed from the system AccountManager,
  36. * providing integration of ownCloud accounts with the Android system.
  37. *
  38. * TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
  39. * TODO - review completeness
  40. */
  41. public class AccountAuthenticator extends AbstractAccountAuthenticator {
  42. /**
  43. * Is used by android system to assign accounts to authenticators.
  44. * Should be used by application and all extensions.
  45. */
  46. public static final String KEY_AUTH_TOKEN_TYPE = "authTokenType";
  47. public static final String KEY_REQUIRED_FEATURES = "requiredFeatures";
  48. public static final String KEY_LOGIN_OPTIONS = "loginOptions";
  49. public static final String KEY_ACCOUNT = "account";
  50. private static final String TAG = AccountAuthenticator.class.getSimpleName();
  51. private Context mContext;
  52. private Handler mHandler;
  53. public AccountAuthenticator(Context context) {
  54. super(context);
  55. mContext = context;
  56. mHandler = new Handler();
  57. }
  58. /**
  59. * {@inheritDoc}
  60. */
  61. @Override
  62. public Bundle addAccount(AccountAuthenticatorResponse response,
  63. String accountType, String authTokenType,
  64. String[] requiredFeatures, Bundle options)
  65. throws NetworkErrorException {
  66. Log_OC.i(TAG, "Adding account with type " + accountType + " and auth token " + authTokenType);
  67. final Bundle bundle = new Bundle();
  68. AccountManager accountManager = AccountManager.get(mContext);
  69. Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
  70. if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
  71. try {
  72. validateAccountType(accountType);
  73. } catch (AuthenticatorException e) {
  74. Log_OC.e(TAG, "Failed to validate account type " + accountType + ": "
  75. + e.getMessage(), e);
  76. return e.getFailureBundle();
  77. }
  78. final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
  79. intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
  80. intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
  81. intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);
  82. intent.putExtra(KEY_LOGIN_OPTIONS, options);
  83. intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
  84. setIntentFlags(intent);
  85. bundle.putParcelable(AccountManager.KEY_INTENT, intent);
  86. } else {
  87. // Return an error
  88. bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
  89. final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name));
  90. bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
  91. mHandler.post(new Runnable() {
  92. @Override
  93. public void run() {
  94. Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
  95. }
  96. });
  97. }
  98. return bundle;
  99. }
  100. /**
  101. * {@inheritDoc}
  102. */
  103. @Override
  104. public Bundle confirmCredentials(AccountAuthenticatorResponse response,
  105. Account account, Bundle options) throws NetworkErrorException {
  106. try {
  107. validateAccountType(account.type);
  108. } catch (AuthenticatorException e) {
  109. Log_OC.e(TAG, "Failed to validate account type " + account.type + ": " + e.getMessage(), e);
  110. return e.getFailureBundle();
  111. }
  112. Intent intent = new Intent(mContext, AuthenticatorActivity.class);
  113. intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
  114. response);
  115. intent.putExtra(KEY_ACCOUNT, account);
  116. intent.putExtra(KEY_LOGIN_OPTIONS, options);
  117. setIntentFlags(intent);
  118. Bundle resultBundle = new Bundle();
  119. resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);
  120. return resultBundle;
  121. }
  122. @Override
  123. public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
  124. return null;
  125. }
  126. /**
  127. * {@inheritDoc}
  128. */
  129. @Override
  130. public Bundle getAuthToken(AccountAuthenticatorResponse response,
  131. Account account, String authTokenType, Bundle options)
  132. throws NetworkErrorException {
  133. /// validate parameters
  134. try {
  135. validateAccountType(account.type);
  136. validateAuthTokenType(authTokenType);
  137. } catch (AuthenticatorException e) {
  138. Log_OC.e(TAG, "Failed to validate account type " + account.type + ": " + e.getMessage(), e);
  139. return e.getFailureBundle();
  140. }
  141. /// check if required token is stored
  142. final AccountManager am = AccountManager.get(mContext);
  143. String accessToken;
  144. if (authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType()))) {
  145. accessToken = am.getPassword(account);
  146. } else {
  147. accessToken = am.peekAuthToken(account, authTokenType);
  148. }
  149. if (accessToken != null) {
  150. final Bundle result = new Bundle();
  151. result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
  152. result.putString(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());
  153. result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
  154. return result;
  155. }
  156. /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
  157. final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
  158. intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
  159. intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
  160. intent.putExtra(KEY_LOGIN_OPTIONS, options);
  161. intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
  162. intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN);
  163. final Bundle bundle = new Bundle();
  164. bundle.putParcelable(AccountManager.KEY_INTENT, intent);
  165. return bundle;
  166. }
  167. @Override
  168. public String getAuthTokenLabel(String authTokenType) {
  169. return null;
  170. }
  171. @Override
  172. public Bundle hasFeatures(AccountAuthenticatorResponse response,
  173. Account account, String[] features) throws NetworkErrorException {
  174. final Bundle result = new Bundle();
  175. result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
  176. return result;
  177. }
  178. @Override
  179. public Bundle updateCredentials(AccountAuthenticatorResponse response,
  180. Account account, String authTokenType, Bundle options)
  181. throws NetworkErrorException {
  182. final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
  183. intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
  184. intent.putExtra(KEY_ACCOUNT, account);
  185. intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
  186. intent.putExtra(KEY_LOGIN_OPTIONS, options);
  187. setIntentFlags(intent);
  188. final Bundle bundle = new Bundle();
  189. bundle.putParcelable(AccountManager.KEY_INTENT, intent);
  190. return bundle;
  191. }
  192. @Override
  193. public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account)
  194. throws NetworkErrorException {
  195. return super.getAccountRemovalAllowed(response, account);
  196. }
  197. private void setIntentFlags(Intent intent) {
  198. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  199. intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
  200. intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
  201. }
  202. private void validateAccountType(String type) throws UnsupportedAccountTypeException {
  203. if (!type.equals(MainApp.getAccountType())) {
  204. throw new UnsupportedAccountTypeException();
  205. }
  206. }
  207. private void validateAuthTokenType(String authTokenType) throws UnsupportedAuthTokenTypeException {
  208. if (!authTokenType.equals(MainApp.getAuthTokenType()) &&
  209. !authTokenType.equals(AccountTypeUtils.getAuthTokenTypePass(MainApp.getAccountType())) &&
  210. !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType())) &&
  211. !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeRefreshToken(MainApp.getAccountType())) &&
  212. !authTokenType.equals(AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType()))) {
  213. throw new UnsupportedAuthTokenTypeException();
  214. }
  215. }
  216. public static class AuthenticatorException extends Exception {
  217. private static final long serialVersionUID = 1L;
  218. private Bundle mFailureBundle;
  219. public AuthenticatorException(int code, String errorMsg) {
  220. mFailureBundle = new Bundle();
  221. mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);
  222. mFailureBundle.putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
  223. }
  224. public Bundle getFailureBundle() {
  225. return mFailureBundle;
  226. }
  227. }
  228. public static class UnsupportedAccountTypeException extends AuthenticatorException {
  229. private static final long serialVersionUID = 1L;
  230. public UnsupportedAccountTypeException() {
  231. super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
  232. "Unsupported account type");
  233. }
  234. }
  235. public static class UnsupportedAuthTokenTypeException extends AuthenticatorException {
  236. private static final long serialVersionUID = 1L;
  237. public UnsupportedAuthTokenTypeException() {
  238. super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
  239. "Unsupported auth token type");
  240. }
  241. }
  242. }