diff --git a/app/src/main/java/tech/relaycorp/ping/App.kt b/app/src/main/java/tech/relaycorp/ping/App.kt index 4223cbc0..45dcc8ab 100644 --- a/app/src/main/java/tech/relaycorp/ping/App.kt +++ b/app/src/main/java/tech/relaycorp/ping/App.kt @@ -8,6 +8,8 @@ import kotlinx.coroutines.launch import tech.relaycorp.awaladroid.Awala import tech.relaycorp.ping.common.di.AppComponent import tech.relaycorp.ping.common.di.DaggerAppComponent +import tech.relaycorp.ping.domain.BootstrapData +import javax.inject.Inject class App : Application() { @@ -18,6 +20,7 @@ class App : Application() { .appModule(AppModule(this)) .build() + @Inject lateinit var bootstrapData: BootstrapData override fun onCreate() { super.onCreate() @@ -25,6 +28,7 @@ class App : Application() { CoroutineScope(coroutineContext).launch { Awala.setup(this@App) + bootstrapData.bootstrapIfNeeded() } } } diff --git a/app/src/main/java/tech/relaycorp/ping/awala/AwalaModule.kt b/app/src/main/java/tech/relaycorp/ping/awala/AwalaModule.kt new file mode 100644 index 00000000..8cd31af6 --- /dev/null +++ b/app/src/main/java/tech/relaycorp/ping/awala/AwalaModule.kt @@ -0,0 +1,16 @@ +package tech.relaycorp.ping.awala + +import dagger.Module +import dagger.Provides +import tech.relaycorp.awaladroid.endpoint.FirstPartyEndpoint + +@Module +class AwalaModule { + + @Provides + fun firstPartyEndpointRegistration(): FirstPartyEndpointRegistration = + object : FirstPartyEndpointRegistration { + override suspend fun register(): FirstPartyEndpoint = + FirstPartyEndpoint.register() + } +} diff --git a/app/src/main/java/tech/relaycorp/ping/awala/FirstPartyEndpointRegistration.kt b/app/src/main/java/tech/relaycorp/ping/awala/FirstPartyEndpointRegistration.kt new file mode 100644 index 00000000..3f3a6e4f --- /dev/null +++ b/app/src/main/java/tech/relaycorp/ping/awala/FirstPartyEndpointRegistration.kt @@ -0,0 +1,7 @@ +package tech.relaycorp.ping.awala + +import tech.relaycorp.awaladroid.endpoint.FirstPartyEndpoint + +interface FirstPartyEndpointRegistration { + suspend fun register(): FirstPartyEndpoint +} diff --git a/app/src/main/java/tech/relaycorp/ping/common/di/AppComponent.kt b/app/src/main/java/tech/relaycorp/ping/common/di/AppComponent.kt index 02391c80..bc5b99b1 100644 --- a/app/src/main/java/tech/relaycorp/ping/common/di/AppComponent.kt +++ b/app/src/main/java/tech/relaycorp/ping/common/di/AppComponent.kt @@ -3,6 +3,7 @@ package tech.relaycorp.ping.common.di import dagger.Component import tech.relaycorp.ping.App import tech.relaycorp.ping.AppModule +import tech.relaycorp.ping.awala.AwalaModule import tech.relaycorp.ping.data.DataModule import tech.relaycorp.ping.ui.main.MainActivity import javax.inject.Singleton @@ -11,7 +12,8 @@ import javax.inject.Singleton @Component( modules = [ AppModule::class, - DataModule::class + DataModule::class, + AwalaModule::class ] ) interface AppComponent { diff --git a/app/src/main/java/tech/relaycorp/ping/data/DataModule.kt b/app/src/main/java/tech/relaycorp/ping/data/DataModule.kt index da9ba0ee..e3398759 100644 --- a/app/src/main/java/tech/relaycorp/ping/data/DataModule.kt +++ b/app/src/main/java/tech/relaycorp/ping/data/DataModule.kt @@ -3,13 +3,19 @@ package tech.relaycorp.ping.data import android.content.Context import androidx.room.Room import dagger.Module +import dagger.Provides import tech.relaycorp.ping.data.database.AppDatabase @Module class DataModule { + @Provides fun appDatabase(context: Context) = Room.databaseBuilder(context, AppDatabase::class.java, "ping").build() + @Provides fun pingDao(db: AppDatabase) = db.pingDao() + + @Provides + fun publicPeerDao(db: AppDatabase) = db.publicPeerDao() } diff --git a/app/src/main/java/tech/relaycorp/ping/domain/AddPublicPeer.kt b/app/src/main/java/tech/relaycorp/ping/domain/AddPublicPeer.kt new file mode 100644 index 00000000..b410af78 --- /dev/null +++ b/app/src/main/java/tech/relaycorp/ping/domain/AddPublicPeer.kt @@ -0,0 +1,23 @@ +package tech.relaycorp.ping.domain + +import tech.relaycorp.awaladroid.endpoint.PublicThirdPartyEndpoint +import tech.relaycorp.ping.data.database.dao.PublicPeerDao +import tech.relaycorp.ping.data.database.entity.PublicPeerEntity +import javax.inject.Inject + +class AddPublicPeer +@Inject constructor( + private val publicPeerDao: PublicPeerDao +) { + + suspend fun add(address: String, identity: ByteArray): PublicThirdPartyEndpoint { + val endpoint = PublicThirdPartyEndpoint.import(address, identity) + publicPeerDao.save( + PublicPeerEntity( + privateAddress = endpoint.privateAddress, + publicAddress = endpoint.publicAddress + ) + ) + return endpoint + } +} diff --git a/app/src/main/java/tech/relaycorp/ping/domain/BootstrapData.kt b/app/src/main/java/tech/relaycorp/ping/domain/BootstrapData.kt new file mode 100644 index 00000000..71bedf4f --- /dev/null +++ b/app/src/main/java/tech/relaycorp/ping/domain/BootstrapData.kt @@ -0,0 +1,32 @@ +package tech.relaycorp.ping.domain + +import android.content.res.Resources +import kotlinx.coroutines.flow.first +import tech.relaycorp.ping.R +import tech.relaycorp.ping.awala.FirstPartyEndpointRegistration +import tech.relaycorp.ping.data.preference.AppPreferences +import javax.inject.Inject + +class BootstrapData +@Inject constructor( + private val resources: Resources, + private val appPreferences: AppPreferences, + private val addPublicPeer: AddPublicPeer, + private val firstPartyEndpointRegistration: FirstPartyEndpointRegistration +) { + + suspend fun bootstrapIfNeeded() { + if (appPreferences.firstPartyEndpointAddress().first() != null) return + + importDefaultPublicPeer() + val endpoint = firstPartyEndpointRegistration.register() + appPreferences.setFirstPartyEndpointAddress(endpoint.privateAddress) + } + + private suspend fun importDefaultPublicPeer() { + addPublicPeer.add( + "ping.awala.services", + resources.openRawResource(R.raw.ping_awala_identity).use { it.readBytes() } + ) + } +} diff --git a/app/src/main/res/raw/identity.der b/app/src/main/res/raw/ping_awala_identity.der similarity index 100% rename from app/src/main/res/raw/identity.der rename to app/src/main/res/raw/ping_awala_identity.der diff --git a/app/src/test/java/tech/relaycorp/ping/domain/BootstrapDataTest.kt b/app/src/test/java/tech/relaycorp/ping/domain/BootstrapDataTest.kt new file mode 100644 index 00000000..02d5fc84 --- /dev/null +++ b/app/src/test/java/tech/relaycorp/ping/domain/BootstrapDataTest.kt @@ -0,0 +1,48 @@ +package tech.relaycorp.ping.domain + +import android.content.res.Resources +import com.nhaarman.mockitokotlin2.* +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runBlockingTest +import org.junit.Test +import tech.relaycorp.awaladroid.endpoint.FirstPartyEndpoint +import tech.relaycorp.ping.awala.FirstPartyEndpointRegistration +import tech.relaycorp.ping.data.preference.AppPreferences + +class BootstrapDataTest { + + private val resources = mock() + private val appPreferences = mock() + private val addPublicPeer = mock() + private val registerFirstPartyEndpoint = mock() + + private val subject = + BootstrapData(resources, appPreferences, addPublicPeer, registerFirstPartyEndpoint) + + @Test + fun bootstrap_ifNotNeeded() = runBlockingTest { + whenever(appPreferences.firstPartyEndpointAddress()).thenReturn(flowOf("123456")) + + subject.bootstrapIfNeeded() + + verifyZeroInteractions(registerFirstPartyEndpoint) + verifyZeroInteractions(addPublicPeer) + verify(appPreferences, never()).setFirstPartyEndpointAddress(any()) + } + + @Test + fun bootstrap_ifNeeded() = runBlockingTest { + whenever(appPreferences.firstPartyEndpointAddress()).thenReturn(flowOf(null)) + val firstPartyEndpoint = mock() + val firstPartyAddress = "123456" + whenever(firstPartyEndpoint.privateAddress).thenReturn(firstPartyAddress) + whenever(registerFirstPartyEndpoint.register()).thenReturn(firstPartyEndpoint) + whenever(resources.openRawResource(any())).thenReturn(ByteArray(0).inputStream()) + + subject.bootstrapIfNeeded() + + verify(addPublicPeer).add(eq("ping.awala.services"), any()) + verify(registerFirstPartyEndpoint).register() + verify(appPreferences).setFirstPartyEndpointAddress(eq(firstPartyAddress)) + } +}