mobile/android/base/sync/setup/SyncAuthenticatorService.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.sync.setup;
michael@0 6
michael@0 7 import java.io.UnsupportedEncodingException;
michael@0 8 import java.security.NoSuchAlgorithmException;
michael@0 9
michael@0 10 import org.mozilla.gecko.background.common.log.Logger;
michael@0 11 import org.mozilla.gecko.sync.SyncConstants;
michael@0 12 import org.mozilla.gecko.sync.Utils;
michael@0 13 import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
michael@0 14
michael@0 15 import android.accounts.AbstractAccountAuthenticator;
michael@0 16 import android.accounts.Account;
michael@0 17 import android.accounts.AccountAuthenticatorResponse;
michael@0 18 import android.accounts.AccountManager;
michael@0 19 import android.accounts.NetworkErrorException;
michael@0 20 import android.app.Service;
michael@0 21 import android.content.Context;
michael@0 22 import android.content.Intent;
michael@0 23 import android.os.Bundle;
michael@0 24 import android.os.IBinder;
michael@0 25
michael@0 26 public class SyncAuthenticatorService extends Service {
michael@0 27 private static final String LOG_TAG = "SyncAuthService";
michael@0 28
michael@0 29 private SyncAccountAuthenticator sAccountAuthenticator = null;
michael@0 30
michael@0 31 @Override
michael@0 32 public void onCreate() {
michael@0 33 Logger.debug(LOG_TAG, "onCreate");
michael@0 34 sAccountAuthenticator = getAuthenticator();
michael@0 35 }
michael@0 36
michael@0 37 @Override
michael@0 38 public IBinder onBind(Intent intent) {
michael@0 39 if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
michael@0 40 return getAuthenticator().getIBinder();
michael@0 41 }
michael@0 42 return null;
michael@0 43 }
michael@0 44
michael@0 45 private SyncAccountAuthenticator getAuthenticator() {
michael@0 46 if (sAccountAuthenticator == null) {
michael@0 47 sAccountAuthenticator = new SyncAccountAuthenticator(this);
michael@0 48 }
michael@0 49 return sAccountAuthenticator;
michael@0 50 }
michael@0 51
michael@0 52 /**
michael@0 53 * Generate a "plain" auth token.
michael@0 54 * <p>
michael@0 55 * Android caches only the value of the key
michael@0 56 * <code>AccountManager.KEY_AUTHTOKEN</code>, so if a caller needs the other
michael@0 57 * keys in this bundle, it needs to invalidate the token (so that the bundle
michael@0 58 * is re-generated).
michael@0 59 *
michael@0 60 * @param context
michael@0 61 * Android context.
michael@0 62 * @param account
michael@0 63 * Android account.
michael@0 64 * @return a <code>Bundle</code> instance containing a subset of the following
michael@0 65 * keys: (caller's must check for missing keys)
michael@0 66 * <ul>
michael@0 67 * <li><code>AccountManager.KEY_ACCOUNT_TYPE</code>: the Android
michael@0 68 * Account's type</li>
michael@0 69 *
michael@0 70 * <li><code>AccountManager.KEY_ACCOUNT_NAME</code>: the Android
michael@0 71 * Account's name</li>
michael@0 72 *
michael@0 73 * <li><code>AccountManager.KEY_AUTHTOKEN</code>: the Sync account's
michael@0 74 * password </li>
michael@0 75 *
michael@0 76 * <li><code> Constants.OPTION_USERNAME</code>: the Sync account's
michael@0 77 * hashed username</li>
michael@0 78 *
michael@0 79 * <li><code>Constants.OPTION_SERVER</code>: the Sync account's
michael@0 80 * server</li>
michael@0 81 *
michael@0 82 * <li><code> Constants.OPTION_SYNCKEY</code>: the Sync account's
michael@0 83 * sync key</li>
michael@0 84 *
michael@0 85 * </ul>
michael@0 86 * @throws NetworkErrorException
michael@0 87 */
michael@0 88 public static Bundle getPlainAuthToken(final Context context, final Account account)
michael@0 89 throws NetworkErrorException {
michael@0 90 // Extract the username and password from the Account Manager, and ask
michael@0 91 // the server for an appropriate AuthToken.
michael@0 92 final AccountManager am = AccountManager.get(context);
michael@0 93 final String password = am.getPassword(account);
michael@0 94 if (password == null) {
michael@0 95 Logger.warn(LOG_TAG, "Returning null bundle for getPlainAuthToken since Account password is null.");
michael@0 96 return null;
michael@0 97 }
michael@0 98
michael@0 99 final Bundle result = new Bundle();
michael@0 100
michael@0 101 // This is a Sync account.
michael@0 102 result.putString(AccountManager.KEY_ACCOUNT_TYPE, SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 103
michael@0 104 // Server.
michael@0 105 String serverURL = am.getUserData(account, Constants.OPTION_SERVER);
michael@0 106 result.putString(Constants.OPTION_SERVER, serverURL);
michael@0 107
michael@0 108 // Full username, before hashing.
michael@0 109 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
michael@0 110
michael@0 111 // Username after hashing.
michael@0 112 try {
michael@0 113 String username = Utils.usernameFromAccount(account.name);
michael@0 114 Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username + ".");
michael@0 115 Logger.debug(LOG_TAG, "Setting username. Null? " + (username == null));
michael@0 116 result.putString(Constants.OPTION_USERNAME, username);
michael@0 117 } catch (NoSuchAlgorithmException e) {
michael@0 118 // Do nothing. Calling code must check for missing value.
michael@0 119 Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
michael@0 120 } catch (UnsupportedEncodingException e) {
michael@0 121 // Do nothing. Calling code must check for missing value.
michael@0 122 Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
michael@0 123 }
michael@0 124
michael@0 125 // Sync key.
michael@0 126 final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY);
michael@0 127 Logger.debug(LOG_TAG, "Setting sync key. Null? " + (syncKey == null));
michael@0 128 result.putString(Constants.OPTION_SYNCKEY, syncKey);
michael@0 129
michael@0 130 // Password.
michael@0 131 result.putString(AccountManager.KEY_AUTHTOKEN, password);
michael@0 132 return result;
michael@0 133 }
michael@0 134
michael@0 135 private static class SyncAccountAuthenticator extends AbstractAccountAuthenticator {
michael@0 136 private Context mContext;
michael@0 137 public SyncAccountAuthenticator(Context context) {
michael@0 138 super(context);
michael@0 139 mContext = context;
michael@0 140 }
michael@0 141
michael@0 142 @Override
michael@0 143 public Bundle addAccount(AccountAuthenticatorResponse response,
michael@0 144 String accountType, String authTokenType, String[] requiredFeatures,
michael@0 145 Bundle options) throws NetworkErrorException {
michael@0 146 Logger.debug(LOG_TAG, "addAccount()");
michael@0 147 final Intent intent = new Intent(mContext, SetupSyncActivity.class);
michael@0 148 intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
michael@0 149 response);
michael@0 150 intent.putExtra("accountType", SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 151 intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, true);
michael@0 152
michael@0 153 final Bundle result = new Bundle();
michael@0 154 result.putParcelable(AccountManager.KEY_INTENT, intent);
michael@0 155
michael@0 156 return result;
michael@0 157 }
michael@0 158
michael@0 159 @Override
michael@0 160 public Bundle confirmCredentials(AccountAuthenticatorResponse response,
michael@0 161 Account account,
michael@0 162 Bundle options) throws NetworkErrorException {
michael@0 163 Logger.debug(LOG_TAG, "confirmCredentials()");
michael@0 164 return null;
michael@0 165 }
michael@0 166
michael@0 167 @Override
michael@0 168 public Bundle editProperties(AccountAuthenticatorResponse response,
michael@0 169 String accountType) {
michael@0 170 Logger.debug(LOG_TAG, "editProperties");
michael@0 171 return null;
michael@0 172 }
michael@0 173
michael@0 174 @Override
michael@0 175 public Bundle getAuthToken(AccountAuthenticatorResponse response,
michael@0 176 Account account, String authTokenType, Bundle options)
michael@0 177 throws NetworkErrorException {
michael@0 178 Logger.debug(LOG_TAG, "getAuthToken()");
michael@0 179
michael@0 180 if (Constants.AUTHTOKEN_TYPE_PLAIN.equals(authTokenType)) {
michael@0 181 return getPlainAuthToken(mContext, account);
michael@0 182 }
michael@0 183
michael@0 184 final Bundle result = new Bundle();
michael@0 185 result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
michael@0 186 return result;
michael@0 187 }
michael@0 188
michael@0 189 @Override
michael@0 190 public String getAuthTokenLabel(String authTokenType) {
michael@0 191 Logger.debug(LOG_TAG, "getAuthTokenLabel()");
michael@0 192 return null;
michael@0 193 }
michael@0 194
michael@0 195 @Override
michael@0 196 public Bundle hasFeatures(AccountAuthenticatorResponse response,
michael@0 197 Account account, String[] features) throws NetworkErrorException {
michael@0 198 Logger.debug(LOG_TAG, "hasFeatures()");
michael@0 199 return null;
michael@0 200 }
michael@0 201
michael@0 202 @Override
michael@0 203 public Bundle updateCredentials(AccountAuthenticatorResponse response,
michael@0 204 Account account, String authTokenType, Bundle options)
michael@0 205 throws NetworkErrorException {
michael@0 206 Logger.debug(LOG_TAG, "updateCredentials()");
michael@0 207 return null;
michael@0 208 }
michael@0 209
michael@0 210 /**
michael@0 211 * Bug 769745: persist pickled Sync account settings so that we can unpickle
michael@0 212 * after Fennec is moved to the SD card.
michael@0 213 * <p>
michael@0 214 * This is <b>not</b> called when an Android Account is blown away due to
michael@0 215 * the SD card being unmounted.
michael@0 216 * <p>
michael@0 217 * Broadcasting a Firefox intent to version sharing this Android Account is
michael@0 218 * a terrible hack, but it's better than the catching the generic
michael@0 219 * "accounts changed" broadcast intent and trying to figure out whether our
michael@0 220 * Account disappeared.
michael@0 221 */
michael@0 222 @Override
michael@0 223 public Bundle getAccountRemovalAllowed(final AccountAuthenticatorResponse response, Account account)
michael@0 224 throws NetworkErrorException {
michael@0 225 Bundle result = super.getAccountRemovalAllowed(response, account);
michael@0 226
michael@0 227 if (result == null ||
michael@0 228 !result.containsKey(AccountManager.KEY_BOOLEAN_RESULT) ||
michael@0 229 result.containsKey(AccountManager.KEY_INTENT)) {
michael@0 230 return result;
michael@0 231 }
michael@0 232
michael@0 233 final boolean removalAllowed = result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT);
michael@0 234 if (!removalAllowed) {
michael@0 235 return result;
michael@0 236 }
michael@0 237
michael@0 238 // Bug 790931: Broadcast a message to all Firefox versions sharing this
michael@0 239 // Android Account type telling that this Sync Account has been deleted.
michael@0 240 //
michael@0 241 // We would really prefer to receive Android's
michael@0 242 // LOGIN_ACCOUNTS_CHANGED_ACTION broadcast, but that
michael@0 243 // doesn't include enough information about which Accounts changed to
michael@0 244 // correctly identify whether a Sync account has been removed (when some
michael@0 245 // Firefox versions are installed on the SD card).
michael@0 246 //
michael@0 247 // Broadcast intents protected with permissions are secure, so it's okay
michael@0 248 // to include password and sync key, etc.
michael@0 249 final Intent intent = SyncAccounts.makeSyncAccountDeletedIntent(mContext, AccountManager.get(mContext), account);
michael@0 250 Logger.info(LOG_TAG, "Account named " + account.name + " being removed; " +
michael@0 251 "broadcasting secure intent " + intent.getAction() + ".");
michael@0 252 mContext.sendBroadcast(intent, SyncConstants.PER_ACCOUNT_TYPE_PERMISSION);
michael@0 253
michael@0 254 return result;
michael@0 255 }
michael@0 256 }
michael@0 257 }

mercurial