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