mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 22:29:53 +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:
1
.idea/runConfigurations/All_tests.xml
generated
1
.idea/runConfigurations/All_tests.xml
generated
@@ -21,6 +21,7 @@
|
|||||||
<method>
|
<method>
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" />
|
||||||
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" />
|
||||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
<option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" />
|
||||||
</method>
|
</method>
|
||||||
|
|||||||
@@ -34,6 +34,14 @@ dependencies {
|
|||||||
|
|
||||||
compileOnly 'javax.annotation:jsr250-api:1.0'
|
compileOnly 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
|
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
testImplementation "org.jmock:jmock:2.8.2"
|
||||||
|
testImplementation "org.jmock:jmock-junit4:2.8.2"
|
||||||
|
testImplementation "org.jmock:jmock-legacy:2.8.2"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-library:1.3"
|
||||||
|
testImplementation "org.hamcrest:hamcrest-core:1.3"
|
||||||
|
|
||||||
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
androidTestImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
androidTestImplementation project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
@@ -43,6 +51,10 @@ dependencies {
|
|||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
|
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
|
||||||
|
'com.android.support.test:monitor:1.0.2:monitor-1.0.2.aar:38ef4fa98a32dc55550ff49bb36a583e178b3a9b830fcb8dcc27bfc4254bc2bc',
|
||||||
|
'com.android.support.test:runner:1.0.2:runner-1.0.2.aar:f04b9ae342975ba1cb3e4a06e13426e3e6b8a73faa45acba604493d83c9a4f00',
|
||||||
|
'com.android.support:support-annotations:27.1.1:support-annotations-27.1.1.jar:3365960206c3d2b09e845f555e7f88f8effc8d2f00b369e66c4be384029299cf',
|
||||||
'com.android.tools.analytics-library:protos:26.1.3:protos-26.1.3.jar:818c9f256f141d9dafec03a1aa2b94d240b2c140acfd7ee31a8b3e6c2b9479e3',
|
'com.android.tools.analytics-library:protos:26.1.3:protos-26.1.3.jar:818c9f256f141d9dafec03a1aa2b94d240b2c140acfd7ee31a8b3e6c2b9479e3',
|
||||||
'com.android.tools.analytics-library:shared:26.1.3:shared-26.1.3.jar:7110706c7ada96c8b6f5ca80c478291bc7899d46277de2c48527e045442401a3',
|
'com.android.tools.analytics-library:shared:26.1.3:shared-26.1.3.jar:7110706c7ada96c8b6f5ca80c478291bc7899d46277de2c48527e045442401a3',
|
||||||
'com.android.tools.analytics-library:tracker:26.1.3:tracker-26.1.3.jar:4155424bf2ce4872da83332579a1707252bc66cbd77c5144fdc4483d0f2e1418',
|
'com.android.tools.analytics-library:tracker:26.1.3:tracker-26.1.3.jar:4155424bf2ce4872da83332579a1707252bc66cbd77c5144fdc4483d0f2e1418',
|
||||||
@@ -91,12 +103,16 @@ dependencyVerification {
|
|||||||
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
|
||||||
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
|
||||||
|
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
|
||||||
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
|
||||||
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
|
||||||
|
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
|
||||||
|
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
|
||||||
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
|
||||||
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
|
||||||
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
|
||||||
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
|
||||||
|
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
|
||||||
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
|
||||||
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
|
||||||
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
|
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
|
||||||
@@ -106,17 +122,25 @@ dependencyVerification {
|
|||||||
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
|
||||||
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
|
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
|
||||||
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
|
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
|
||||||
|
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
|
||||||
|
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
|
||||||
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
|
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
|
||||||
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
|
||||||
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
|
||||||
|
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
|
||||||
|
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
|
||||||
|
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
|
||||||
|
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
|
||||||
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
|
||||||
|
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
|
||||||
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
|
||||||
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
|
||||||
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
|
||||||
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
|
||||||
|
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
|
||||||
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
|
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
import java.util.List;
|
||||||
|
|
||||||
|
// TODO: Create a module for this so it doesn't have to be public
|
||||||
|
|
||||||
public interface CircumventionProvider {
|
public interface CircumventionProvider {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ import java.util.Set;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
// TODO: Create a module for this so it doesn't need to be public
|
||||||
|
|
||||||
public class CircumventionProviderImpl implements CircumventionProvider {
|
public class CircumventionProviderImpl implements CircumventionProvider {
|
||||||
|
|
||||||
private final static String BRIDGE_FILE_NAME = "bridges";
|
private final static String BRIDGE_FILE_NAME = "bridges";
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
|
||||||
@@ -58,30 +57,6 @@ public class AndroidUtils {
|
|||||||
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
|
&& !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) {
|
public static File getReportDir(Context ctx) {
|
||||||
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
package org.briarproject.bramble.api.account;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public interface AccountManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the manager has the database key. This will be false
|
||||||
|
* before {@link #createAccount(String, String)} or {@link #signIn(String)}
|
||||||
|
* has been called, and true after {@link #createAccount(String, String)}
|
||||||
|
* or {@link #signIn(String)} has returned true, until the process exits.
|
||||||
|
*/
|
||||||
|
boolean hasDatabaseKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the database key if the manager has it. This will be null
|
||||||
|
* before {@link #createAccount(String, String)} or {@link #signIn(String)}
|
||||||
|
* has been called, and non-null after
|
||||||
|
* {@link #createAccount(String, String)} or {@link #signIn(String)} has
|
||||||
|
* returned true, until the process exits.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
SecretKey getDatabaseKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the encrypted database key can be loaded from disk, and
|
||||||
|
* the database directory exists and is a directory.
|
||||||
|
*/
|
||||||
|
boolean accountExists();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an identity with the given name and registers it with the
|
||||||
|
* {@link IdentityManager}. Creates a database key, encrypts it with the
|
||||||
|
* given password and stores it on disk.
|
||||||
|
* <p/>
|
||||||
|
* This method does not create the database directory, so
|
||||||
|
* {@link #accountExists()} will continue to return false until the
|
||||||
|
* database directory is created.
|
||||||
|
*/
|
||||||
|
boolean createAccount(String name, String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes all account state from disk. {@link #accountExists()} will
|
||||||
|
* return false after this method returns.
|
||||||
|
*/
|
||||||
|
void deleteAccount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the encrypted database key from disk and decrypts it with the
|
||||||
|
* given password.
|
||||||
|
*
|
||||||
|
* @return true if the database key was successfully loaded and decrypted.
|
||||||
|
*/
|
||||||
|
boolean signIn(String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the encrypted database key from disk, decrypts it with the old
|
||||||
|
* password, encrypts it with the new password, and stores it on disk,
|
||||||
|
* replacing the old key.
|
||||||
|
*
|
||||||
|
* @return true if the database key was successfully loaded, re-encrypted
|
||||||
|
* and stored.
|
||||||
|
*/
|
||||||
|
boolean changePassword(String oldPassword, String newPassword);
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ public interface ContactManager {
|
|||||||
/**
|
/**
|
||||||
* Registers a hook to be called whenever a contact is added or removed.
|
* Registers a hook to be called whenever a contact is added or removed.
|
||||||
* This method should be called before
|
* This method should be called before
|
||||||
* {@link LifecycleManager#startServices(String)}.
|
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerContactHook(ContactHook hook);
|
void registerContactHook(ContactHook hook);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.db;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
@@ -44,7 +45,8 @@ public interface DatabaseComponent {
|
|||||||
* @throws DataTooOldException if the data uses an older schema than the
|
* @throws DataTooOldException if the data uses an older schema than the
|
||||||
* current code and cannot be migrated
|
* current code and cannot be migrated
|
||||||
*/
|
*/
|
||||||
boolean open(@Nullable MigrationListener listener) throws DbException;
|
boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for any open transactions to finish and closes the database.
|
* Waits for any open transactions to finish and closes the database.
|
||||||
@@ -267,7 +269,7 @@ public interface DatabaseComponent {
|
|||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
Collection<MessageId> getMessageIds(Transaction txn, GroupId g)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the IDs of any messages that need to be validated.
|
* Returns the IDs of any messages that need to be validated.
|
||||||
@@ -487,7 +489,7 @@ public interface DatabaseComponent {
|
|||||||
* Removes the given transport keys from the database.
|
* Removes the given transport keys from the database.
|
||||||
*/
|
*/
|
||||||
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the given contact as verified.
|
* Marks the given contact as verified.
|
||||||
@@ -534,7 +536,7 @@ public interface DatabaseComponent {
|
|||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the given transport keys, deleting any keys they have replaced.
|
* Stores the given transport keys, deleting any keys they have replaced.
|
||||||
|
|||||||
@@ -1,30 +1,15 @@
|
|||||||
package org.briarproject.bramble.api.db;
|
package org.briarproject.bramble.api.db;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface DatabaseConfig {
|
public interface DatabaseConfig {
|
||||||
|
|
||||||
boolean databaseExists();
|
|
||||||
|
|
||||||
File getDatabaseDirectory();
|
File getDatabaseDirectory();
|
||||||
|
|
||||||
File getDatabaseKeyDirectory();
|
File getDatabaseKeyDirectory();
|
||||||
|
|
||||||
void setEncryptionKey(SecretKey key);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
SecretKey getEncryptionKey();
|
|
||||||
|
|
||||||
void setLocalAuthorName(String nickname);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getLocalAuthorName();
|
|
||||||
|
|
||||||
long getMaxSize();
|
long getMaxSize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.identity;
|
package org.briarproject.bramble.api.identity;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.Author.Status;
|
||||||
@@ -9,29 +10,40 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
public interface IdentityManager {
|
public interface IdentityManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the local pseudonym.
|
* Creates a local identity with the given name.
|
||||||
*/
|
*/
|
||||||
void registerLocalAuthor(LocalAuthor a) throws DbException;
|
@CryptoExecutor
|
||||||
|
LocalAuthor createLocalAuthor(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the cached main local identity, non-blocking, or loads it from
|
* Registers the given local identity with the manager. The identity is
|
||||||
* the db, blocking
|
* not stored until {@link #storeLocalAuthor()} is called.
|
||||||
|
*/
|
||||||
|
void registerLocalAuthor(LocalAuthor a);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the local identity registered with
|
||||||
|
* {@link #registerLocalAuthor(LocalAuthor)}, if any.
|
||||||
|
*/
|
||||||
|
void storeLocalAuthor() throws DbException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cached local identity or loads it from the database.
|
||||||
*/
|
*/
|
||||||
LocalAuthor getLocalAuthor() throws DbException;
|
LocalAuthor getLocalAuthor() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the cached main local identity, non-blocking, or loads it from
|
* Returns the cached local identity or loads it from the database.
|
||||||
* the db, blocking, within the given Transaction.
|
|
||||||
*/
|
*/
|
||||||
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
|
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the trust-level status of the author
|
* Returns the {@link Status} of the given author.
|
||||||
*/
|
*/
|
||||||
Status getAuthorStatus(AuthorId a) throws DbException;
|
Status getAuthorStatus(AuthorId a) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the trust-level status of the author
|
* Returns the {@link Status} of the given author.
|
||||||
*/
|
*/
|
||||||
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
|
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
package org.briarproject.bramble.api.lifecycle;
|
package org.briarproject.bramble.api.lifecycle;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.Client;
|
import org.briarproject.bramble.api.sync.Client;
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the lifecycle of the app, starting {@link Client Clients}, starting
|
* Manages the lifecycle of the app, starting {@link Client Clients}, starting
|
||||||
* and stopping {@link Service Services}, shutting down
|
* and stopping {@link Service Services}, shutting down
|
||||||
@@ -18,7 +17,7 @@ import javax.annotation.Nullable;
|
|||||||
public interface LifecycleManager {
|
public interface LifecycleManager {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The result of calling {@link #startServices(String)}.
|
* The result of calling {@link #startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
enum StartResult {
|
enum StartResult {
|
||||||
ALREADY_RUNNING,
|
ALREADY_RUNNING,
|
||||||
@@ -44,28 +43,27 @@ public interface LifecycleManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a {@link Service} to be started and stopped. This method
|
* Registers a {@link Service} to be started and stopped. This method
|
||||||
* should be called before {@link #startServices(String)}.
|
* should be called before {@link #startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerService(Service s);
|
void registerService(Service s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a {@link Client} to be started. This method should be called
|
* Registers a {@link Client} to be started. This method should be called
|
||||||
* before {@link #startServices(String)}.
|
* before {@link #startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerClient(Client c);
|
void registerClient(Client c);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an {@link ExecutorService} to be shut down. This method
|
* Registers an {@link ExecutorService} to be shut down. This method
|
||||||
* should be called before {@link #startServices(String)}.
|
* should be called before {@link #startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerForShutdown(ExecutorService e);
|
void registerForShutdown(ExecutorService e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens the {@link DatabaseComponent}, optionally creates a local author
|
* Opens the {@link DatabaseComponent} using the given key and starts any
|
||||||
* with the provided nickname, and starts any registered
|
* registered {@link Client Clients} and {@link Service Services}.
|
||||||
* {@link Client Clients} and {@link Service Services}.
|
|
||||||
*/
|
*/
|
||||||
StartResult startServices(@Nullable String nickname);
|
StartResult startServices(SecretKey dbKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stops any registered {@link Service Services}, shuts down any
|
* Stops any registered {@link Service Services}, shuts down any
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.api.sync;
|
package org.briarproject.bramble.api.sync;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Metadata;
|
import org.briarproject.bramble.api.db.Metadata;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
@@ -35,7 +36,8 @@ public interface ValidationManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers the message validator for the given client. This method
|
* Registers the message validator for the given client. This method
|
||||||
* should be called before {@link LifecycleManager#startServices(String)}.
|
* should be called before
|
||||||
|
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerMessageValidator(ClientId c, int majorVersion,
|
void registerMessageValidator(ClientId c, int majorVersion,
|
||||||
MessageValidator v);
|
MessageValidator v);
|
||||||
@@ -44,7 +46,7 @@ public interface ValidationManager {
|
|||||||
* Registers the incoming message hook for the given client. The hook will
|
* Registers the incoming message hook for the given client. The hook will
|
||||||
* be called once for each incoming message that passes validation. This
|
* be called once for each incoming message that passes validation. This
|
||||||
* method should be called before
|
* method should be called before
|
||||||
* {@link LifecycleManager#startServices(String)}.
|
* {@link LifecycleManager#startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerIncomingMessageHook(ClientId c, int majorVersion,
|
void registerIncomingMessageHook(ClientId c, int majorVersion,
|
||||||
IncomingMessageHook hook);
|
IncomingMessageHook hook);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.versioning;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
@@ -25,7 +26,7 @@ public interface ClientVersioningManager {
|
|||||||
/**
|
/**
|
||||||
* Registers a client that will be advertised to contacts. The hook will
|
* Registers a client that will be advertised to contacts. The hook will
|
||||||
* be called when the visibility of the client changes. This method should
|
* be called when the visibility of the client changes. This method should
|
||||||
* be called before {@link LifecycleManager#startServices(String)}.
|
* be called before {@link LifecycleManager#startServices(SecretKey)}.
|
||||||
*/
|
*/
|
||||||
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
|
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
|
||||||
ClientVersioningHook hook);
|
ClientVersioningHook hook);
|
||||||
|
|||||||
@@ -0,0 +1,221 @@
|
|||||||
|
package org.briarproject.bramble.account;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.util.IoUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.fromHexString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
class AccountManagerImpl implements AccountManager {
|
||||||
|
|
||||||
|
private static final Logger LOG =
|
||||||
|
Logger.getLogger(AccountManagerImpl.class.getName());
|
||||||
|
|
||||||
|
private static final String DB_KEY_FILENAME = "db.key";
|
||||||
|
private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
|
||||||
|
|
||||||
|
private final DatabaseConfig databaseConfig;
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final IdentityManager identityManager;
|
||||||
|
private final File dbKeyFile, dbKeyBackupFile;
|
||||||
|
|
||||||
|
final Object stateChangeLock = new Object();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SecretKey databaseKey = null;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountManagerImpl(DatabaseConfig databaseConfig, CryptoComponent crypto,
|
||||||
|
IdentityManager identityManager) {
|
||||||
|
this.databaseConfig = databaseConfig;
|
||||||
|
this.crypto = crypto;
|
||||||
|
this.identityManager = identityManager;
|
||||||
|
File keyDir = databaseConfig.getDatabaseKeyDirectory();
|
||||||
|
dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
|
||||||
|
dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasDatabaseKey() {
|
||||||
|
return databaseKey != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public SecretKey getDatabaseKey() {
|
||||||
|
return databaseKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
@Nullable
|
||||||
|
protected String loadEncryptedDatabaseKey() {
|
||||||
|
String key = readDbKeyFromFile(dbKeyFile);
|
||||||
|
if (key == null) {
|
||||||
|
LOG.info("No database key in primary file");
|
||||||
|
key = readDbKeyFromFile(dbKeyBackupFile);
|
||||||
|
if (key == null) LOG.info("No database key in backup file");
|
||||||
|
else LOG.warning("Found database key in backup file");
|
||||||
|
} else {
|
||||||
|
LOG.info("Found database key in primary file");
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
@Nullable
|
||||||
|
private String readDbKeyFromFile(File f) {
|
||||||
|
if (!f.exists()) {
|
||||||
|
LOG.info("Key file does not exist");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
|
new FileInputStream(f), "UTF-8"));
|
||||||
|
String key = reader.readLine();
|
||||||
|
reader.close();
|
||||||
|
return key;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
protected boolean storeEncryptedDatabaseKey(String hex) {
|
||||||
|
LOG.info("Storing database key in file");
|
||||||
|
// Create the directory if necessary
|
||||||
|
if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
|
||||||
|
LOG.info("Created database key directory");
|
||||||
|
// If only the backup file exists, rename it so we don't overwrite it
|
||||||
|
if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
|
||||||
|
if (dbKeyBackupFile.renameTo(dbKeyFile))
|
||||||
|
LOG.info("Renamed old backup");
|
||||||
|
else LOG.warning("Failed to rename old backup");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// Write to the backup file
|
||||||
|
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||||
|
LOG.info("Stored database key in backup file");
|
||||||
|
// Delete the old primary file, if it exists
|
||||||
|
if (dbKeyFile.exists()) {
|
||||||
|
if (dbKeyFile.delete()) LOG.info("Deleted primary file");
|
||||||
|
else LOG.warning("Failed to delete primary file");
|
||||||
|
}
|
||||||
|
// The backup file becomes the new primary
|
||||||
|
if (dbKeyBackupFile.renameTo(dbKeyFile)) {
|
||||||
|
LOG.info("Renamed backup file to primary");
|
||||||
|
} else {
|
||||||
|
LOG.warning("Failed to rename backup file to primary");
|
||||||
|
return false; // Don't overwrite our only copy
|
||||||
|
}
|
||||||
|
// Write a second copy to the backup file
|
||||||
|
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||||
|
LOG.info("Stored second copy of database key in backup file");
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
logException(LOG, WARNING, e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||||
|
FileOutputStream out = new FileOutputStream(f);
|
||||||
|
out.write(key.getBytes("UTF-8"));
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean accountExists() {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
return loadEncryptedDatabaseKey() != null
|
||||||
|
&& databaseConfig.getDatabaseDirectory().isDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean createAccount(String name, String password) {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
|
||||||
|
identityManager.registerLocalAuthor(localAuthor);
|
||||||
|
SecretKey key = crypto.generateSecretKey();
|
||||||
|
if (!encryptAndStoreDatabaseKey(key, password)) return false;
|
||||||
|
databaseKey = key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
|
||||||
|
byte[] plaintext = key.getBytes();
|
||||||
|
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
|
||||||
|
return storeEncryptedDatabaseKey(toHexString(ciphertext));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteAccount() {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
LOG.info("Deleting account");
|
||||||
|
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
|
||||||
|
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean signIn(String password) {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
SecretKey key = loadAndDecryptDatabaseKey(password);
|
||||||
|
if (key == null) return false;
|
||||||
|
databaseKey = key;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Locking: stateChangeLock
|
||||||
|
@Nullable
|
||||||
|
private SecretKey loadAndDecryptDatabaseKey(String password) {
|
||||||
|
String hex = loadEncryptedDatabaseKey();
|
||||||
|
if (hex == null) {
|
||||||
|
LOG.warning("Failed to load encrypted database key");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
byte[] ciphertext = fromHexString(hex);
|
||||||
|
byte[] plaintext = crypto.decryptWithPassword(ciphertext, password);
|
||||||
|
if (plaintext == null) {
|
||||||
|
LOG.info("Failed to decrypt database key");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SecretKey(plaintext);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean changePassword(String oldPassword, String newPassword) {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
|
||||||
|
return key != null && encryptAndStoreDatabaseKey(key, newPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.account;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class AccountModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
AccountManager provideAccountManager(AccountManagerImpl accountManager) {
|
||||||
|
return accountManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -48,7 +49,8 @@ interface Database<T> {
|
|||||||
* @throws DataTooOldException if the data uses an older schema than the
|
* @throws DataTooOldException if the data uses an older schema than the
|
||||||
* current code and cannot be migrated
|
* current code and cannot be migrated
|
||||||
*/
|
*/
|
||||||
boolean open(@Nullable MigrationListener listener) throws DbException;
|
boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents new transactions from starting, waits for all current
|
* Prevents new transactions from starting, waits for all current
|
||||||
@@ -641,7 +643,7 @@ interface Database<T> {
|
|||||||
* Marks the given transport keys as usable for outgoing streams.
|
* Marks the given transport keys as usable for outgoing streams.
|
||||||
*/
|
*/
|
||||||
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the transmission count and expiry time of the given message
|
* Updates the transmission count and expiry time of the given message
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
|||||||
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
|
||||||
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.ContactExistsException;
|
import org.briarproject.bramble.api.db.ContactExistsException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -103,9 +104,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener)
|
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
boolean reopened = db.open(listener);
|
boolean reopened = db.open(key, listener);
|
||||||
shutdown.addShutdownHook(() -> {
|
shutdown.addShutdownHook(() -> {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class H2Database extends JdbcDatabase {
|
|||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SecretKey key = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
H2Database(DatabaseConfig config, Clock clock) {
|
H2Database(DatabaseConfig config, Clock clock) {
|
||||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||||
@@ -44,11 +47,11 @@ class H2Database extends JdbcDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener)
|
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
boolean reopen = config.databaseExists();
|
this.key = key;
|
||||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
boolean reopen = !config.getDatabaseDirectory().mkdirs();
|
||||||
super.open("org.h2.Driver", reopen, listener);
|
super.open("org.h2.Driver", reopen, key, listener);
|
||||||
return reopen;
|
return reopen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +66,7 @@ class H2Database extends JdbcDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getFreeSpace() throws DbException {
|
public long getFreeSpace() {
|
||||||
File dir = config.getDatabaseDirectory();
|
File dir = config.getDatabaseDirectory();
|
||||||
long maxSize = config.getMaxSize();
|
long maxSize = config.getMaxSize();
|
||||||
long free = dir.getFreeSpace();
|
long free = dir.getFreeSpace();
|
||||||
@@ -88,7 +91,7 @@ class H2Database extends JdbcDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Connection createConnection() throws SQLException {
|
protected Connection createConnection() throws SQLException {
|
||||||
SecretKey key = config.getEncryptionKey();
|
SecretKey key = this.key;
|
||||||
if (key == null) throw new IllegalStateException();
|
if (key == null) throw new IllegalStateException();
|
||||||
Properties props = new Properties();
|
Properties props = new Properties();
|
||||||
props.setProperty("user", "user");
|
props.setProperty("user", "user");
|
||||||
|
|||||||
@@ -33,6 +33,9 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
private final DatabaseConfig config;
|
private final DatabaseConfig config;
|
||||||
private final String url;
|
private final String url;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private volatile SecretKey key = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
|
||||||
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
|
||||||
@@ -46,10 +49,11 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean open(@Nullable MigrationListener listener) throws DbException {
|
public boolean open(SecretKey key, @Nullable MigrationListener listener)
|
||||||
boolean reopen = config.databaseExists();
|
throws DbException {
|
||||||
if (!reopen) config.getDatabaseDirectory().mkdirs();
|
this.key = key;
|
||||||
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener);
|
boolean reopen = !config.getDatabaseDirectory().mkdirs();
|
||||||
|
super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
|
||||||
return reopen;
|
return reopen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +97,7 @@ class HyperSqlDatabase extends JdbcDatabase {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Connection createConnection() throws SQLException {
|
protected Connection createConnection() throws SQLException {
|
||||||
SecretKey key = config.getEncryptionKey();
|
SecretKey key = this.key;
|
||||||
if (key == null) throw new IllegalStateException();
|
if (key == null) throw new IllegalStateException();
|
||||||
String hex = StringUtils.toHexString(key.getBytes());
|
String hex = StringUtils.toHexString(key.getBytes());
|
||||||
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
return DriverManager.getConnection(url + ";crypt_key=" + hex);
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ abstract class JdbcDatabase implements Database<Connection> {
|
|||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void open(String driverClass, boolean reopen,
|
protected void open(String driverClass, boolean reopen, SecretKey key,
|
||||||
@Nullable MigrationListener listener) throws DbException {
|
@Nullable MigrationListener listener) throws DbException {
|
||||||
// Load the JDBC driver
|
// Load the JDBC driver
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package org.briarproject.bramble.identity;
|
package org.briarproject.bramble.identity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.Author.Status;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
@@ -21,6 +24,8 @@ import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
|||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
|
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
|
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
|
|
||||||
@ThreadSafe
|
@ThreadSafe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -30,25 +35,51 @@ class IdentityManagerImpl implements IdentityManager {
|
|||||||
Logger.getLogger(IdentityManagerImpl.class.getName());
|
Logger.getLogger(IdentityManagerImpl.class.getName());
|
||||||
|
|
||||||
private final DatabaseComponent db;
|
private final DatabaseComponent db;
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final AuthorFactory authorFactory;
|
||||||
|
|
||||||
// The local author is immutable so we can cache it
|
// The local author is immutable so we can cache it
|
||||||
@Nullable
|
@Nullable
|
||||||
private volatile LocalAuthor cachedAuthor;
|
private volatile LocalAuthor cachedAuthor;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
IdentityManagerImpl(DatabaseComponent db) {
|
IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto,
|
||||||
|
AuthorFactory authorFactory) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
|
this.crypto = crypto;
|
||||||
|
this.authorFactory = authorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerLocalAuthor(LocalAuthor localAuthor)
|
public LocalAuthor createLocalAuthor(String name) {
|
||||||
throws DbException {
|
long start = now();
|
||||||
|
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
||||||
|
byte[] publicKey = keyPair.getPublic().getEncoded();
|
||||||
|
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
||||||
|
LocalAuthor localAuthor = authorFactory.createLocalAuthor(name,
|
||||||
|
publicKey, privateKey);
|
||||||
|
logDuration(LOG, "Creating local author", start);
|
||||||
|
return localAuthor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerLocalAuthor(LocalAuthor a) {
|
||||||
|
cachedAuthor = a;
|
||||||
|
LOG.info("Local author registered");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void storeLocalAuthor() throws DbException {
|
||||||
|
LocalAuthor cached = cachedAuthor;
|
||||||
|
if (cached == null) {
|
||||||
|
LOG.info("No local author to store");
|
||||||
|
return;
|
||||||
|
}
|
||||||
Transaction txn = db.startTransaction(false);
|
Transaction txn = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, cached);
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
cachedAuthor = localAuthor;
|
LOG.info("Local author stored");
|
||||||
LOG.info("Local author registered");
|
|
||||||
} finally {
|
} finally {
|
||||||
db.endTransaction(txn);
|
db.endTransaction(txn);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.bramble.lifecycle;
|
package org.briarproject.bramble.lifecycle;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.crypto.KeyPair;
|
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
@@ -9,9 +8,7 @@ import org.briarproject.bramble.api.db.DbException;
|
|||||||
import org.briarproject.bramble.api.db.MigrationListener;
|
import org.briarproject.bramble.api.db.MigrationListener;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||||
@@ -26,7 +23,6 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.concurrent.ThreadSafe;
|
import javax.annotation.concurrent.ThreadSafe;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -60,8 +56,6 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
private final List<Service> services;
|
private final List<Service> services;
|
||||||
private final List<Client> clients;
|
private final List<Client> clients;
|
||||||
private final List<ExecutorService> executors;
|
private final List<ExecutorService> executors;
|
||||||
private final CryptoComponent crypto;
|
|
||||||
private final AuthorFactory authorFactory;
|
|
||||||
private final IdentityManager identityManager;
|
private final IdentityManager identityManager;
|
||||||
private final Semaphore startStopSemaphore = new Semaphore(1);
|
private final Semaphore startStopSemaphore = new Semaphore(1);
|
||||||
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
private final CountDownLatch dbLatch = new CountDownLatch(1);
|
||||||
@@ -72,12 +66,9 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
|
||||||
CryptoComponent crypto, AuthorFactory authorFactory,
|
|
||||||
IdentityManager identityManager) {
|
IdentityManager identityManager) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.crypto = crypto;
|
|
||||||
this.authorFactory = authorFactory;
|
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
services = new CopyOnWriteArrayList<>();
|
services = new CopyOnWriteArrayList<>();
|
||||||
clients = new CopyOnWriteArrayList<>();
|
clients = new CopyOnWriteArrayList<>();
|
||||||
@@ -104,25 +95,8 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
executors.add(e);
|
executors.add(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalAuthor createLocalAuthor(String nickname) {
|
|
||||||
long start = now();
|
|
||||||
KeyPair keyPair = crypto.generateSignatureKeyPair();
|
|
||||||
byte[] publicKey = keyPair.getPublic().getEncoded();
|
|
||||||
byte[] privateKey = keyPair.getPrivate().getEncoded();
|
|
||||||
LocalAuthor localAuthor = authorFactory
|
|
||||||
.createLocalAuthor(nickname, publicKey, privateKey);
|
|
||||||
logDuration(LOG, "Creating local author", start);
|
|
||||||
return localAuthor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void registerLocalAuthor(LocalAuthor author) throws DbException {
|
|
||||||
long start = now();
|
|
||||||
identityManager.registerLocalAuthor(author);
|
|
||||||
logDuration(LOG, "Registering local author", start);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StartResult startServices(@Nullable String nickname) {
|
public StartResult startServices(SecretKey dbKey) {
|
||||||
if (!startStopSemaphore.tryAcquire()) {
|
if (!startStopSemaphore.tryAcquire()) {
|
||||||
LOG.info("Already starting or stopping");
|
LOG.info("Already starting or stopping");
|
||||||
return ALREADY_RUNNING;
|
return ALREADY_RUNNING;
|
||||||
@@ -131,13 +105,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
|
|||||||
LOG.info("Starting services");
|
LOG.info("Starting services");
|
||||||
long start = now();
|
long start = now();
|
||||||
|
|
||||||
boolean reopened = db.open(this);
|
boolean reopened = db.open(dbKey, this);
|
||||||
if (reopened) logDuration(LOG, "Reopening database", start);
|
if (reopened) logDuration(LOG, "Reopening database", start);
|
||||||
else logDuration(LOG, "Creating database", start);
|
else logDuration(LOG, "Creating database", start);
|
||||||
|
identityManager.storeLocalAuthor();
|
||||||
if (nickname != null) {
|
|
||||||
registerLocalAuthor(createLocalAuthor(nickname));
|
|
||||||
}
|
|
||||||
|
|
||||||
state = STARTING_SERVICES;
|
state = STARTING_SERVICES;
|
||||||
dbLatch.countDown();
|
dbLatch.countDown();
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
package org.briarproject.bramble.lifecycle;
|
package org.briarproject.bramble.lifecycle;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
|
||||||
import org.briarproject.bramble.api.identity.AuthorFactory;
|
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
|
||||||
@@ -54,11 +49,9 @@ public class LifecycleModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
LifecycleManager provideLifecycleManager(DatabaseComponent db,
|
LifecycleManager provideLifecycleManager(
|
||||||
EventBus eventBus, CryptoComponent crypto,
|
LifecycleManagerImpl lifecycleManager) {
|
||||||
AuthorFactory authorFactory, IdentityManager identityManager) {
|
return lifecycleManager;
|
||||||
return new LifecycleManagerImpl(db, eventBus, crypto, authorFactory,
|
|
||||||
identityManager);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -0,0 +1,341 @@
|
|||||||
|
package org.briarproject.bramble.account;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.assertFalse;
|
||||||
|
import static junit.framework.Assert.assertNull;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
|
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||||
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
|
public class AccountManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
|
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 SecretKey key = getSecretKey();
|
||||||
|
private final byte[] encryptedKey = getRandomBytes(123);
|
||||||
|
private final String encryptedKeyHex = toHexString(encryptedKey);
|
||||||
|
private final byte[] newEncryptedKey = getRandomBytes(123);
|
||||||
|
private final String newEncryptedKeyHex = toHexString(newEncryptedKey);
|
||||||
|
private final LocalAuthor localAuthor = getLocalAuthor();
|
||||||
|
private final String authorName = localAuthor.getName();
|
||||||
|
private final String password = getRandomString(10);
|
||||||
|
private final String newPassword = getRandomString(10);
|
||||||
|
private final File testDir = getTestDirectory();
|
||||||
|
private final File dbDir = new File(testDir, "db");
|
||||||
|
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 AccountManagerImpl accountManager;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
allowing(databaseConfig).getDatabaseDirectory();
|
||||||
|
will(returnValue(dbDir));
|
||||||
|
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||||
|
will(returnValue(keyDir));
|
||||||
|
}});
|
||||||
|
|
||||||
|
accountManager =
|
||||||
|
new AccountManagerImpl(databaseConfig, crypto, identityManager);
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignInReturnsFalseIfDbKeyCannotBeLoaded() {
|
||||||
|
assertFalse(accountManager.signIn(password));
|
||||||
|
assertFalse(accountManager.hasDatabaseKey());
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignInReturnsFalseIfPasswordIsWrong() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(crypto).decryptWithPassword(encryptedKey, password);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
|
||||||
|
assertFalse(accountManager.signIn(password));
|
||||||
|
assertFalse(accountManager.hasDatabaseKey());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignInReturnsTrueIfPasswordIsRight() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(crypto).decryptWithPassword(encryptedKey, password);
|
||||||
|
will(returnValue(key.getBytes()));
|
||||||
|
}});
|
||||||
|
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
|
||||||
|
assertTrue(accountManager.signIn(password));
|
||||||
|
assertTrue(accountManager.hasDatabaseKey());
|
||||||
|
SecretKey decrypted = accountManager.getDatabaseKey();
|
||||||
|
assertNotNull(decrypted);
|
||||||
|
assertArrayEquals(key.getBytes(), decrypted.getBytes());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex,
|
||||||
|
accountManager.loadEncryptedDatabaseKey());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDbKeyIsLoadedFromBackupFile() throws Exception {
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex,
|
||||||
|
accountManager.loadEncryptedDatabaseKey());
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDbKeyIsNullIfNotFound() {
|
||||||
|
assertNull(accountManager.loadEncryptedDatabaseKey());
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStoringDbKeyOverwritesPrimary() throws Exception {
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
|
||||||
|
assertTrue(accountManager.storeEncryptedDatabaseKey(
|
||||||
|
newEncryptedKeyHex));
|
||||||
|
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStoringDbKeyOverwritesBackup() throws Exception {
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
|
||||||
|
assertTrue(accountManager.storeEncryptedDatabaseKey(
|
||||||
|
newEncryptedKeyHex));
|
||||||
|
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountExistsReturnsFalseIfDbKeyCannotBeLoaded() {
|
||||||
|
assertFalse(accountManager.accountExists());
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountExistsReturnsFalseIfDbDirectoryDoesNotExist()
|
||||||
|
throws Exception {
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertFalse(dbDir.exists());
|
||||||
|
|
||||||
|
assertFalse(accountManager.accountExists());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
assertFalse(dbDir.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountExistsReturnsFalseIfDbDirectoryIsNotDirectory()
|
||||||
|
throws Exception {
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertTrue(dbDir.createNewFile());
|
||||||
|
assertFalse(dbDir.isDirectory());
|
||||||
|
|
||||||
|
assertFalse(accountManager.accountExists());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
assertTrue(dbDir.exists());
|
||||||
|
assertFalse(dbDir.isDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAccountExistsReturnsTrueIfDbDirectoryIsDirectory()
|
||||||
|
throws Exception {
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertTrue(dbDir.mkdirs());
|
||||||
|
assertTrue(dbDir.isDirectory());
|
||||||
|
|
||||||
|
assertTrue(accountManager.accountExists());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
assertTrue(dbDir.exists());
|
||||||
|
assertTrue(dbDir.isDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCreateAccountStoresDbKey() throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(identityManager).createLocalAuthor(authorName);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
oneOf(identityManager).registerLocalAuthor(localAuthor);
|
||||||
|
oneOf(crypto).generateSecretKey();
|
||||||
|
will(returnValue(key));
|
||||||
|
oneOf(crypto).encryptWithPassword(key.getBytes(), password);
|
||||||
|
will(returnValue(encryptedKey));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertFalse(accountManager.hasDatabaseKey());
|
||||||
|
|
||||||
|
assertTrue(accountManager.createAccount(authorName, password));
|
||||||
|
|
||||||
|
assertTrue(accountManager.hasDatabaseKey());
|
||||||
|
SecretKey dbKey = accountManager.getDatabaseKey();
|
||||||
|
assertNotNull(dbKey);
|
||||||
|
assertArrayEquals(key.getBytes(), dbKey.getBytes());
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangePasswordReturnsFalseIfDbKeyCannotBeLoaded() {
|
||||||
|
assertFalse(accountManager.changePassword(password, newPassword));
|
||||||
|
|
||||||
|
assertFalse(keyFile.exists());
|
||||||
|
assertFalse(keyBackupFile.exists());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangePasswordReturnsFalseIfPasswordIsWrong()
|
||||||
|
throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(crypto).decryptWithPassword(encryptedKey, password);
|
||||||
|
will(returnValue(null));
|
||||||
|
}});
|
||||||
|
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertFalse(accountManager.changePassword(password, newPassword));
|
||||||
|
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testChangePasswordReturnsTrueIfPasswordIsRight()
|
||||||
|
throws Exception {
|
||||||
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(crypto).decryptWithPassword(encryptedKey, password);
|
||||||
|
will(returnValue(key.getBytes()));
|
||||||
|
oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword);
|
||||||
|
will(returnValue(newEncryptedKey));
|
||||||
|
}});
|
||||||
|
|
||||||
|
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||||
|
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||||
|
|
||||||
|
assertTrue(accountManager.changePassword(password, newPassword));
|
||||||
|
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
|
||||||
|
assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void storeDatabaseKey(File f, String hex) throws IOException {
|
||||||
|
f.getParentFile().mkdirs();
|
||||||
|
FileOutputStream out = new FileOutputStream(f);
|
||||||
|
out.write(hex.getBytes("UTF-8"));
|
||||||
|
out.flush();
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private String loadDatabaseKey(File f) throws IOException {
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||||
|
new FileInputStream(f), "UTF-8"));
|
||||||
|
String hex = reader.readLine();
|
||||||
|
reader.close();
|
||||||
|
return hex;
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
deleteTestDirectory(testDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,6 +89,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
context.mock(ShutdownManager.class);
|
context.mock(ShutdownManager.class);
|
||||||
private final EventBus eventBus = context.mock(EventBus.class);
|
private final EventBus eventBus = context.mock(EventBus.class);
|
||||||
|
|
||||||
|
private final SecretKey key = getSecretKey();
|
||||||
private final Object txn = new Object();
|
private final Object txn = new Object();
|
||||||
private final ClientId clientId;
|
private final ClientId clientId;
|
||||||
private final int majorVersion;
|
private final int majorVersion;
|
||||||
@@ -141,7 +142,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
int shutdownHandle = 12345;
|
int shutdownHandle = 12345;
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// open()
|
// open()
|
||||||
oneOf(database).open(null);
|
oneOf(database).open(key, null);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
||||||
will(returnValue(shutdownHandle));
|
will(returnValue(shutdownHandle));
|
||||||
@@ -208,7 +209,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
|
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
Transaction transaction = db.startTransaction(false);
|
Transaction transaction = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.addLocalAuthor(transaction, localAuthor);
|
db.addLocalAuthor(transaction, localAuthor);
|
||||||
@@ -1602,7 +1603,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
MessageId messageId2 = new MessageId(getRandomId());
|
MessageId messageId2 = new MessageId(getRandomId());
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// open()
|
// open()
|
||||||
oneOf(database).open(null);
|
oneOf(database).open(key, null);
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
|
||||||
will(returnValue(shutdownHandle));
|
will(returnValue(shutdownHandle));
|
||||||
@@ -1646,7 +1647,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
shutdown);
|
||||||
|
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
Transaction transaction = db.startTransaction(false);
|
Transaction transaction = db.startTransaction(false);
|
||||||
try {
|
try {
|
||||||
db.addLocalMessage(transaction, message, metadata, true);
|
db.addLocalMessage(transaction, message, metadata, true);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.db;
|
package org.briarproject.bramble.db;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DataTooNewException;
|
import org.briarproject.bramble.api.db.DataTooNewException;
|
||||||
import org.briarproject.bramble.api.db.DataTooOldException;
|
import org.briarproject.bramble.api.db.DataTooOldException;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
@@ -26,6 +27,7 @@ import static java.util.Collections.singletonList;
|
|||||||
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
|
||||||
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
|
||||||
import static org.briarproject.bramble.db.JdbcDatabase.CODE_SCHEMA_VERSION;
|
import static org.briarproject.bramble.db.JdbcDatabase.CODE_SCHEMA_VERSION;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -43,6 +45,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
protected final DatabaseConfig config =
|
protected final DatabaseConfig config =
|
||||||
new TestDatabaseConfig(testDir, 1024 * 1024);
|
new TestDatabaseConfig(testDir, 1024 * 1024);
|
||||||
|
protected final SecretKey key = getSecretKey();
|
||||||
protected final Clock clock = new SystemClock();
|
protected final Clock clock = new SystemClock();
|
||||||
|
|
||||||
abstract Database<Connection> createDatabase(
|
abstract Database<Connection> createDatabase(
|
||||||
@@ -62,7 +65,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
public void testDoesNotRunMigrationsWhenCreatingDatabase()
|
public void testDoesNotRunMigrationsWhenCreatingDatabase()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Database<Connection> db = createDatabase(singletonList(migration));
|
Database<Connection> db = createDatabase(singletonList(migration));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
@@ -72,14 +75,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
// Override the data schema version
|
// Override the data schema version
|
||||||
setDataSchemaVersion(db, -1);
|
setDataSchemaVersion(db, -1);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - an exception should be thrown
|
// Reopen the DB - an exception should be thrown
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
db.open(null);
|
db.open(key, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -87,12 +90,12 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - migrations should not be run
|
// Reopen the DB - migrations should not be run
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
assertTrue(db.open(null));
|
assertTrue(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
@@ -101,14 +104,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception {
|
public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception {
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
// Override the data schema version
|
// Override the data schema version
|
||||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION + 1);
|
setDataSchemaVersion(db, CODE_SCHEMA_VERSION + 1);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - an exception should be thrown
|
// Reopen the DB - an exception should be thrown
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
db.open(null);
|
db.open(key, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = DataTooOldException.class)
|
@Test(expected = DataTooOldException.class)
|
||||||
@@ -116,13 +119,13 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(emptyList());
|
Database<Connection> db = createDatabase(emptyList());
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 1);
|
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 1);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - an exception should be thrown
|
// Reopen the DB - an exception should be thrown
|
||||||
db = createDatabase(emptyList());
|
db = createDatabase(emptyList());
|
||||||
db.open(null);
|
db.open(key, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = DataTooOldException.class)
|
@Test(expected = DataTooOldException.class)
|
||||||
@@ -141,14 +144,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
// Override the data schema version
|
// Override the data schema version
|
||||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 3);
|
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 3);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - an exception should be thrown
|
// Reopen the DB - an exception should be thrown
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
db.open(null);
|
db.open(key, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -170,14 +173,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
// Override the data schema version
|
// Override the data schema version
|
||||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - the first migration should be run
|
// Reopen the DB - the first migration should be run
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
assertTrue(db.open(null));
|
assertTrue(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
@@ -202,14 +205,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
// Open the DB for the first time
|
// Open the DB for the first time
|
||||||
Database<Connection> db = createDatabase(asList(migration, migration1));
|
Database<Connection> db = createDatabase(asList(migration, migration1));
|
||||||
assertFalse(db.open(null));
|
assertFalse(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
// Override the data schema version
|
// Override the data schema version
|
||||||
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
|
||||||
db.close();
|
db.close();
|
||||||
// Reopen the DB - both migrations should be run
|
// Reopen the DB - both migrations should be run
|
||||||
db = createDatabase(asList(migration, migration1));
|
db = createDatabase(asList(migration, migration1));
|
||||||
assertTrue(db.open(null));
|
assertTrue(db.open(key, null));
|
||||||
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import java.util.List;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMean;
|
import static org.briarproject.bramble.test.TestUtils.getMean;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMedian;
|
import static org.briarproject.bramble.test.TestUtils.getMedian;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
|
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
|
||||||
import static org.briarproject.bramble.test.UTest.Z_CRITICAL_0_01;
|
import static org.briarproject.bramble.test.UTest.Z_CRITICAL_0_01;
|
||||||
|
|
||||||
@@ -71,7 +72,7 @@ public abstract class DatabasePerformanceComparisonTest
|
|||||||
throws DbException {
|
throws DbException {
|
||||||
Database<Connection> db = createDatabase(conditionA,
|
Database<Connection> db = createDatabase(conditionA,
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||||
db.open(null);
|
db.open(getSecretKey(), null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import java.sql.Connection;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
|
||||||
public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
|
public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
|
|||||||
private Database<Connection> openDatabase() throws DbException {
|
private Database<Connection> openDatabase() throws DbException {
|
||||||
Database<Connection> db = createDatabase(
|
Database<Connection> db = createDatabase(
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||||
db.open(null);
|
db.open(getSecretKey(), null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import java.util.List;
|
|||||||
public class H2MigrationTest extends DatabaseMigrationTest {
|
public class H2MigrationTest extends DatabaseMigrationTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Database<Connection> createDatabase(List<Migration<Connection>> migrations)
|
Database<Connection> createDatabase(
|
||||||
throws Exception {
|
List<Migration<Connection>> migrations) {
|
||||||
return new H2Database(config, clock) {
|
return new H2Database(config, clock) {
|
||||||
@Override
|
@Override
|
||||||
List<Migration<Connection>> getMigrations() {
|
List<Migration<Connection>> getMigrations() {
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
import static org.briarproject.bramble.test.TestUtils.getRandomId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -79,7 +80,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
private static final int ONE_MEGABYTE = 1024 * 1024;
|
private static final int ONE_MEGABYTE = 1024 * 1024;
|
||||||
private static final int MAX_SIZE = 5 * ONE_MEGABYTE;
|
private static final int MAX_SIZE = 5 * ONE_MEGABYTE;
|
||||||
|
|
||||||
private final File testDir = TestUtils.getTestDirectory();
|
private final SecretKey key = getSecretKey();
|
||||||
|
private final File testDir = getTestDirectory();
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
private final ClientId clientId;
|
private final ClientId clientId;
|
||||||
private final int majorVersion;
|
private final int majorVersion;
|
||||||
@@ -96,7 +98,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
private final KeySetId keySetId, keySetId1;
|
private final KeySetId keySetId, keySetId1;
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
JdbcDatabaseTest() throws Exception {
|
JdbcDatabaseTest() {
|
||||||
clientId = getClientId();
|
clientId = getClientId();
|
||||||
majorVersion = 123;
|
majorVersion = 123;
|
||||||
group = getGroup(clientId, majorVersion);
|
group = getGroup(clientId, majorVersion);
|
||||||
@@ -1819,7 +1821,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Database<Connection> db = createDatabase(
|
Database<Connection> db = createDatabase(
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE), clock);
|
new TestDatabaseConfig(testDir, MAX_SIZE), clock);
|
||||||
if (!resume) TestUtils.deleteTestDirectory(testDir);
|
if (!resume) TestUtils.deleteTestDirectory(testDir);
|
||||||
db.open(null);
|
db.open(key, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.List;
|
|||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMean;
|
import static org.briarproject.bramble.test.TestUtils.getMean;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getMedian;
|
import static org.briarproject.bramble.test.TestUtils.getMedian;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
|
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
|
||||||
|
|
||||||
public abstract class SingleDatabasePerformanceTest
|
public abstract class SingleDatabasePerformanceTest
|
||||||
@@ -40,7 +41,7 @@ public abstract class SingleDatabasePerformanceTest
|
|||||||
private Database<Connection> openDatabase() throws DbException {
|
private Database<Connection> openDatabase() throws DbException {
|
||||||
Database<Connection> db = createDatabase(
|
Database<Connection> db = createDatabase(
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
|
||||||
db.open(null);
|
db.open(getSecretKey(), null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,21 @@ package org.briarproject.bramble.identity;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.contact.Contact;
|
import org.briarproject.bramble.api.contact.Contact;
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.KeyPair;
|
||||||
|
import org.briarproject.bramble.api.crypto.PrivateKey;
|
||||||
|
import org.briarproject.bramble.api.crypto.PublicKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.db.Transaction;
|
import org.briarproject.bramble.api.db.Transaction;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
|
import org.briarproject.bramble.api.identity.AuthorFactory;
|
||||||
import org.briarproject.bramble.api.identity.AuthorId;
|
import org.briarproject.bramble.api.identity.AuthorId;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -27,24 +33,48 @@ import static org.junit.Assert.assertEquals;
|
|||||||
|
|
||||||
public class IdentityManagerImplTest extends BrambleMockTestCase {
|
public class IdentityManagerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final IdentityManager identityManager;
|
|
||||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||||
|
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||||
|
private final AuthorFactory authorFactory =
|
||||||
|
context.mock(AuthorFactory.class);
|
||||||
|
private final PublicKey publicKey = context.mock(PublicKey.class);
|
||||||
|
private final PrivateKey privateKey = context.mock(PrivateKey.class);
|
||||||
|
|
||||||
private final Transaction txn = new Transaction(null, false);
|
private final Transaction txn = new Transaction(null, false);
|
||||||
private final LocalAuthor localAuthor = getLocalAuthor();
|
private final LocalAuthor localAuthor = getLocalAuthor();
|
||||||
private final Collection<LocalAuthor> localAuthors =
|
private final Collection<LocalAuthor> localAuthors =
|
||||||
Collections.singletonList(localAuthor);
|
Collections.singletonList(localAuthor);
|
||||||
|
private final String authorName = localAuthor.getName();
|
||||||
|
private final KeyPair keyPair = new KeyPair(publicKey, privateKey);
|
||||||
|
private final byte[] publicKeyBytes = localAuthor.getPublicKey();
|
||||||
|
private final byte[] privateKeyBytes = localAuthor.getPrivateKey();
|
||||||
|
private IdentityManager identityManager;
|
||||||
|
|
||||||
public IdentityManagerImplTest() {
|
@Before
|
||||||
identityManager = new IdentityManagerImpl(db);
|
public void setUp() {
|
||||||
|
identityManager = new IdentityManagerImpl(db, crypto, authorFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRegisterLocalAuthor() throws DbException {
|
public void testCreateLocalAuthor() {
|
||||||
expectRegisterLocalAuthor();
|
context.checking(new Expectations() {{
|
||||||
identityManager.registerLocalAuthor(localAuthor);
|
oneOf(crypto).generateSignatureKeyPair();
|
||||||
|
will(returnValue(keyPair));
|
||||||
|
oneOf(publicKey).getEncoded();
|
||||||
|
will(returnValue(publicKeyBytes));
|
||||||
|
oneOf(privateKey).getEncoded();
|
||||||
|
will(returnValue(privateKeyBytes));
|
||||||
|
oneOf(authorFactory).createLocalAuthor(authorName,
|
||||||
|
publicKeyBytes, privateKeyBytes);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
|
}});
|
||||||
|
|
||||||
|
assertEquals(localAuthor,
|
||||||
|
identityManager.createLocalAuthor(authorName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectRegisterLocalAuthor() throws DbException {
|
@Test
|
||||||
|
public void testRegisterAndStoreLocalAuthor() throws DbException {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).startTransaction(false);
|
oneOf(db).startTransaction(false);
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
@@ -52,6 +82,10 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(db).commitTransaction(txn);
|
oneOf(db).commitTransaction(txn);
|
||||||
oneOf(db).endTransaction(txn);
|
oneOf(db).endTransaction(txn);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
|
identityManager.registerLocalAuthor(localAuthor);
|
||||||
|
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
||||||
|
identityManager.storeLocalAuthor();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -69,7 +103,6 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetCachedLocalAuthor() throws DbException {
|
public void testGetCachedLocalAuthor() throws DbException {
|
||||||
expectRegisterLocalAuthor();
|
|
||||||
identityManager.registerLocalAuthor(localAuthor);
|
identityManager.registerLocalAuthor(localAuthor);
|
||||||
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
assertEquals(localAuthor, identityManager.getLocalAuthor());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
@@ -11,7 +10,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
|||||||
|
|
||||||
private final File dbDir, keyDir;
|
private final File dbDir, keyDir;
|
||||||
private final long maxSize;
|
private final long maxSize;
|
||||||
private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]);
|
|
||||||
|
|
||||||
public TestDatabaseConfig(File testDir, long maxSize) {
|
public TestDatabaseConfig(File testDir, long maxSize) {
|
||||||
dbDir = new File(testDir, "db");
|
dbDir = new File(testDir, "db");
|
||||||
@@ -19,13 +17,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
|||||||
this.maxSize = maxSize;
|
this.maxSize = maxSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean databaseExists() {
|
|
||||||
if (!dbDir.isDirectory()) return false;
|
|
||||||
File[] files = dbDir.listFiles();
|
|
||||||
return files != null && files.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getDatabaseDirectory() {
|
public File getDatabaseDirectory() {
|
||||||
return dbDir;
|
return dbDir;
|
||||||
@@ -36,26 +27,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
|
|||||||
return keyDir;
|
return keyDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEncryptionKey(SecretKey key) {
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SecretKey getEncryptionKey() {
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLocalAuthorName(String nickname) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLocalAuthorName() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getMaxSize() {
|
public long getMaxSize() {
|
||||||
return maxSize;
|
return maxSize;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.bramble.test;
|
package org.briarproject.bramble.test;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.lifecycle.Service;
|
||||||
@@ -11,7 +12,6 @@ import java.util.concurrent.Executor;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
@@ -40,7 +40,7 @@ public class TestLifecycleModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StartResult startServices(@Nullable String nickname) {
|
public StartResult startServices(SecretKey dbKey) {
|
||||||
return StartResult.SUCCESS;
|
return StartResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,15 +49,15 @@ public class TestLifecycleModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void waitForDatabase() throws InterruptedException {
|
public void waitForDatabase() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void waitForStartup() throws InterruptedException {
|
public void waitForStartup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void waitForShutdown() throws InterruptedException {
|
public void waitForShutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package org.briarproject.bramble.account;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
import org.briarproject.briar.android.Localizer;
|
||||||
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
class BriarAccountManager extends AndroidAccountManager {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
BriarAccountManager(DatabaseConfig databaseConfig, CryptoComponent crypto,
|
||||||
|
IdentityManager identityManager, SharedPreferences prefs,
|
||||||
|
Application app) {
|
||||||
|
super(databaseConfig, crypto, identityManager, prefs, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteAccount() {
|
||||||
|
synchronized (stateChangeLock) {
|
||||||
|
super.deleteAccount();
|
||||||
|
Localizer.reinitialize();
|
||||||
|
UiUtils.setTheme(appContext,
|
||||||
|
appContext.getString(R.string.pref_theme_light_value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package org.briarproject.bramble.account;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
|
||||||
|
import dagger.Module;
|
||||||
|
import dagger.Provides;
|
||||||
|
|
||||||
|
@Module
|
||||||
|
public class BriarAccountModule {
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
AccountManager provideAccountManager(BriarAccountManager accountManager) {
|
||||||
|
return accountManager;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.BrambleAndroidModule;
|
import org.briarproject.bramble.BrambleAndroidModule;
|
||||||
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
import org.briarproject.bramble.BrambleCoreEagerSingletons;
|
||||||
import org.briarproject.bramble.BrambleCoreModule;
|
import org.briarproject.bramble.BrambleCoreModule;
|
||||||
|
import org.briarproject.bramble.account.BriarAccountModule;
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
import org.briarproject.bramble.api.contact.ContactExchangeTask;
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.identity.IdentityManager;
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
@@ -62,6 +60,7 @@ import dagger.Component;
|
|||||||
BrambleCoreModule.class,
|
BrambleCoreModule.class,
|
||||||
BriarCoreModule.class,
|
BriarCoreModule.class,
|
||||||
BrambleAndroidModule.class,
|
BrambleAndroidModule.class,
|
||||||
|
BriarAccountModule.class,
|
||||||
AppModule.class
|
AppModule.class
|
||||||
})
|
})
|
||||||
public interface AndroidComponent
|
public interface AndroidComponent
|
||||||
@@ -73,10 +72,6 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
PasswordStrengthEstimator passwordStrengthIndicator();
|
PasswordStrengthEstimator passwordStrengthIndicator();
|
||||||
|
|
||||||
CryptoComponent cryptoComponent();
|
|
||||||
|
|
||||||
DatabaseConfig databaseConfig();
|
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
Executor databaseExecutor();
|
Executor databaseExecutor();
|
||||||
|
|
||||||
@@ -92,8 +87,6 @@ public interface AndroidComponent
|
|||||||
|
|
||||||
AndroidNotificationManager androidNotificationManager();
|
AndroidNotificationManager androidNotificationManager();
|
||||||
|
|
||||||
SharedPreferences sharedPreferences();
|
|
||||||
|
|
||||||
ScreenFilterMonitor screenFilterMonitor();
|
ScreenFilterMonitor screenFilterMonitor();
|
||||||
|
|
||||||
ConnectionRegistry connectionRegistry();
|
ConnectionRegistry connectionRegistry();
|
||||||
@@ -151,6 +144,8 @@ public interface AndroidComponent
|
|||||||
@IoExecutor
|
@IoExecutor
|
||||||
Executor ioExecutor();
|
Executor ioExecutor();
|
||||||
|
|
||||||
|
AccountManager accountManager();
|
||||||
|
|
||||||
void inject(SignInReminderReceiver briarService);
|
void inject(SignInReminderReceiver briarService);
|
||||||
|
|
||||||
void inject(BriarService briarService);
|
void inject(BriarService briarService);
|
||||||
|
|||||||
@@ -1,99 +1,30 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class AndroidDatabaseConfig implements DatabaseConfig {
|
class AndroidDatabaseConfig implements DatabaseConfig {
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(AndroidDatabaseConfig.class.getName());
|
|
||||||
|
|
||||||
private final File dbDir, keyDir;
|
private final File dbDir, keyDir;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private volatile SecretKey key = null;
|
|
||||||
@Nullable
|
|
||||||
private volatile String nickname = null;
|
|
||||||
|
|
||||||
AndroidDatabaseConfig(File dbDir, File keyDir) {
|
AndroidDatabaseConfig(File dbDir, File keyDir) {
|
||||||
this.dbDir = dbDir;
|
this.dbDir = dbDir;
|
||||||
this.keyDir = keyDir;
|
this.keyDir = keyDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean databaseExists() {
|
|
||||||
// FIXME should not run on UiThread #620
|
|
||||||
if (!dbDir.isDirectory()) {
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info(dbDir.getAbsolutePath() + " is not a directory");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
File[] files = dbDir.listFiles();
|
|
||||||
if (LOG.isLoggable(INFO)) {
|
|
||||||
if (files == null) {
|
|
||||||
LOG.info("Could not list files in " + dbDir.getAbsolutePath());
|
|
||||||
} else {
|
|
||||||
LOG.info("Files in " + dbDir.getAbsolutePath() + ":");
|
|
||||||
for (File f : files) LOG.info(f.getName());
|
|
||||||
}
|
|
||||||
LOG.info("Database exists: " + (files != null && files.length > 0));
|
|
||||||
}
|
|
||||||
return files != null && files.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getDatabaseDirectory() {
|
public File getDatabaseDirectory() {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Database directory: " + dbDir.getAbsolutePath());
|
|
||||||
return dbDir;
|
return dbDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File getDatabaseKeyDirectory() {
|
public File getDatabaseKeyDirectory() {
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Database key directory: " + keyDir.getAbsolutePath());
|
|
||||||
return keyDir;
|
return keyDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEncryptionKey(SecretKey key) {
|
|
||||||
LOG.info("Setting database key");
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLocalAuthorName(String nickname) {
|
|
||||||
LOG.info("Setting local author name");
|
|
||||||
this.nickname = nickname;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public String getLocalAuthorName() {
|
|
||||||
String nickname = this.nickname;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Local author name has been set: " + (nickname != null));
|
|
||||||
return nickname;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public SecretKey getEncryptionKey() {
|
|
||||||
SecretKey key = this.key;
|
|
||||||
if (LOG.isLoggable(INFO))
|
|
||||||
LOG.info("Database key has been set: " + (key != null));
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getMaxSize() {
|
public long getMaxSize() {
|
||||||
return Long.MAX_VALUE;
|
return Long.MAX_VALUE;
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
|||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
import org.briarproject.bramble.api.plugin.BackoffFactory;
|
||||||
import org.briarproject.bramble.api.plugin.PluginConfig;
|
import org.briarproject.bramble.api.plugin.PluginConfig;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
|
||||||
@@ -86,11 +84,7 @@ public class AppModule {
|
|||||||
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||||
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
|
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
|
||||||
StrictMode.setThreadPolicy(tp);
|
StrictMode.setThreadPolicy(tp);
|
||||||
@MethodsNotNullByDefault
|
return new AndroidDatabaseConfig(dbDir, keyDir);
|
||||||
@ParametersNotNullByDefault
|
|
||||||
DatabaseConfig databaseConfig =
|
|
||||||
new AndroidDatabaseConfig(dbDir, keyDir);
|
|
||||||
return databaseConfig;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import android.os.IBinder;
|
|||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
@@ -74,12 +75,13 @@ public class BriarService extends Service {
|
|||||||
private BroadcastReceiver receiver = null;
|
private BroadcastReceiver receiver = null;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected DatabaseConfig databaseConfig;
|
AccountManager accountManager;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
// Fields that are accessed from background threads must be volatile
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile LifecycleManager lifecycleManager;
|
volatile LifecycleManager lifecycleManager;
|
||||||
@Inject
|
@Inject
|
||||||
protected volatile AndroidExecutor androidExecutor;
|
volatile AndroidExecutor androidExecutor;
|
||||||
private volatile boolean started = false;
|
private volatile boolean started = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,7 +97,8 @@ public class BriarService extends Service {
|
|||||||
stopSelf();
|
stopSelf();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (databaseConfig.getEncryptionKey() == null) {
|
SecretKey dbKey = accountManager.getDatabaseKey();
|
||||||
|
if (dbKey == null) {
|
||||||
LOG.info("No database key");
|
LOG.info("No database key");
|
||||||
stopSelf();
|
stopSelf();
|
||||||
return;
|
return;
|
||||||
@@ -138,8 +141,7 @@ public class BriarService extends Service {
|
|||||||
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
startForeground(ONGOING_NOTIFICATION_ID, b.build());
|
||||||
// Start the services in a background thread
|
// Start the services in a background thread
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
String nickname = databaseConfig.getLocalAuthorName();
|
StartResult result = lifecycleManager.startServices(dbKey);
|
||||||
StartResult result = lifecycleManager.startServices(nickname);
|
|
||||||
if (result == SUCCESS) {
|
if (result == SUCCESS) {
|
||||||
started = true;
|
started = true;
|
||||||
} else if (result == ALREADY_RUNNING) {
|
} else if (result == ALREADY_RUNNING) {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import android.app.Activity;
|
|||||||
|
|
||||||
import org.briarproject.briar.android.controller.BriarController;
|
import org.briarproject.briar.android.controller.BriarController;
|
||||||
import org.briarproject.briar.android.controller.BriarControllerImpl;
|
import org.briarproject.briar.android.controller.BriarControllerImpl;
|
||||||
import org.briarproject.briar.android.controller.ConfigController;
|
|
||||||
import org.briarproject.briar.android.controller.ConfigControllerImpl;
|
|
||||||
import org.briarproject.briar.android.controller.DbController;
|
import org.briarproject.briar.android.controller.DbController;
|
||||||
import org.briarproject.briar.android.controller.DbControllerImpl;
|
import org.briarproject.briar.android.controller.DbControllerImpl;
|
||||||
import org.briarproject.briar.android.login.PasswordController;
|
import org.briarproject.briar.android.login.PasswordController;
|
||||||
@@ -48,13 +46,6 @@ public class ActivityModule {
|
|||||||
return setupController;
|
return setupController;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ActivityScope
|
|
||||||
@Provides
|
|
||||||
ConfigController provideConfigController(
|
|
||||||
ConfigControllerImpl configController) {
|
|
||||||
return configController;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
@Provides
|
@Provides
|
||||||
PasswordController providePasswordController(
|
PasswordController providePasswordController(
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
if (!briarController.hasEncryptionKey() && !isFinishing()) {
|
if (!briarController.accountSignedIn() && !isFinishing()) {
|
||||||
Intent i = new Intent(this, PasswordActivity.class);
|
Intent i = new Intent(this, PasswordActivity.class);
|
||||||
startActivityForResult(i, REQUEST_PASSWORD);
|
startActivityForResult(i, REQUEST_PASSWORD);
|
||||||
} else if (SDK_INT >= 23) {
|
} else if (SDK_INT >= 23) {
|
||||||
@@ -138,7 +138,7 @@ public abstract class BriarActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void signOut(boolean removeFromRecentApps) {
|
protected void signOut(boolean removeFromRecentApps) {
|
||||||
if (briarController.hasEncryptionKey()) {
|
if (briarController.accountSignedIn()) {
|
||||||
// Don't use UiResultHandler because we want the result even if
|
// Don't use UiResultHandler because we want the result even if
|
||||||
// this activity has been destroyed
|
// this activity has been destroyed
|
||||||
briarController.signOut(result -> runOnUiThread(
|
briarController.signOut(result -> runOnUiThread(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ public interface BriarController extends ActivityLifecycleController {
|
|||||||
|
|
||||||
void startAndBindService();
|
void startAndBindService();
|
||||||
|
|
||||||
boolean hasEncryptionKey();
|
boolean accountSignedIn();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true via the handler when the app has dozed
|
* Returns true via the handler when the app has dozed
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.content.Intent;
|
|||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.settings.Settings;
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
@@ -33,8 +33,7 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
public static final String DOZE_ASK_AGAIN = "dozeAskAgain";
|
public static final String DOZE_ASK_AGAIN = "dozeAskAgain";
|
||||||
|
|
||||||
private final BriarServiceConnection serviceConnection;
|
private final BriarServiceConnection serviceConnection;
|
||||||
private final DatabaseConfig databaseConfig;
|
private final AccountManager accountManager;
|
||||||
@DatabaseExecutor
|
|
||||||
private final Executor databaseExecutor;
|
private final Executor databaseExecutor;
|
||||||
private final SettingsManager settingsManager;
|
private final SettingsManager settingsManager;
|
||||||
private final DozeWatchdog dozeWatchdog;
|
private final DozeWatchdog dozeWatchdog;
|
||||||
@@ -44,12 +43,12 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
BriarControllerImpl(BriarServiceConnection serviceConnection,
|
||||||
DatabaseConfig databaseConfig,
|
AccountManager accountManager,
|
||||||
@DatabaseExecutor Executor databaseExecutor,
|
@DatabaseExecutor Executor databaseExecutor,
|
||||||
SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
|
SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
|
||||||
Activity activity) {
|
Activity activity) {
|
||||||
this.serviceConnection = serviceConnection;
|
this.serviceConnection = serviceConnection;
|
||||||
this.databaseConfig = databaseConfig;
|
this.accountManager = accountManager;
|
||||||
this.databaseExecutor = databaseExecutor;
|
this.databaseExecutor = databaseExecutor;
|
||||||
this.settingsManager = settingsManager;
|
this.settingsManager = settingsManager;
|
||||||
this.dozeWatchdog = dozeWatchdog;
|
this.dozeWatchdog = dozeWatchdog;
|
||||||
@@ -59,7 +58,7 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void onActivityCreate(Activity activity) {
|
public void onActivityCreate(Activity activity) {
|
||||||
if (databaseConfig.getEncryptionKey() != null) startAndBindService();
|
if (accountManager.hasDatabaseKey()) startAndBindService();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -84,8 +83,8 @@ public class BriarControllerImpl implements BriarController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasEncryptionKey() {
|
public boolean accountSignedIn() {
|
||||||
return databaseConfig.getEncryptionKey() != null;
|
return accountManager.hasDatabaseKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
package org.briarproject.briar.android.controller;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public interface ConfigController {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getEncryptedDatabaseKey();
|
|
||||||
|
|
||||||
boolean storeEncryptedDatabaseKey(String hex);
|
|
||||||
|
|
||||||
void deleteAccount(Context ctx);
|
|
||||||
|
|
||||||
boolean accountExists();
|
|
||||||
|
|
||||||
boolean accountSignedIn();
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
package org.briarproject.briar.android.controller;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.support.v7.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.util.AndroidUtils;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class ConfigControllerImpl implements ConfigController {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
|
||||||
Logger.getLogger(ConfigControllerImpl.class.getName());
|
|
||||||
|
|
||||||
private static final String PREF_DB_KEY = "key";
|
|
||||||
private static final String DB_KEY_FILENAME = "db.key";
|
|
||||||
private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
|
|
||||||
|
|
||||||
private final SharedPreferences briarPrefs;
|
|
||||||
private final File dbKeyFile, dbKeyBackupFile;
|
|
||||||
protected final DatabaseConfig databaseConfig;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ConfigControllerImpl(SharedPreferences briarPrefs,
|
|
||||||
DatabaseConfig databaseConfig) {
|
|
||||||
this.briarPrefs = briarPrefs;
|
|
||||||
this.databaseConfig = databaseConfig;
|
|
||||||
File keyDir = databaseConfig.getDatabaseKeyDirectory();
|
|
||||||
dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
|
|
||||||
dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@Nullable
|
|
||||||
public String getEncryptedDatabaseKey() {
|
|
||||||
String key = getDatabaseKeyFromPreferences();
|
|
||||||
if (key == null) key = getDatabaseKeyFromFile();
|
|
||||||
else migrateDatabaseKeyToFile(key);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String getDatabaseKeyFromPreferences() {
|
|
||||||
String key = briarPrefs.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String getDatabaseKeyFromFile() {
|
|
||||||
String key = readDbKeyFromFile(dbKeyFile);
|
|
||||||
if (key == null) {
|
|
||||||
LOG.info("No database key in primary file");
|
|
||||||
key = readDbKeyFromFile(dbKeyBackupFile);
|
|
||||||
if (key == null) LOG.info("No database key in backup file");
|
|
||||||
else LOG.warning("Found database key in backup file");
|
|
||||||
} else {
|
|
||||||
LOG.info("Found database key in primary file");
|
|
||||||
}
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String readDbKeyFromFile(File f) {
|
|
||||||
if (!f.exists()) {
|
|
||||||
LOG.info("Key file does not exist");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
|
||||||
new FileInputStream(f), "UTF-8"));
|
|
||||||
String key = reader.readLine();
|
|
||||||
reader.close();
|
|
||||||
return key;
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void migrateDatabaseKeyToFile(String key) {
|
|
||||||
if (storeEncryptedDatabaseKey(key)) {
|
|
||||||
if (briarPrefs.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 boolean storeEncryptedDatabaseKey(String hex) {
|
|
||||||
LOG.info("Storing database key in file");
|
|
||||||
// Create the directory if necessary
|
|
||||||
if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
|
|
||||||
LOG.info("Created database key directory");
|
|
||||||
// If only the backup file exists, rename it so we don't overwrite it
|
|
||||||
if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
|
|
||||||
if (dbKeyBackupFile.renameTo(dbKeyFile))
|
|
||||||
LOG.info("Renamed old backup");
|
|
||||||
else LOG.warning("Failed to rename old backup");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Write to the backup file
|
|
||||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
|
||||||
LOG.info("Stored database key in backup file");
|
|
||||||
// Delete the old primary file, if it exists
|
|
||||||
if (dbKeyFile.exists()) {
|
|
||||||
if (dbKeyFile.delete()) LOG.info("Deleted primary file");
|
|
||||||
else LOG.warning("Failed to delete primary file");
|
|
||||||
}
|
|
||||||
// The backup file becomes the new primary
|
|
||||||
if (dbKeyBackupFile.renameTo(dbKeyFile)) {
|
|
||||||
LOG.info("Renamed backup file to primary");
|
|
||||||
} else {
|
|
||||||
LOG.warning("Failed to rename backup file to primary");
|
|
||||||
return false; // Don't overwrite our only copy
|
|
||||||
}
|
|
||||||
// Write a second copy to the backup file
|
|
||||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
|
||||||
LOG.info("Stored second copy of database key in backup file");
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
|
||||||
FileOutputStream out = new FileOutputStream(f);
|
|
||||||
out.write(key.getBytes("UTF-8"));
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteAccount(Context ctx) {
|
|
||||||
LOG.info("Deleting account");
|
|
||||||
SharedPreferences defaultPrefs =
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(ctx);
|
|
||||||
AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accountExists() {
|
|
||||||
String hex = getEncryptedDatabaseKey();
|
|
||||||
return hex != null && databaseConfig.databaseExists();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean accountSignedIn() {
|
|
||||||
return databaseConfig.getEncryptionKey() != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,8 +13,8 @@ import android.widget.Button;
|
|||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.Localizer;
|
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
import org.briarproject.briar.android.controller.BriarController;
|
import org.briarproject.briar.android.controller.BriarController;
|
||||||
@@ -33,6 +33,9 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.REMI
|
|||||||
|
|
||||||
public class PasswordActivity extends BaseActivity {
|
public class PasswordActivity extends BaseActivity {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountManager accountManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PasswordController passwordController;
|
PasswordController passwordController;
|
||||||
|
|
||||||
@@ -50,7 +53,8 @@ public class PasswordActivity extends BaseActivity {
|
|||||||
// fade-in after splash screen instead of default animation
|
// fade-in after splash screen instead of default animation
|
||||||
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
|
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
|
||||||
|
|
||||||
if (!passwordController.accountExists()) {
|
if (!accountManager.accountExists()) {
|
||||||
|
// TODO: Finish instead of deleting account?
|
||||||
deleteAccount();
|
deleteAccount();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -87,7 +91,7 @@ public class PasswordActivity extends BaseActivity {
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
// If the user has already signed in, clean up this instance
|
// If the user has already signed in, clean up this instance
|
||||||
if (briarController.hasEncryptionKey()) {
|
if (briarController.accountSignedIn()) {
|
||||||
setResult(RESULT_OK);
|
setResult(RESULT_OK);
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
@@ -112,9 +116,7 @@ public class PasswordActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void deleteAccount() {
|
private void deleteAccount() {
|
||||||
passwordController.deleteAccount(this);
|
accountManager.deleteAccount();
|
||||||
Localizer.reinitialize();
|
|
||||||
UiUtils.setTheme(this, getString(R.string.pref_theme_light_value));
|
|
||||||
setResult(RESULT_CANCELED);
|
setResult(RESULT_CANCELED);
|
||||||
Intent i = new Intent(this, SetupActivity.class);
|
Intent i = new Intent(this, SetupActivity.class);
|
||||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.controller.ConfigController;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface PasswordController extends ConfigController {
|
public interface PasswordController {
|
||||||
|
|
||||||
float estimatePasswordStrength(String password);
|
float estimatePasswordStrength(String password);
|
||||||
|
|
||||||
void validatePassword(String password,
|
void validatePassword(String password,
|
||||||
ResultHandler<Boolean> resultHandler);
|
ResultHandler<Boolean> resultHandler);
|
||||||
|
|
||||||
void changePassword(String password, String newPassword,
|
void changePassword(String oldPassword, String newPassword,
|
||||||
ResultHandler<Boolean> resultHandler);
|
ResultHandler<Boolean> resultHandler);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,28 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
|
||||||
import org.briarproject.briar.android.controller.ConfigControllerImpl;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class PasswordControllerImpl extends ConfigControllerImpl
|
public class PasswordControllerImpl implements PasswordController {
|
||||||
implements PasswordController {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
protected final AccountManager accountManager;
|
||||||
Logger.getLogger(PasswordControllerImpl.class.getName());
|
protected final Executor ioExecutor;
|
||||||
|
|
||||||
protected final Executor cryptoExecutor;
|
|
||||||
protected final CryptoComponent crypto;
|
|
||||||
private final PasswordStrengthEstimator strengthEstimator;
|
private final PasswordStrengthEstimator strengthEstimator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
PasswordControllerImpl(SharedPreferences briarPrefs,
|
PasswordControllerImpl(AccountManager accountManager,
|
||||||
DatabaseConfig databaseConfig,
|
@IoExecutor Executor ioExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
|
|
||||||
PasswordStrengthEstimator strengthEstimator) {
|
PasswordStrengthEstimator strengthEstimator) {
|
||||||
super(briarPrefs, databaseConfig);
|
this.accountManager = accountManager;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.crypto = crypto;
|
|
||||||
this.strengthEstimator = strengthEstimator;
|
this.strengthEstimator = strengthEstimator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,46 +34,17 @@ public class PasswordControllerImpl extends ConfigControllerImpl
|
|||||||
@Override
|
@Override
|
||||||
public void validatePassword(String password,
|
public void validatePassword(String password,
|
||||||
ResultHandler<Boolean> resultHandler) {
|
ResultHandler<Boolean> resultHandler) {
|
||||||
byte[] encrypted = getEncryptedKey();
|
ioExecutor.execute(() ->
|
||||||
cryptoExecutor.execute(() -> {
|
resultHandler.onResult(accountManager.signIn(password)));
|
||||||
byte[] key = crypto.decryptWithPassword(encrypted, password);
|
|
||||||
if (key == null) {
|
|
||||||
resultHandler.onResult(false);
|
|
||||||
} else {
|
|
||||||
databaseConfig.setEncryptionKey(new SecretKey(key));
|
|
||||||
resultHandler.onResult(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void changePassword(String password, String newPassword,
|
public void changePassword(String oldPassword, String newPassword,
|
||||||
ResultHandler<Boolean> resultHandler) {
|
ResultHandler<Boolean> resultHandler) {
|
||||||
byte[] encrypted = getEncryptedKey();
|
ioExecutor.execute(() -> {
|
||||||
cryptoExecutor.execute(() -> {
|
boolean changed =
|
||||||
byte[] key = crypto.decryptWithPassword(encrypted, password);
|
accountManager.changePassword(oldPassword, newPassword);
|
||||||
if (key == null) {
|
resultHandler.onResult(changed);
|
||||||
resultHandler.onResult(false);
|
|
||||||
} else {
|
|
||||||
String hex =
|
|
||||||
encryptDatabaseKey(new SecretKey(key), newPassword);
|
|
||||||
resultHandler.onResult(storeEncryptedDatabaseKey(hex));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] getEncryptedKey() {
|
|
||||||
String hex = getEncryptedDatabaseKey();
|
|
||||||
if (hex == null)
|
|
||||||
throw new IllegalStateException("Encrypted database key is null");
|
|
||||||
return StringUtils.fromHexString(hex);
|
|
||||||
}
|
|
||||||
|
|
||||||
@CryptoExecutor
|
|
||||||
String encryptDatabaseKey(SecretKey key, String password) {
|
|
||||||
long start = now();
|
|
||||||
byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password);
|
|
||||||
logDuration(LOG, "Key derivation", start);
|
|
||||||
return StringUtils.toHexString(encrypted);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
@@ -25,6 +26,9 @@ public class SetupActivity extends BaseActivity
|
|||||||
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
|
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
|
||||||
private static final String STATE_KEY_PASSWORD = "password";
|
private static final String STATE_KEY_PASSWORD = "password";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AccountManager accountManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SetupController setupController;
|
SetupController setupController;
|
||||||
|
|
||||||
@@ -39,8 +43,7 @@ public class SetupActivity extends BaseActivity
|
|||||||
setContentView(R.layout.activity_fragment_container);
|
setContentView(R.layout.activity_fragment_container);
|
||||||
|
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
if (setupController.accountExists())
|
if (accountManager.accountExists()) throw new AssertionError();
|
||||||
throw new AssertionError();
|
|
||||||
showInitialFragment(AuthorNameFragment.newInstance());
|
showInitialFragment(AuthorNameFragment.newInstance());
|
||||||
} else {
|
} else {
|
||||||
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
|
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.crypto.CryptoExecutor;
|
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
@@ -28,12 +25,10 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
private volatile SetupActivity setupActivity;
|
private volatile SetupActivity setupActivity;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
SetupControllerImpl(SharedPreferences briarPrefs,
|
SetupControllerImpl(AccountManager accountManager,
|
||||||
DatabaseConfig databaseConfig,
|
@IoExecutor Executor ioExecutor,
|
||||||
@CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
|
|
||||||
PasswordStrengthEstimator strengthEstimator) {
|
PasswordStrengthEstimator strengthEstimator) {
|
||||||
super(briarPrefs, databaseConfig, cryptoExecutor, crypto,
|
super(accountManager, ioExecutor, strengthEstimator);
|
||||||
strengthEstimator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,10 +75,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
@Override
|
@Override
|
||||||
public void createAccount() {
|
public void createAccount() {
|
||||||
SetupActivity setupActivity = this.setupActivity;
|
SetupActivity setupActivity = this.setupActivity;
|
||||||
UiResultHandler<Void> resultHandler =
|
UiResultHandler<Boolean> resultHandler =
|
||||||
new UiResultHandler<Void>(setupActivity) {
|
new UiResultHandler<Boolean>(setupActivity) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Void result) {
|
public void onResultUi(Boolean result) {
|
||||||
|
// TODO: Show an error if result is false
|
||||||
if (setupActivity == null)
|
if (setupActivity == null)
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
setupActivity.showApp();
|
setupActivity.showApp();
|
||||||
@@ -93,22 +89,17 @@ public class SetupControllerImpl extends PasswordControllerImpl
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Package access for testing
|
// Package access for testing
|
||||||
void createAccount(ResultHandler<Void> resultHandler) {
|
void createAccount(ResultHandler<Boolean> resultHandler) {
|
||||||
SetupActivity setupActivity = this.setupActivity;
|
SetupActivity setupActivity = this.setupActivity;
|
||||||
if (setupActivity == null) throw new IllegalStateException();
|
if (setupActivity == null) throw new IllegalStateException();
|
||||||
String authorName = setupActivity.getAuthorName();
|
String authorName = setupActivity.getAuthorName();
|
||||||
if (authorName == null) throw new IllegalStateException();
|
if (authorName == null) throw new IllegalStateException();
|
||||||
String password = setupActivity.getPassword();
|
String password = setupActivity.getPassword();
|
||||||
if (password == null) throw new IllegalStateException();
|
if (password == null) throw new IllegalStateException();
|
||||||
cryptoExecutor.execute(() -> {
|
ioExecutor.execute(() -> {
|
||||||
LOG.info("Creating account");
|
LOG.info("Creating account");
|
||||||
databaseConfig.setLocalAuthorName(authorName);
|
resultHandler.onResult(accountManager.createAccount(authorName,
|
||||||
SecretKey key = crypto.generateSecretKey();
|
password));
|
||||||
databaseConfig.setEncryptionKey(key);
|
|
||||||
String hex = encryptDatabaseKey(key, password);
|
|
||||||
storeEncryptedDatabaseKey(hex);
|
|
||||||
resultHandler.onResult(null);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.AndroidComponent;
|
import org.briarproject.briar.android.AndroidComponent;
|
||||||
import org.briarproject.briar.android.BriarApplication;
|
import org.briarproject.briar.android.BriarApplication;
|
||||||
@@ -37,7 +37,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
|
|||||||
public static final String DISMISS_REMINDER = "dismissReminder";
|
public static final String DISMISS_REMINDER = "dismissReminder";
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
DatabaseConfig databaseConfig;
|
AccountManager accountManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context ctx, Intent intent) {
|
public void onReceive(Context ctx, Intent intent) {
|
||||||
@@ -51,7 +51,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
|
|||||||
if (action == null) return;
|
if (action == null) return;
|
||||||
if (action.equals(ACTION_BOOT_COMPLETED) ||
|
if (action.equals(ACTION_BOOT_COMPLETED) ||
|
||||||
action.equals(ACTION_MY_PACKAGE_REPLACED)) {
|
action.equals(ACTION_MY_PACKAGE_REPLACED)) {
|
||||||
if (databaseConfig.databaseExists()) {
|
if (accountManager.accountExists()) {
|
||||||
SharedPreferences prefs = app.getDefaultSharedPreferences();
|
SharedPreferences prefs = app.getDefaultSharedPreferences();
|
||||||
if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) {
|
if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) {
|
||||||
showSignInNotification(ctx);
|
showSignInNotification(ctx);
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.preference.PreferenceManager;
|
import android.support.v7.preference.PreferenceManager;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.controller.ConfigController;
|
|
||||||
import org.iilab.IilabEngineeringRSA2048Pin;
|
import org.iilab.IilabEngineeringRSA2048Pin;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -33,7 +33,7 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
Logger.getLogger(PanicResponderActivity.class.getName());
|
Logger.getLogger(PanicResponderActivity.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ConfigController configController;
|
protected AccountManager accountManager;
|
||||||
@Inject
|
@Inject
|
||||||
protected AndroidExecutor androidExecutor;
|
protected AndroidExecutor androidExecutor;
|
||||||
|
|
||||||
@@ -94,7 +94,7 @@ public class PanicResponderActivity extends BriarActivity {
|
|||||||
|
|
||||||
private void deleteAllData() {
|
private void deleteAllData() {
|
||||||
androidExecutor.runOnBackgroundThread(() -> {
|
androidExecutor.runOnBackgroundThread(() -> {
|
||||||
configController.deleteAccount(PanicResponderActivity.this);
|
accountManager.deleteAccount();
|
||||||
// TODO somehow delete/shred the database more thoroughly
|
// TODO somehow delete/shred the database more thoroughly
|
||||||
PanicResponder.deleteAllAppData(PanicResponderActivity.this);
|
PanicResponder.deleteAllAppData(PanicResponderActivity.this);
|
||||||
|
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import android.os.Handler;
|
|||||||
import android.support.v7.preference.PreferenceManager;
|
import android.support.v7.preference.PreferenceManager;
|
||||||
import android.transition.Fade;
|
import android.transition.Fade;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
import org.briarproject.briar.android.controller.ConfigController;
|
|
||||||
import org.briarproject.briar.android.login.OpenDatabaseActivity;
|
import org.briarproject.briar.android.login.OpenDatabaseActivity;
|
||||||
import org.briarproject.briar.android.login.SetupActivity;
|
import org.briarproject.briar.android.login.SetupActivity;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
Logger.getLogger(SplashScreenActivity.class.getName());
|
Logger.getLogger(SplashScreenActivity.class.getName());
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ConfigController configController;
|
protected AccountManager accountManager;
|
||||||
@Inject
|
@Inject
|
||||||
protected AndroidExecutor androidExecutor;
|
protected AndroidExecutor androidExecutor;
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
|
|
||||||
setContentView(R.layout.splash);
|
setContentView(R.layout.splash);
|
||||||
|
|
||||||
if (configController.accountSignedIn()) {
|
if (accountManager.hasDatabaseKey()) {
|
||||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||||
finish();
|
finish();
|
||||||
} else {
|
} else {
|
||||||
@@ -64,12 +64,12 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
LOG.info("Expired");
|
LOG.info("Expired");
|
||||||
startActivity(new Intent(this, ExpiredActivity.class));
|
startActivity(new Intent(this, ExpiredActivity.class));
|
||||||
} else {
|
} else {
|
||||||
if (configController.accountExists()) {
|
if (accountManager.accountExists()) {
|
||||||
LOG.info("Account exists");
|
LOG.info("Account exists");
|
||||||
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
startActivity(new Intent(this, OpenDatabaseActivity.class));
|
||||||
} else {
|
} else {
|
||||||
LOG.info("Account does not exist");
|
LOG.info("Account does not exist");
|
||||||
configController.deleteAccount(this);
|
accountManager.deleteAccount();
|
||||||
startActivity(new Intent(this, SetupActivity.class));
|
startActivity(new Intent(this, SetupActivity.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
package org.briarproject.briar.android;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
public class TestDatabaseKeyUtils {
|
|
||||||
|
|
||||||
public static void storeDatabaseKey(File f, String hex) throws IOException {
|
|
||||||
f.getParentFile().mkdirs();
|
|
||||||
FileOutputStream out = new FileOutputStream(f);
|
|
||||||
out.write(hex.getBytes("UTF-8"));
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static String loadDatabaseKey(File f) throws IOException {
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
|
||||||
new FileInputStream(f), "UTF-8"));
|
|
||||||
String hex = reader.readLine();
|
|
||||||
reader.close();
|
|
||||||
return hex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
package org.briarproject.briar.android.controller;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.SharedPreferences.Editor;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
|
||||||
import org.jmock.Expectations;
|
|
||||||
import org.junit.After;
|
|
||||||
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.assertNull;
|
|
||||||
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;
|
|
||||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
|
||||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
|
|
||||||
|
|
||||||
public class ConfigControllerImplTest extends BrambleMockTestCase {
|
|
||||||
|
|
||||||
private final SharedPreferences prefs =
|
|
||||||
context.mock(SharedPreferences.class);
|
|
||||||
private final DatabaseConfig databaseConfig =
|
|
||||||
context.mock(DatabaseConfig.class);
|
|
||||||
private final Editor editor = context.mock(Editor.class);
|
|
||||||
|
|
||||||
private final byte[] encryptedKey = getRandomBytes(123);
|
|
||||||
private final String encryptedKeyHex = toHexString(encryptedKey);
|
|
||||||
private final String oldEncryptedKeyHex = 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");
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDbKeyIsMigratedFromPreferencesToFile() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(prefs).getString("key", null);
|
|
||||||
will(returnValue(encryptedKeyHex));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
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());
|
|
||||||
|
|
||||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(prefs).getString("key", null);
|
|
||||||
will(returnValue(null));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
|
|
||||||
storeDatabaseKey(keyFile, encryptedKeyHex);
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
|
|
||||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDbKeyIsLoadedFromBackupFile() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(prefs).getString("key", null);
|
|
||||||
will(returnValue(null));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
|
|
||||||
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
|
|
||||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDbKeyIsNullIfNotFound() {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
oneOf(prefs).getString("key", null);
|
|
||||||
will(returnValue(null));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
|
|
||||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertNull(c.getEncryptedDatabaseKey());
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStoringDbKeyOverwritesPrimary() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
|
|
||||||
storeDatabaseKey(keyFile, oldEncryptedKeyHex);
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
|
|
||||||
ConfigController c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testStoringDbKeyOverwritesBackup() throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertFalse(keyBackupFile.exists());
|
|
||||||
|
|
||||||
storeDatabaseKey(keyBackupFile, oldEncryptedKeyHex);
|
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
|
|
||||||
ConfigController c = new ConfigControllerImpl(prefs,
|
|
||||||
databaseConfig);
|
|
||||||
|
|
||||||
assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
|
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
deleteTestDirectory(testDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,7 +45,7 @@ public class TestForumActivity extends ForumActivity {
|
|||||||
protected BriarController provideBriarController(
|
protected BriarController provideBriarController(
|
||||||
BriarControllerImpl briarController) {
|
BriarControllerImpl briarController) {
|
||||||
BriarController c = Mockito.mock(BriarController.class);
|
BriarController c = Mockito.mock(BriarController.class);
|
||||||
Mockito.when(c.hasEncryptionKey()).thenReturn(true);
|
Mockito.when(c.accountSignedIn()).thenReturn(true);
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,124 +1,58 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static junit.framework.Assert.assertFalse;
|
import static junit.framework.Assert.assertFalse;
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
|
||||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
|
||||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
|
|
||||||
|
|
||||||
public class PasswordControllerImplTest extends BrambleMockTestCase {
|
public class PasswordControllerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final SharedPreferences briarPrefs =
|
private final AccountManager accountManager =
|
||||||
context.mock(SharedPreferences.class);
|
context.mock(AccountManager.class);
|
||||||
private final DatabaseConfig databaseConfig =
|
|
||||||
context.mock(DatabaseConfig.class);
|
|
||||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
|
||||||
private final PasswordStrengthEstimator estimator =
|
private final PasswordStrengthEstimator estimator =
|
||||||
context.mock(PasswordStrengthEstimator.class);
|
context.mock(PasswordStrengthEstimator.class);
|
||||||
|
|
||||||
private final Executor cryptoExecutor = new ImmediateExecutor();
|
private final Executor ioExecutor = new ImmediateExecutor();
|
||||||
|
|
||||||
private final String oldPassword = "some.old.pass";
|
private final String oldPassword = getRandomString(10);
|
||||||
private final String newPassword = "some.new.pass";
|
private final String newPassword = getRandomString(10);
|
||||||
private final byte[] oldEncryptedKey = getRandomBytes(123);
|
|
||||||
private final byte[] newEncryptedKey = getRandomBytes(123);
|
|
||||||
private final byte[] key = getSecretKey().getBytes();
|
|
||||||
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");
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangePasswordReturnsTrue() throws Exception {
|
public void testChangePasswordReturnsTrue() {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Look up the encrypted DB key
|
oneOf(accountManager).changePassword(oldPassword, newPassword);
|
||||||
oneOf(briarPrefs).getString("key", null);
|
will(returnValue(true));
|
||||||
will(returnValue(null));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
// Decrypt and re-encrypt the key
|
|
||||||
oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
|
|
||||||
will(returnValue(key));
|
|
||||||
oneOf(crypto).encryptWithPassword(key, newPassword);
|
|
||||||
will(returnValue(newEncryptedKey));
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
|
||||||
assertFalse(keyBackupFile.exists());
|
ioExecutor, estimator);
|
||||||
|
|
||||||
storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
|
|
||||||
storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
|
|
||||||
|
|
||||||
PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
|
|
||||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
|
||||||
|
|
||||||
AtomicBoolean capturedResult = new AtomicBoolean(false);
|
AtomicBoolean capturedResult = new AtomicBoolean(false);
|
||||||
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
||||||
assertTrue(capturedResult.get());
|
assertTrue(capturedResult.get());
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(toHexString(newEncryptedKey), loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(toHexString(newEncryptedKey),
|
|
||||||
loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testChangePasswordReturnsFalseIfOldPasswordIsWrong()
|
public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() {
|
||||||
throws Exception {
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Look up the encrypted DB key
|
oneOf(accountManager).changePassword(oldPassword, newPassword);
|
||||||
oneOf(briarPrefs).getString("key", null);
|
will(returnValue(false));
|
||||||
will(returnValue(null));
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
// Try to decrypt the key - the password is wrong
|
|
||||||
oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
|
|
||||||
will(returnValue(null));
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
|
||||||
assertFalse(keyBackupFile.exists());
|
ioExecutor, estimator);
|
||||||
|
|
||||||
storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
|
|
||||||
storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
|
|
||||||
|
|
||||||
PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
|
|
||||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
|
||||||
|
|
||||||
AtomicBoolean capturedResult = new AtomicBoolean(true);
|
AtomicBoolean capturedResult = new AtomicBoolean(true);
|
||||||
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
||||||
assertFalse(capturedResult.get());
|
assertFalse(capturedResult.get());
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(toHexString(oldEncryptedKey), loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(toHexString(oldEncryptedKey),
|
|
||||||
loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
deleteTestDirectory(testDir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,32 @@
|
|||||||
package org.briarproject.briar.android.login;
|
package org.briarproject.briar.android.login;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import org.briarproject.bramble.api.account.AccountManager;
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
|
||||||
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
|
||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
|
||||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||||
import org.jmock.Expectations;
|
import org.jmock.Expectations;
|
||||||
import org.jmock.lib.legacy.ClassImposteriser;
|
import org.jmock.lib.legacy.ClassImposteriser;
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import static junit.framework.Assert.assertEquals;
|
|
||||||
import static junit.framework.Assert.assertFalse;
|
|
||||||
import static junit.framework.Assert.assertTrue;
|
import static junit.framework.Assert.assertTrue;
|
||||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
|
||||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
|
||||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
|
||||||
|
|
||||||
public class SetupControllerImplTest extends BrambleMockTestCase {
|
public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||||
|
|
||||||
private final SharedPreferences briarPrefs =
|
private final AccountManager accountManager =
|
||||||
context.mock(SharedPreferences.class);
|
context.mock(AccountManager.class);
|
||||||
private final DatabaseConfig databaseConfig =
|
|
||||||
context.mock(DatabaseConfig.class);
|
|
||||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
|
||||||
private final PasswordStrengthEstimator estimator =
|
private final PasswordStrengthEstimator estimator =
|
||||||
context.mock(PasswordStrengthEstimator.class);
|
context.mock(PasswordStrengthEstimator.class);
|
||||||
private final SetupActivity setupActivity;
|
private final SetupActivity setupActivity;
|
||||||
|
|
||||||
private final Executor cryptoExecutor = new ImmediateExecutor();
|
private final Executor ioExecutor = new ImmediateExecutor();
|
||||||
|
|
||||||
private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||||
private final String password = "some.strong.pass";
|
private final String password = getRandomString(10);
|
||||||
private final byte[] encryptedKey = getRandomBytes(123);
|
|
||||||
private final SecretKey key = getSecretKey();
|
|
||||||
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");
|
|
||||||
|
|
||||||
public SetupControllerImplTest() {
|
public SetupControllerImplTest() {
|
||||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||||
@@ -59,13 +35,8 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||||
public void testCreateAccount() throws Exception {
|
public void testCreateAccount() {
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Allow the contents of the data directory to be logged
|
|
||||||
allowing(setupActivity).getApplicationInfo();
|
|
||||||
will(returnValue(new ApplicationInfo() {{
|
|
||||||
dataDir = testDir.getAbsolutePath();
|
|
||||||
}}));
|
|
||||||
// Set the author name and password
|
// Set the author name and password
|
||||||
oneOf(setupActivity).setAuthorName(authorName);
|
oneOf(setupActivity).setAuthorName(authorName);
|
||||||
oneOf(setupActivity).setPassword(password);
|
oneOf(setupActivity).setPassword(password);
|
||||||
@@ -74,25 +45,13 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(authorName));
|
will(returnValue(authorName));
|
||||||
oneOf(setupActivity).getPassword();
|
oneOf(setupActivity).getPassword();
|
||||||
will(returnValue(password));
|
will(returnValue(password));
|
||||||
// Generate a database key
|
// Create the account
|
||||||
oneOf(crypto).generateSecretKey();
|
oneOf(accountManager).createAccount(authorName, password);
|
||||||
will(returnValue(key));
|
will(returnValue(true));
|
||||||
// Attach the author name and database key to the database config
|
|
||||||
oneOf(databaseConfig).setLocalAuthorName(authorName);
|
|
||||||
oneOf(databaseConfig).setEncryptionKey(key);
|
|
||||||
// Encrypt the key with the password
|
|
||||||
oneOf(crypto).encryptWithPassword(key.getBytes(), password);
|
|
||||||
will(returnValue(encryptedKey));
|
|
||||||
// Store the encrypted key
|
|
||||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
|
||||||
will(returnValue(keyDir));
|
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertFalse(keyFile.exists());
|
SetupControllerImpl s = new SetupControllerImpl(accountManager,
|
||||||
assertFalse(keyBackupFile.exists());
|
ioExecutor, estimator);
|
||||||
|
|
||||||
SetupControllerImpl s = new SetupControllerImpl(briarPrefs,
|
|
||||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
|
||||||
s.setSetupActivity(setupActivity);
|
s.setSetupActivity(setupActivity);
|
||||||
|
|
||||||
AtomicBoolean called = new AtomicBoolean(false);
|
AtomicBoolean called = new AtomicBoolean(false);
|
||||||
@@ -100,15 +59,5 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
|||||||
s.setPassword(password);
|
s.setPassword(password);
|
||||||
s.createAccount(result -> called.set(true));
|
s.createAccount(result -> called.set(true));
|
||||||
assertTrue(called.get());
|
assertTrue(called.get());
|
||||||
|
|
||||||
assertTrue(keyFile.exists());
|
|
||||||
assertTrue(keyBackupFile.exists());
|
|
||||||
assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyFile));
|
|
||||||
assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyBackupFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
deleteTestDirectory(testDir);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.feed;
|
package org.briarproject.briar.feed;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
import org.briarproject.bramble.crypto.CryptoExecutorModule;
|
||||||
@@ -25,6 +27,7 @@ import org.junit.Test;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -47,8 +50,12 @@ public class FeedManagerIntegrationTest extends BriarTestCase {
|
|||||||
component.inject(this);
|
component.inject(this);
|
||||||
injectEagerSingletons(component);
|
injectEagerSingletons(component);
|
||||||
|
|
||||||
|
IdentityManager identityManager = component.getIdentityManager();
|
||||||
|
LocalAuthor localAuthor = identityManager.createLocalAuthor("feedTest");
|
||||||
|
identityManager.registerLocalAuthor(localAuthor);
|
||||||
|
|
||||||
lifecycleManager = component.getLifecycleManager();
|
lifecycleManager = component.getLifecycleManager();
|
||||||
lifecycleManager.startServices("feedTest");
|
lifecycleManager.startServices(getSecretKey());
|
||||||
lifecycleManager.waitForStartup();
|
lifecycleManager.waitForStartup();
|
||||||
|
|
||||||
feedManager = component.getFeedManager();
|
feedManager = component.getFeedManager();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.feed;
|
package org.briarproject.briar.feed;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.identity.IdentityManager;
|
||||||
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
|
||||||
import org.briarproject.bramble.client.ClientModule;
|
import org.briarproject.bramble.client.ClientModule;
|
||||||
import org.briarproject.bramble.contact.ContactModule;
|
import org.briarproject.bramble.contact.ContactModule;
|
||||||
@@ -76,6 +77,8 @@ interface FeedManagerIntegrationTestComponent {
|
|||||||
|
|
||||||
void inject(VersioningModule.EagerSingletons init);
|
void inject(VersioningModule.EagerSingletons init);
|
||||||
|
|
||||||
|
IdentityManager getIdentityManager();
|
||||||
|
|
||||||
LifecycleManager getLifecycleManager();
|
LifecycleManager getLifecycleManager();
|
||||||
|
|
||||||
FeedManager getFeedManager();
|
FeedManager getFeedManager();
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ import java.io.InputStream;
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.test.TestPluginConfigModule.MAX_LATENCY;
|
import static org.briarproject.bramble.test.TestPluginConfigModule.MAX_LATENCY;
|
||||||
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
|
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
|
|
||||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -58,8 +57,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
private final File bobDir = new File(testDir, "bob");
|
private final File bobDir = new File(testDir, "bob");
|
||||||
private final SecretKey master = getSecretKey();
|
private final SecretKey master = getSecretKey();
|
||||||
private final long timestamp = System.currentTimeMillis();
|
private final long timestamp = System.currentTimeMillis();
|
||||||
private final LocalAuthor aliceAuthor = getLocalAuthor();
|
|
||||||
private final LocalAuthor bobAuthor = getLocalAuthor();
|
|
||||||
|
|
||||||
private SimplexMessagingIntegrationTestComponent alice, bob;
|
private SimplexMessagingIntegrationTestComponent alice, bob;
|
||||||
|
|
||||||
@@ -76,6 +73,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testWriteAndRead() throws Exception {
|
public void testWriteAndRead() throws Exception {
|
||||||
|
// Create the identities
|
||||||
|
LocalAuthor aliceAuthor =
|
||||||
|
alice.getIdentityManager().createLocalAuthor("Alice");
|
||||||
|
LocalAuthor bobAuthor =
|
||||||
|
bob.getIdentityManager().createLocalAuthor("Bob");
|
||||||
// Set up the devices and get the contact IDs
|
// Set up the devices and get the contact IDs
|
||||||
ContactId bobId = setUp(alice, aliceAuthor, bobAuthor, true);
|
ContactId bobId = setUp(alice, aliceAuthor, bobAuthor, true);
|
||||||
ContactId aliceId = setUp(bob, bobAuthor, aliceAuthor, false);
|
ContactId aliceId = setUp(bob, bobAuthor, aliceAuthor, false);
|
||||||
@@ -98,13 +100,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
|||||||
|
|
||||||
private ContactId setUp(SimplexMessagingIntegrationTestComponent device,
|
private ContactId setUp(SimplexMessagingIntegrationTestComponent device,
|
||||||
LocalAuthor local, Author remote, boolean alice) throws Exception {
|
LocalAuthor local, Author remote, boolean alice) throws Exception {
|
||||||
// Start the lifecycle manager
|
|
||||||
LifecycleManager lifecycleManager = device.getLifecycleManager();
|
|
||||||
lifecycleManager.startServices(null);
|
|
||||||
lifecycleManager.waitForStartup();
|
|
||||||
// Add an identity for the user
|
// Add an identity for the user
|
||||||
IdentityManager identityManager = device.getIdentityManager();
|
IdentityManager identityManager = device.getIdentityManager();
|
||||||
identityManager.registerLocalAuthor(local);
|
identityManager.registerLocalAuthor(local);
|
||||||
|
// Start the lifecycle manager
|
||||||
|
LifecycleManager lifecycleManager = device.getLifecycleManager();
|
||||||
|
lifecycleManager.startServices(getSecretKey());
|
||||||
|
lifecycleManager.waitForStartup();
|
||||||
// Add the other user as a contact
|
// Add the other user as a contact
|
||||||
ContactManager contactManager = device.getContactManager();
|
ContactManager contactManager = device.getContactManager();
|
||||||
return contactManager.addContact(remote, local.getId(), master,
|
return contactManager.addContact(remote, local.getId(), master,
|
||||||
|
|||||||
@@ -160,8 +160,8 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
validationWaiter = new Waiter();
|
validationWaiter = new Waiter();
|
||||||
deliveryWaiter = new Waiter();
|
deliveryWaiter = new Waiter();
|
||||||
|
|
||||||
|
createAndRegisterIdentities();
|
||||||
startLifecycles();
|
startLifecycles();
|
||||||
getDefaultIdentities();
|
|
||||||
listenToEvents();
|
listenToEvents();
|
||||||
addDefaultContacts();
|
addDefaultContacts();
|
||||||
}
|
}
|
||||||
@@ -193,9 +193,9 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
lifecycleManager0 = c0.getLifecycleManager();
|
lifecycleManager0 = c0.getLifecycleManager();
|
||||||
lifecycleManager1 = c1.getLifecycleManager();
|
lifecycleManager1 = c1.getLifecycleManager();
|
||||||
lifecycleManager2 = c2.getLifecycleManager();
|
lifecycleManager2 = c2.getLifecycleManager();
|
||||||
lifecycleManager0.startServices(AUTHOR0);
|
lifecycleManager0.startServices(getSecretKey());
|
||||||
lifecycleManager1.startServices(AUTHOR1);
|
lifecycleManager1.startServices(getSecretKey());
|
||||||
lifecycleManager2.startServices(AUTHOR2);
|
lifecycleManager2.startServices(getSecretKey());
|
||||||
lifecycleManager0.waitForStartup();
|
lifecycleManager0.waitForStartup();
|
||||||
lifecycleManager1.waitForStartup();
|
lifecycleManager1.waitForStartup();
|
||||||
lifecycleManager2.waitForStartup();
|
lifecycleManager2.waitForStartup();
|
||||||
@@ -230,10 +230,13 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getDefaultIdentities() throws DbException {
|
private void createAndRegisterIdentities() {
|
||||||
author0 = identityManager0.getLocalAuthor();
|
author0 = identityManager0.createLocalAuthor(AUTHOR0);
|
||||||
author1 = identityManager1.getLocalAuthor();
|
identityManager0.registerLocalAuthor(author0);
|
||||||
author2 = identityManager2.getLocalAuthor();
|
author1 = identityManager1.createLocalAuthor(AUTHOR1);
|
||||||
|
identityManager1.registerLocalAuthor(author1);
|
||||||
|
author2 = identityManager2.createLocalAuthor(AUTHOR2);
|
||||||
|
identityManager2.registerLocalAuthor(author2);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void addDefaultContacts() throws Exception {
|
protected void addDefaultContacts() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user