added "x folders and y files" to end of list
[pub/Android/ownCloud.git] / src / com / owncloud / android / authentication / AccountAuthenticator.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
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.
8 *
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.
13 *
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/>.
16 *
17 */
18
19 package com.owncloud.android.authentication;
20
21 import com.owncloud.android.Log_OC;
22 import com.owncloud.android.MainApp;
23 import com.owncloud.android.R;
24
25 import android.accounts.*;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.os.Bundle;
29 import android.os.Handler;
30 import android.widget.Toast;
31
32
33
34
35 /**
36 * Authenticator for ownCloud accounts.
37 *
38 * Controller class accessed from the system AccountManager, providing integration of ownCloud accounts with the Android system.
39 *
40 * TODO - better separation in operations for OAuth-capable and regular ownCloud accounts.
41 * TODO - review completeness
42 *
43 * @author David A. Velasco
44 */
45 public class AccountAuthenticator extends AbstractAccountAuthenticator {
46
47 /**
48 * Is used by android system to assign accounts to authenticators. Should be
49 * used by application and all extensions.
50 */
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";
55
56 /**
57 * Value under this key should handle path to webdav php script. Will be
58 * removed and usage should be replaced by combining
59 * {@link com.owncloud.android.authentication.AuthenticatorActivity.KEY_OC_BASE_URL} and
60 * {@link com.owncloud.android.utils.OwnCloudVersion}
61 *
62 * @deprecated
63 */
64 public static final String KEY_OC_URL = "oc_url";
65 /**
66 * Version should be 3 numbers separated by dot so it can be parsed by
67 * {@link com.owncloud.android.utils.OwnCloudVersion}
68 */
69 public static final String KEY_OC_VERSION = "oc_version";
70 /**
71 * Base url should point to owncloud installation without trailing / ie:
72 * http://server/path or https://owncloud.server
73 */
74 public static final String KEY_OC_BASE_URL = "oc_base_url";
75 /**
76 * Flag signaling if the ownCloud server can be accessed with OAuth2 access tokens.
77 */
78 public static final String KEY_SUPPORTS_OAUTH2 = "oc_supports_oauth2";
79 /**
80 * Flag signaling if the ownCloud server can be accessed with session cookies from SAML-based web single-sign-on.
81 */
82 public static final String KEY_SUPPORTS_SAML_WEB_SSO = "oc_supports_saml_web_sso";
83
84 private static final String TAG = AccountAuthenticator.class.getSimpleName();
85
86 private Context mContext;
87
88 private Handler mHandler;
89
90 public AccountAuthenticator(Context context) {
91 super(context);
92 mContext = context;
93 mHandler = new Handler();
94 }
95
96 /**
97 * {@inheritDoc}
98 */
99 @Override
100 public Bundle addAccount(AccountAuthenticatorResponse response,
101 String accountType, String authTokenType,
102 String[] requiredFeatures, Bundle options)
103 throws NetworkErrorException {
104 Log_OC.i(TAG, "Adding account with type " + accountType
105 + " and auth token " + authTokenType);
106
107 final Bundle bundle = new Bundle();
108
109 AccountManager accountManager = AccountManager.get(mContext);
110 Account[] accounts = accountManager.getAccountsByType(MainApp.getAccountType());
111
112 if (mContext.getResources().getBoolean(R.bool.multiaccount_support) || accounts.length < 1) {
113 try {
114 validateAccountType(accountType);
115 } catch (AuthenticatorException e) {
116 Log_OC.e(TAG, "Failed to validate account type " + accountType + ": "
117 + e.getMessage());
118 e.printStackTrace();
119 return e.getFailureBundle();
120 }
121
122 final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
123 intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
124 intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
125 intent.putExtra(KEY_REQUIRED_FEATURES, requiredFeatures);
126 intent.putExtra(KEY_LOGIN_OPTIONS, options);
127 intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_CREATE);
128
129 setIntentFlags(intent);
130
131 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
132
133 } else {
134
135 // Return an error
136 bundle.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION);
137 final String message = String.format(mContext.getString(R.string.auth_unsupported_multiaccount), mContext.getString(R.string.app_name));
138 bundle.putString(AccountManager.KEY_ERROR_MESSAGE, message);
139
140 mHandler.post(new Runnable() {
141
142 @Override
143 public void run() {
144 Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
145 }
146 });
147
148 }
149
150 return bundle;
151 }
152
153 /**
154 * {@inheritDoc}
155 */
156 @Override
157 public Bundle confirmCredentials(AccountAuthenticatorResponse response,
158 Account account, Bundle options) throws NetworkErrorException {
159 try {
160 validateAccountType(account.type);
161 } catch (AuthenticatorException e) {
162 Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
163 + e.getMessage());
164 e.printStackTrace();
165 return e.getFailureBundle();
166 }
167 Intent intent = new Intent(mContext, AuthenticatorActivity.class);
168 intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
169 response);
170 intent.putExtra(KEY_ACCOUNT, account);
171 intent.putExtra(KEY_LOGIN_OPTIONS, options);
172
173 setIntentFlags(intent);
174
175 Bundle resultBundle = new Bundle();
176 resultBundle.putParcelable(AccountManager.KEY_INTENT, intent);
177 return resultBundle;
178 }
179
180 @Override
181 public Bundle editProperties(AccountAuthenticatorResponse response,
182 String accountType) {
183 return null;
184 }
185
186 /**
187 * {@inheritDoc}
188 */
189 @Override
190 public Bundle getAuthToken(AccountAuthenticatorResponse response,
191 Account account, String authTokenType, Bundle options)
192 throws NetworkErrorException {
193 /// validate parameters
194 try {
195 validateAccountType(account.type);
196 validateAuthTokenType(authTokenType);
197 } catch (AuthenticatorException e) {
198 Log_OC.e(TAG, "Failed to validate account type " + account.type + ": "
199 + e.getMessage());
200 e.printStackTrace();
201 return e.getFailureBundle();
202 }
203
204 /// check if required token is stored
205 final AccountManager am = AccountManager.get(mContext);
206 String accessToken;
207 if (authTokenType.equals(MainApp.getAuthTokenTypePass())) {
208 accessToken = am.getPassword(account);
209 } else {
210 accessToken = am.peekAuthToken(account, authTokenType);
211 }
212 if (accessToken != null) {
213 final Bundle result = new Bundle();
214 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
215 result.putString(AccountManager.KEY_ACCOUNT_TYPE, MainApp.getAccountType());
216 result.putString(AccountManager.KEY_AUTHTOKEN, accessToken);
217 return result;
218 }
219
220 /// if not stored, return Intent to access the AuthenticatorActivity and UPDATE the token for the account
221 final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
222 intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
223 intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
224 intent.putExtra(KEY_LOGIN_OPTIONS, options);
225 intent.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);
226 intent.putExtra(AuthenticatorActivity.EXTRA_ENFORCED_UPDATE, true);
227 intent.putExtra(AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_TOKEN);
228
229
230 final Bundle bundle = new Bundle();
231 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
232 return bundle;
233 }
234
235 @Override
236 public String getAuthTokenLabel(String authTokenType) {
237 return null;
238 }
239
240 @Override
241 public Bundle hasFeatures(AccountAuthenticatorResponse response,
242 Account account, String[] features) throws NetworkErrorException {
243 final Bundle result = new Bundle();
244 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
245 return result;
246 }
247
248 @Override
249 public Bundle updateCredentials(AccountAuthenticatorResponse response,
250 Account account, String authTokenType, Bundle options)
251 throws NetworkErrorException {
252 final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
253 intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
254 response);
255 intent.putExtra(KEY_ACCOUNT, account);
256 intent.putExtra(KEY_AUTH_TOKEN_TYPE, authTokenType);
257 intent.putExtra(KEY_LOGIN_OPTIONS, options);
258 setIntentFlags(intent);
259
260 final Bundle bundle = new Bundle();
261 bundle.putParcelable(AccountManager.KEY_INTENT, intent);
262 return bundle;
263 }
264
265 @Override
266 public Bundle getAccountRemovalAllowed(
267 AccountAuthenticatorResponse response, Account account)
268 throws NetworkErrorException {
269 return super.getAccountRemovalAllowed(response, account);
270 }
271
272 private void setIntentFlags(Intent intent) {
273 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
274 intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
275 intent.addFlags(Intent.FLAG_FROM_BACKGROUND);
276 }
277
278 private void validateAccountType(String type)
279 throws UnsupportedAccountTypeException {
280 if (!type.equals(MainApp.getAccountType())) {
281 throw new UnsupportedAccountTypeException();
282 }
283 }
284
285 private void validateAuthTokenType(String authTokenType)
286 throws UnsupportedAuthTokenTypeException {
287 if (!authTokenType.equals(MainApp.getAuthTokenType()) &&
288 !authTokenType.equals(MainApp.getAuthTokenTypePass()) &&
289 !authTokenType.equals(MainApp.getAuthTokenTypeAccessToken()) &&
290 !authTokenType.equals(MainApp.getAuthTokenTypeRefreshToken()) &&
291 !authTokenType.equals(MainApp.getAuthTokenTypeSamlSessionCookie())) {
292 throw new UnsupportedAuthTokenTypeException();
293 }
294 }
295
296 public static class AuthenticatorException extends Exception {
297 private static final long serialVersionUID = 1L;
298 private Bundle mFailureBundle;
299
300 public AuthenticatorException(int code, String errorMsg) {
301 mFailureBundle = new Bundle();
302 mFailureBundle.putInt(AccountManager.KEY_ERROR_CODE, code);
303 mFailureBundle
304 .putString(AccountManager.KEY_ERROR_MESSAGE, errorMsg);
305 }
306
307 public Bundle getFailureBundle() {
308 return mFailureBundle;
309 }
310 }
311
312 public static class UnsupportedAccountTypeException extends
313 AuthenticatorException {
314 private static final long serialVersionUID = 1L;
315
316 public UnsupportedAccountTypeException() {
317 super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
318 "Unsupported account type");
319 }
320 }
321
322 public static class UnsupportedAuthTokenTypeException extends
323 AuthenticatorException {
324 private static final long serialVersionUID = 1L;
325
326 public UnsupportedAuthTokenTypeException() {
327 super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
328 "Unsupported auth token type");
329 }
330 }
331
332 public static class UnsupportedFeaturesException extends
333 AuthenticatorException {
334 public static final long serialVersionUID = 1L;
335
336 public UnsupportedFeaturesException() {
337 super(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION,
338 "Unsupported features");
339 }
340 }
341
342 public static class AccessDeniedException extends AuthenticatorException {
343 public AccessDeniedException(int code, String errorMsg) {
344 super(AccountManager.ERROR_CODE_INVALID_RESPONSE, "Access Denied");
345 }
346
347 private static final long serialVersionUID = 1L;
348
349 }
350 }