mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-13 19:29:06 +01:00
Merge branch '1341-account-manager-refactoring' into 'master'
Refactor authentication and account management code Closes #1341 See merge request briar/briar!866
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
package org.briarproject.bramble.account;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
|
||||
import org.briarproject.bramble.api.account.AccountManager;
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.util.IoUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
class AndroidAccountManager extends AccountManagerImpl
|
||||
implements AccountManager {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidAccountManager.class.getName());
|
||||
|
||||
private static final String PREF_DB_KEY = "key";
|
||||
|
||||
protected final Context appContext;
|
||||
private final SharedPreferences prefs;
|
||||
|
||||
@Inject
|
||||
AndroidAccountManager(DatabaseConfig databaseConfig,
|
||||
CryptoComponent crypto, IdentityManager identityManager,
|
||||
SharedPreferences prefs, Application app) {
|
||||
super(databaseConfig, crypto, identityManager);
|
||||
this.prefs = prefs;
|
||||
appContext = app.getApplicationContext();
|
||||
}
|
||||
|
||||
// Locking: stateChangeLock
|
||||
@Override
|
||||
@Nullable
|
||||
protected String loadEncryptedDatabaseKey() {
|
||||
String key = getDatabaseKeyFromPreferences();
|
||||
if (key == null) key = super.loadEncryptedDatabaseKey();
|
||||
else migrateDatabaseKeyToFile(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
// Locking: stateChangeLock
|
||||
@Nullable
|
||||
private String getDatabaseKeyFromPreferences() {
|
||||
String key = prefs.getString(PREF_DB_KEY, null);
|
||||
if (key == null) LOG.info("No database key in preferences");
|
||||
else LOG.info("Found database key in preferences");
|
||||
return key;
|
||||
}
|
||||
|
||||
// Locking: stateChangeLock
|
||||
private void migrateDatabaseKeyToFile(String key) {
|
||||
if (storeEncryptedDatabaseKey(key)) {
|
||||
if (prefs.edit().remove(PREF_DB_KEY).commit())
|
||||
LOG.info("Database key migrated to file");
|
||||
else LOG.warning("Database key not removed from preferences");
|
||||
} else {
|
||||
LOG.warning("Database key not migrated to file");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAccount() {
|
||||
synchronized (stateChangeLock) {
|
||||
super.deleteAccount();
|
||||
SharedPreferences defaultPrefs = getDefaultSharedPreferences();
|
||||
deleteAppData(prefs, defaultPrefs);
|
||||
}
|
||||
}
|
||||
|
||||
// Package access for testing
|
||||
SharedPreferences getDefaultSharedPreferences() {
|
||||
return PreferenceManager.getDefaultSharedPreferences(appContext);
|
||||
}
|
||||
|
||||
// Locking: stateChangeLock
|
||||
private void deleteAppData(SharedPreferences... clear) {
|
||||
// Clear and commit shared preferences
|
||||
for (SharedPreferences prefs : clear) {
|
||||
if (!prefs.edit().clear().commit())
|
||||
LOG.warning("Could not clear shared preferences");
|
||||
}
|
||||
// Delete files, except lib and shared_prefs directories
|
||||
File dataDir = new File(appContext.getApplicationInfo().dataDir);
|
||||
File[] children = dataDir.listFiles();
|
||||
if (children == null) {
|
||||
LOG.warning("Could not list files in app data dir");
|
||||
} else {
|
||||
for (File child : children) {
|
||||
String name = child.getName();
|
||||
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
||||
IoUtils.deleteFileOrDir(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||
if (!new File(dataDir, "cache").mkdir())
|
||||
LOG.warning("Could not recreate cache dir");
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
// TODO: Create a module for this so it doesn't have to be public
|
||||
|
||||
public interface CircumventionProvider {
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,6 +16,8 @@ import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
// TODO: Create a module for this so it doesn't need to be public
|
||||
|
||||
public class CircumventionProviderImpl implements CircumventionProvider {
|
||||
|
||||
private final static String BRIDGE_FILE_NAME = "bridges";
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
@@ -58,30 +57,6 @@ public class AndroidUtils {
|
||||
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
|
||||
}
|
||||
|
||||
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
|
||||
// Clear and commit shared preferences
|
||||
for (SharedPreferences prefs : clear) {
|
||||
if (!prefs.edit().clear().commit())
|
||||
LOG.warning("Could not clear shared preferences");
|
||||
}
|
||||
// Delete files, except lib and shared_prefs directories
|
||||
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
||||
File[] children = dataDir.listFiles();
|
||||
if (children == null) {
|
||||
LOG.warning("Could not list files in app data dir");
|
||||
} else {
|
||||
for (File child : children) {
|
||||
String name = child.getName();
|
||||
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
||||
IoUtils.deleteFileOrDir(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Recreate the cache dir as some OpenGL drivers expect it to exist
|
||||
if (!new File(dataDir, "cache").mkdir())
|
||||
LOG.warning("Could not recreate cache dir");
|
||||
}
|
||||
|
||||
public static File getReportDir(Context ctx) {
|
||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
package org.briarproject.bramble.account;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
|
||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
|
||||
public class AndroidAccountManagerTest extends BrambleMockTestCase {
|
||||
|
||||
private final SharedPreferences prefs =
|
||||
context.mock(SharedPreferences.class, "prefs");
|
||||
private final SharedPreferences defaultPrefs =
|
||||
context.mock(SharedPreferences.class, "defaultPrefs");
|
||||
private final DatabaseConfig databaseConfig =
|
||||
context.mock(DatabaseConfig.class);
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
private final IdentityManager identityManager =
|
||||
context.mock(IdentityManager.class);
|
||||
private final SharedPreferences.Editor
|
||||
editor = context.mock(SharedPreferences.Editor.class);
|
||||
private final Application app;
|
||||
private final ApplicationInfo applicationInfo;
|
||||
|
||||
private final String encryptedKeyHex = toHexString(getRandomBytes(123));
|
||||
private final File testDir = getTestDirectory();
|
||||
private final File keyDir = new File(testDir, "key");
|
||||
private final File keyFile = new File(keyDir, "db.key");
|
||||
private final File keyBackupFile = new File(keyDir, "db.key.bak");
|
||||
private final File dbDir = new File(testDir, "db");
|
||||
|
||||
private AndroidAccountManager accountManager;
|
||||
|
||||
public AndroidAccountManagerTest() {
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
app = context.mock(Application.class);
|
||||
applicationInfo = new ApplicationInfo();
|
||||
applicationInfo.dataDir = testDir.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
context.checking(new Expectations() {{
|
||||
allowing(databaseConfig).getDatabaseDirectory();
|
||||
will(returnValue(dbDir));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
allowing(app).getApplicationContext();
|
||||
will(returnValue(app));
|
||||
}});
|
||||
accountManager = new AndroidAccountManager(databaseConfig, crypto,
|
||||
identityManager, prefs, app) {
|
||||
@Override
|
||||
SharedPreferences getDefaultSharedPreferences() {
|
||||
return defaultPrefs;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbKeyIsMigratedFromPreferencesToFile() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).getString("key", null);
|
||||
will(returnValue(encryptedKeyHex));
|
||||
oneOf(prefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).remove("key");
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
assertEquals(encryptedKeyHex,
|
||||
accountManager.loadEncryptedDatabaseKey());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteAccountClearsSharedPrefsAndDeletesFiles()
|
||||
throws Exception {
|
||||
// Directories 'lib' and 'shared_prefs' should be spared
|
||||
File libDir = new File(testDir, "lib");
|
||||
File libFile = new File(libDir, "file");
|
||||
File sharedPrefsDir = new File(testDir, "shared_prefs");
|
||||
File sharedPrefsFile = new File(sharedPrefsDir, "file");
|
||||
// Directory 'cache' should be emptied
|
||||
File cacheDir = new File(testDir, "cache");
|
||||
File cacheFile = new File(cacheDir, "file");
|
||||
// Other directories should be deleted
|
||||
File potatoDir = new File(testDir, ".potato");
|
||||
File potatoFile = new File(potatoDir, "file");
|
||||
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).clear();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
will(returnValue(true));
|
||||
oneOf(defaultPrefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).clear();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
will(returnValue(true));
|
||||
oneOf(app).getApplicationInfo();
|
||||
will(returnValue(applicationInfo));
|
||||
}});
|
||||
|
||||
assertTrue(dbDir.mkdirs());
|
||||
assertTrue(keyDir.mkdirs());
|
||||
assertTrue(libDir.mkdirs());
|
||||
assertTrue(libFile.createNewFile());
|
||||
assertTrue(sharedPrefsDir.mkdirs());
|
||||
assertTrue(sharedPrefsFile.createNewFile());
|
||||
assertTrue(cacheDir.mkdirs());
|
||||
assertTrue(cacheFile.createNewFile());
|
||||
assertTrue(potatoDir.mkdirs());
|
||||
assertTrue(potatoFile.createNewFile());
|
||||
|
||||
accountManager.deleteAccount();
|
||||
|
||||
assertFalse(dbDir.exists());
|
||||
assertFalse(keyDir.exists());
|
||||
assertTrue(libDir.exists());
|
||||
assertTrue(libFile.exists());
|
||||
assertTrue(sharedPrefsDir.exists());
|
||||
assertTrue(sharedPrefsFile.exists());
|
||||
assertTrue(cacheDir.exists());
|
||||
assertFalse(cacheFile.exists());
|
||||
assertFalse(potatoDir.exists());
|
||||
assertFalse(potatoFile.exists());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user