Add support for PNIs in storage service.

This commit is contained in:
Greyson Parrelli
2022-08-17 11:26:47 -04:00
committed by Cody Henthorne
parent cb057968ee
commit 95fc9d6c3c
12 changed files with 584 additions and 90 deletions

View File

@@ -0,0 +1,318 @@
package org.thoughtcrime.securesms.storage
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.BeforeClass
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockedStatic
import org.mockito.internal.configuration.plugins.Plugins
import org.mockito.internal.junit.JUnitRule
import org.mockito.junit.MockitoRule
import org.mockito.quality.Strictness
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.testutil.EmptyLogger
import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.PNI
import org.whispersystems.signalservice.api.storage.SignalContactRecord
import org.whispersystems.signalservice.api.storage.StorageId
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord
import java.util.UUID
class ContactRecordProcessorTest {
@Rule
@JvmField
val mockitoRule: MockitoRule = JUnitRule(Plugins.getMockitoLogger(), Strictness.STRICT_STUBS)
@Mock
lateinit var recipientDatabase: RecipientDatabase
@Mock
lateinit var featureFlags: MockedStatic<FeatureFlags>
@Test
fun `isInvalid, normal, false`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceId(ACI_B.toString())
setServicePni(PNI_B.toString())
setServiceE164(E164_B)
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertFalse(result)
}
@Test
fun `isInvalid, missing serviceId, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceE164(E164_B)
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, e164 matches self, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceId(ACI_B.toString())
setServiceE164(E164_A)
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, aci matches self, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceId(ACI_A.toString())
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, pni matches self as serviceId, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceId(PNI_A.toString())
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, pni matches self as pni, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
val record = buildRecord {
setServiceId(ACI_B.toString())
setServicePni(PNI_A.toString())
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, pniOnly pnpDisabled, true`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(false)
val record = buildRecord {
setServiceId(PNI_B.toString())
setServicePni(PNI_B.toString())
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertTrue(result)
}
@Test
fun `isInvalid, pniOnly pnpEnabled, false`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(true)
val record = buildRecord {
setServiceId(PNI_B.toString())
setServicePni(PNI_B.toString())
}
// WHEN
val result = subject.isInvalid(record)
// THEN
assertFalse(result)
}
@Test
fun `merge, e164MatchesButPnisDont pnpEnabled, keepLocal`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(true)
val local = buildRecord(STORAGE_ID_A) {
setServiceId(ACI_A.toString())
setServiceE164(E164_A)
setServicePni(PNI_A.toString())
}
val remote = buildRecord(STORAGE_ID_B) {
setServiceId(ACI_A.toString())
setServiceE164(E164_A)
setServicePni(PNI_B.toString())
}
// WHEN
val result = subject.merge(remote, local, TestKeyGenerator(STORAGE_ID_C))
// THEN
assertEquals(local.serviceId, result.serviceId)
assertEquals(local.number.get(), result.number.get())
assertEquals(local.pni.get(), result.pni.get())
}
@Test
fun `merge, pnisMatchButE164sDont pnpEnabled, keepLocal`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(true)
val local = buildRecord(STORAGE_ID_A) {
setServiceId(ACI_A.toString())
setServiceE164(E164_A)
setServicePni(PNI_A.toString())
}
val remote = buildRecord(STORAGE_ID_B) {
setServiceId(ACI_A.toString())
setServiceE164(E164_B)
setServicePni(PNI_A.toString())
}
// WHEN
val result = subject.merge(remote, local, TestKeyGenerator(STORAGE_ID_C))
// THEN
assertEquals(local.serviceId, result.serviceId)
assertEquals(local.number.get(), result.number.get())
assertEquals(local.pni.get(), result.pni.get())
}
@Test
fun `merge, e164AndPniChange pnpEnabled, useRemote`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(true)
val local = buildRecord(STORAGE_ID_A) {
setServiceId(ACI_A.toString())
setServiceE164(E164_A)
setServicePni(PNI_A.toString())
}
val remote = buildRecord(STORAGE_ID_B) {
setServiceId(ACI_A.toString())
setServiceE164(E164_B)
setServicePni(PNI_B.toString())
}
// WHEN
val result = subject.merge(remote, local, TestKeyGenerator(STORAGE_ID_C))
// THEN
assertEquals(remote.serviceId, result.serviceId)
assertEquals(remote.number.get(), result.number.get())
assertEquals(remote.pni.get(), result.pni.get())
}
@Test
fun `merge, pnpDisabled, pniDropped`() {
// GIVEN
val subject = ContactRecordProcessor(ACI_A, PNI_A, E164_A, recipientDatabase)
featureFlags.`when`<Boolean> { FeatureFlags.phoneNumberPrivacy() }.thenReturn(false)
val local = buildRecord(STORAGE_ID_A) {
setServiceId(ACI_A.toString())
setServiceE164(E164_A)
setServicePni(PNI_A.toString())
}
val remote = buildRecord(STORAGE_ID_B) {
setServiceId(ACI_A.toString())
setServiceE164(E164_B)
setServicePni(PNI_B.toString())
}
// WHEN
val result = subject.merge(remote, local, TestKeyGenerator(STORAGE_ID_C))
// THEN
assertEquals(remote.serviceId, result.serviceId)
assertEquals(remote.number.get(), result.number.get())
assertEquals(false, result.pni.isPresent)
}
private fun buildRecord(id: StorageId = STORAGE_ID_A, applyParams: ContactRecord.Builder.() -> ContactRecord.Builder): SignalContactRecord {
return SignalContactRecord(id, ContactRecord.getDefaultInstance().toBuilder().applyParams().build())
}
private class TestKeyGenerator(private val value: StorageId) : StorageKeyGenerator {
override fun generate(): ByteArray {
return value.raw
}
}
companion object {
val STORAGE_ID_A: StorageId = StorageId.forStoryDistributionList(byteArrayOf(1, 2, 3, 4))
val STORAGE_ID_B: StorageId = StorageId.forStoryDistributionList(byteArrayOf(5, 6, 7, 8))
val STORAGE_ID_C: StorageId = StorageId.forStoryDistributionList(byteArrayOf(5, 6, 7, 8))
val ACI_A = ACI.from(UUID.fromString("3436efbe-5a76-47fa-a98a-7e72c948a82e"))
val ACI_B = ACI.from(UUID.fromString("8de7f691-0b60-4a68-9cd9-ed2f8453f9ed"))
val PNI_A = PNI.from(UUID.fromString("154b8d92-c960-4f6c-8385-671ad2ffb999"))
val PNI_B = PNI.from(UUID.fromString("ba92b1fb-cd55-40bf-adda-c35a85375533"))
const val E164_A = "+12221234567"
const val E164_B = "+13331234567"
@JvmStatic
@BeforeClass
fun setUpClass() {
Log.initialize(EmptyLogger())
}
}
}

View File

@@ -176,7 +176,8 @@ public final class StorageSyncHelperTest {
String e164,
String profileName)
{
return new SignalContactRecord.Builder(byteArray(key), new SignalServiceAddress(aci, e164), null)
return new SignalContactRecord.Builder(byteArray(key), aci, null)
.setE164(e164)
.setGivenName(profileName);
}