Bug 714657 - Purge existing preferences when (re-)adding an Android Sync account. r=liuche

This commit is contained in:
Nick Alexander 2012-06-12 12:12:42 -07:00
parent 38854e0a87
commit a6c7f112d8
6 changed files with 71 additions and 48 deletions

View File

@ -176,22 +176,61 @@ public class Utils {
return (long)(decimal * 1000);
}
public static byte[] sha1(String utf8)
protected static byte[] sha1(final String utf8)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
return sha1.digest(utf8.getBytes("UTF-8"));
}
public static String sha1Base32(String utf8)
protected static String sha1Base32(final String utf8)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
return new Base32().encodeAsString(sha1(utf8)).toLowerCase(Locale.US);
}
public static String getPrefsPath(String username, String serverURL)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
return "sync.prefs." + sha1Base32(serverURL + ":" + username);
/**
* If we encounter characters not allowed by the API (as found for
* instance in an email address), hash the value.
* @param account
* An account string.
* @return
* An acceptable string.
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static String usernameFromAccount(final String account) throws NoSuchAlgorithmException, UnsupportedEncodingException {
if (account == null || account.equals("")) {
throw new IllegalArgumentException("No account name provided.");
}
if (account.matches("^[A-Za-z0-9._-]+$")) {
return account.toLowerCase(Locale.US);
}
return sha1Base32(account.toLowerCase(Locale.US));
}
/**
* Get shared preferences path for a Sync account.
*
* @param username the Sync account name, optionally encoded with <code>Utils.usernameFromAccount</code>.
* @param serverURL the Sync account server URL.
* @return the path.
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public static String getPrefsPath(String username, String serverURL)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
return "sync.prefs." + sha1Base32(serverURL + ":" + usernameFromAccount(username));
}
/**
* Get shared preferences for a Sync account.
*
* @param context Android <code>Context</code>.
* @param username the Sync account name, optionally encoded with <code>Utils.usernameFromAccount</code>.
* @param serverURL the Sync account server URL.
* @return a <code>SharedPreferences</code> instance.
* @throws NoSuchAlgorithmException
* @throws UnsupportedEncodingException
*/
public static SharedPreferences getSharedPreferences(Context context, String username, String serverURL) throws NoSuchAlgorithmException, UnsupportedEncodingException {
String prefsPath = getPrefsPath(username, serverURL);
Logger.debug(LOG_TAG, "Shared preferences: " + prefsPath);

View File

@ -5,16 +5,15 @@
package org.mozilla.gecko.sync.crypto;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.sync.Utils;
import java.security.InvalidKeyException;
import java.util.Arrays;
import java.util.Locale;
public class KeyBundle {
private static final String KEY_ALGORITHM_SPEC = "AES";
@ -28,29 +27,6 @@ public class KeyBundle {
private static final byte[] ENCR_INPUT_BYTES = {1};
private static final byte[] HMAC_INPUT_BYTES = {2};
/**
* If we encounter characters not allowed by the API (as found for
* instance in an email address), hash the value.
* @param account
* An account string.
* @return
* An acceptable string.
* @throws UnsupportedEncodingException
* @throws NoSuchAlgorithmException
*/
public static String usernameFromAccount(String account) throws NoSuchAlgorithmException, UnsupportedEncodingException {
if (account == null || account.equals("")) {
throw new IllegalArgumentException("No account name provided.");
}
if (account.matches("^[A-Za-z0-9._-]+$")) {
return account.toLowerCase(Locale.US);
}
return Utils.sha1Base32(account.toLowerCase(Locale.US));
}
// If we encounter characters not allowed by the API (as found for
// instance in an email address), hash the value.
/*
* Mozilla's use of HKDF for getting keys from the Sync Key string.
*
@ -67,7 +43,7 @@ public class KeyBundle {
}
// Hash appropriately.
try {
username = usernameFromAccount(username);
username = Utils.usernameFromAccount(username);
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException("Invalid username.");
} catch (UnsupportedEncodingException e) {

View File

@ -214,9 +214,13 @@ public class SyncAccounts {
// TODO: for each, also add to res/xml to make visible in account settings
Logger.debug(LOG_TAG, "Finished setting syncables.");
// TODO: correctly implement Sync Options.
Logger.info(LOG_TAG, "Clearing preferences for this account.");
// Purging global prefs assumes we have only a single Sync account at one time.
// TODO: Bug 761682: don't do anything with global prefs here.
Logger.info(LOG_TAG, "Clearing global prefs.");
SyncAdapter.purgeGlobalPrefs(context);
try {
Logger.info(LOG_TAG, "Clearing preferences path " + Utils.getPrefsPath(username, serverURL) + " for this account.");
SharedPreferences.Editor editor = Utils.getSharedPreferences(context, username, serverURL).edit().clear();
if (syncAccount.clusterURL != null) {
editor.putString(SyncConfiguration.PREF_CLUSTER_URL, syncAccount.clusterURL);

View File

@ -8,7 +8,7 @@ import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.activities.SetupSyncActivity;
import android.accounts.AbstractAccountAuthenticator;
@ -118,7 +118,7 @@ public class SyncAuthenticatorService extends Service {
// Username after hashing.
try {
String username = KeyBundle.usernameFromAccount(account.name);
String username = Utils.usernameFromAccount(account.name);
Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username);
Logger.info(LOG_TAG, "Setting username. Null? " + (username == null));
result.putString(Constants.OPTION_USERNAME, username);

View File

@ -9,7 +9,7 @@ import java.util.Queue;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.ThreadPool;
import org.mozilla.gecko.sync.crypto.KeyBundle;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.sync.setup.activities.AccountActivity;
import android.util.Log;
@ -52,7 +52,7 @@ public class AccountAuthenticator {
// Calculate and save username hash.
try {
username = KeyBundle.usernameFromAccount(account);
username = Utils.usernameFromAccount(account);
} catch (Exception e) {
abort(AuthenticationResult.FAILURE_OTHER, e);
return;

View File

@ -68,25 +68,29 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
mAccountManager = AccountManager.get(context);
}
private SharedPreferences getGlobalPrefs() {
return mContext.getSharedPreferences("sync.prefs.global", SHARED_PREFERENCES_MODE);
public static SharedPreferences getGlobalPrefs(Context context) {
return context.getSharedPreferences("sync.prefs.global", SHARED_PREFERENCES_MODE);
}
public static void purgeGlobalPrefs(Context context) {
getGlobalPrefs(context).edit().clear().commit();
}
/**
* Backoff.
*/
public synchronized long getEarliestNextSync() {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
return sharedPreferences.getLong(PREFS_EARLIEST_NEXT_SYNC, 0);
}
public synchronized void setEarliestNextSync(long next) {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
Editor edit = sharedPreferences.edit();
edit.putLong(PREFS_EARLIEST_NEXT_SYNC, next);
edit.commit();
}
public synchronized void extendEarliestNextSync(long next) {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
if (sharedPreferences.getLong(PREFS_EARLIEST_NEXT_SYNC, 0) >= next) {
return;
}
@ -96,17 +100,17 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
}
public synchronized boolean getShouldInvalidateAuthToken() {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
return sharedPreferences.getBoolean(PREFS_INVALIDATE_AUTH_TOKEN, false);
}
public synchronized void clearShouldInvalidateAuthToken() {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
Editor edit = sharedPreferences.edit();
edit.remove(PREFS_INVALIDATE_AUTH_TOKEN);
edit.commit();
}
public synchronized void setShouldInvalidateAuthToken() {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
Editor edit = sharedPreferences.edit();
edit.putBoolean(PREFS_INVALIDATE_AUTH_TOKEN, true);
edit.commit();
@ -515,12 +519,12 @@ public class SyncAdapter extends AbstractThreadedSyncAdapter implements GlobalSe
}
public synchronized boolean getClusterURLIsStale() {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
return sharedPreferences.getBoolean(PREFS_CLUSTER_URL_IS_STALE, false);
}
public synchronized void setClusterURLIsStale(boolean clusterURLIsStale) {
SharedPreferences sharedPreferences = getGlobalPrefs();
SharedPreferences sharedPreferences = getGlobalPrefs(mContext);
Editor edit = sharedPreferences.edit();
edit.putBoolean(PREFS_CLUSTER_URL_IS_STALE, clusterURLIsStale);
edit.commit();