mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-21 19:48:29 +00:00
Sync PNI verification status to storage service.
This commit is contained in:
committed by
Nicholas Tinsley
parent
459607adae
commit
716afc98ac
@@ -18,6 +18,7 @@ import org.signal.core.util.Base64
|
|||||||
import org.signal.core.util.SqlUtil
|
import org.signal.core.util.SqlUtil
|
||||||
import org.signal.core.util.exists
|
import org.signal.core.util.exists
|
||||||
import org.signal.core.util.orNull
|
import org.signal.core.util.orNull
|
||||||
|
import org.signal.core.util.readToSingleBoolean
|
||||||
import org.signal.core.util.requireLong
|
import org.signal.core.util.requireLong
|
||||||
import org.signal.core.util.requireNonNullString
|
import org.signal.core.util.requireNonNullString
|
||||||
import org.signal.core.util.select
|
import org.signal.core.util.select
|
||||||
@@ -109,6 +110,18 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
val record = SignalDatabase.recipients.getRecord(id)
|
val record = SignalDatabase.recipients.getRecord(id)
|
||||||
assertEquals(RecipientTable.RegisteredState.REGISTERED, record.registered)
|
assertEquals(RecipientTable.RegisteredState.REGISTERED, record.registered)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("e164+pni+aci insert, pni verified") {
|
||||||
|
val id = process(E164_A, PNI_A, ACI_A, pniVerified = true)
|
||||||
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
expectPniVerified()
|
||||||
|
|
||||||
|
val record = SignalDatabase.recipients.getRecord(id)
|
||||||
|
assertEquals(RecipientTable.RegisteredState.REGISTERED, record.registered)
|
||||||
|
|
||||||
|
process(E164_A, PNI_A, ACI_A, pniVerified = false)
|
||||||
|
expectPniVerified()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -164,6 +177,7 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
expect(E164_A, PNI_A, ACI_A)
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
expectNoSessionSwitchoverEvent()
|
expectNoSessionSwitchoverEvent()
|
||||||
|
expectPniVerified()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("no match, all fields") {
|
test("no match, all fields") {
|
||||||
@@ -225,6 +239,8 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
given(E164_A, PNI_A, null, pniSession = true)
|
given(E164_A, PNI_A, null, pniSession = true)
|
||||||
process(E164_A, PNI_A, ACI_A, pniVerified = true)
|
process(E164_A, PNI_A, ACI_A, pniVerified = true)
|
||||||
expect(E164_A, PNI_A, ACI_A)
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectPniVerified()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("e164 and aci matches, all provided, new pni") {
|
test("e164 and aci matches, all provided, new pni") {
|
||||||
@@ -694,6 +710,8 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
|
|
||||||
expectDeleted()
|
expectDeleted()
|
||||||
expect(E164_A, PNI_A, ACI_A)
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
|
expectPniVerified()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("merge, e164+pni & aci, pni session, pni verified") {
|
test("merge, e164+pni & aci, pni session, pni verified") {
|
||||||
@@ -706,6 +724,7 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
expect(E164_A, PNI_A, ACI_A)
|
expect(E164_A, PNI_A, ACI_A)
|
||||||
|
|
||||||
expectThreadMergeEvent(E164_A)
|
expectThreadMergeEvent(E164_A)
|
||||||
|
expectPniVerified()
|
||||||
}
|
}
|
||||||
|
|
||||||
test("merge, e164+pni & e164+pni+aci, change number") {
|
test("merge, e164+pni & e164+pni+aci, change number") {
|
||||||
@@ -1037,6 +1056,10 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
if (!test.sessionSwitchoverExpected) {
|
if (!test.sessionSwitchoverExpected) {
|
||||||
test.expectNoSessionSwitchoverEvent()
|
test.expectNoSessionSwitchoverEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!test.pniVerifiedExpected) {
|
||||||
|
test.expectPniNotVerified()
|
||||||
|
}
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
if (e.javaClass != exception) {
|
if (e.javaClass != exception) {
|
||||||
val error = java.lang.AssertionError("[$name] ${e.message}")
|
val error = java.lang.AssertionError("[$name] ${e.message}")
|
||||||
@@ -1056,6 +1079,7 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
var changeNumberExpected = false
|
var changeNumberExpected = false
|
||||||
var threadMergeExpected = false
|
var threadMergeExpected = false
|
||||||
var sessionSwitchoverExpected = false
|
var sessionSwitchoverExpected = false
|
||||||
|
var pniVerifiedExpected = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// Need to delete these first to prevent foreign key crash
|
// Need to delete these first to prevent foreign key crash
|
||||||
@@ -1207,6 +1231,24 @@ class RecipientTableTest_getAndPossiblyMerge {
|
|||||||
assertNull("Unexpected thread merge event!", getLatestThreadMergeEvent(outputRecipientId))
|
assertNull("Unexpected thread merge event!", getLatestThreadMergeEvent(outputRecipientId))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun expectPniVerified() {
|
||||||
|
assertTrue("Expected PNI to be verified!", isPniVerified(outputRecipientId))
|
||||||
|
pniVerifiedExpected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expectPniNotVerified() {
|
||||||
|
assertFalse("Expected PNI to be not be verified!", isPniVerified(outputRecipientId))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isPniVerified(recipientId: RecipientId): Boolean {
|
||||||
|
return SignalDatabase.rawDatabase
|
||||||
|
.select(RecipientTable.PNI_SIGNATURE_VERIFIED)
|
||||||
|
.from(RecipientTable.TABLE_NAME)
|
||||||
|
.where("${RecipientTable.ID} = ?", recipientId)
|
||||||
|
.run()
|
||||||
|
.readToSingleBoolean(false)
|
||||||
|
}
|
||||||
|
|
||||||
private fun insert(e164: String?, pni: PNI?, aci: ACI?): RecipientId {
|
private fun insert(e164: String?, pni: PNI?, aci: ACI?): RecipientId {
|
||||||
val id: Long = SignalDatabase.rawDatabase.insert(
|
val id: Long = SignalDatabase.rawDatabase.insert(
|
||||||
RecipientTable.TABLE_NAME,
|
RecipientTable.TABLE_NAME,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import org.signal.core.util.requireLong
|
|||||||
import org.signal.core.util.requireNonNullString
|
import org.signal.core.util.requireNonNullString
|
||||||
import org.signal.core.util.requireString
|
import org.signal.core.util.requireString
|
||||||
import org.signal.core.util.select
|
import org.signal.core.util.select
|
||||||
|
import org.signal.core.util.toInt
|
||||||
import org.signal.core.util.update
|
import org.signal.core.util.update
|
||||||
import org.signal.core.util.updateAll
|
import org.signal.core.util.updateAll
|
||||||
import org.signal.core.util.withinTransaction
|
import org.signal.core.util.withinTransaction
|
||||||
@@ -183,6 +184,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
const val REPORTING_TOKEN = "reporting_token"
|
const val REPORTING_TOKEN = "reporting_token"
|
||||||
const val PHONE_NUMBER_SHARING = "phone_number_sharing"
|
const val PHONE_NUMBER_SHARING = "phone_number_sharing"
|
||||||
const val PHONE_NUMBER_DISCOVERABLE = "phone_number_discoverable"
|
const val PHONE_NUMBER_DISCOVERABLE = "phone_number_discoverable"
|
||||||
|
const val PNI_SIGNATURE_VERIFIED = "pni_signature_verified"
|
||||||
|
|
||||||
const val SEARCH_PROFILE_NAME = "search_signal_profile"
|
const val SEARCH_PROFILE_NAME = "search_signal_profile"
|
||||||
const val SORT_NAME = "sort_name"
|
const val SORT_NAME = "sort_name"
|
||||||
@@ -250,7 +252,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
$NEEDS_PNI_SIGNATURE INTEGER DEFAULT 0,
|
$NEEDS_PNI_SIGNATURE INTEGER DEFAULT 0,
|
||||||
$REPORTING_TOKEN BLOB DEFAULT NULL,
|
$REPORTING_TOKEN BLOB DEFAULT NULL,
|
||||||
$PHONE_NUMBER_SHARING INTEGER DEFAULT ${PhoneNumberSharingState.UNKNOWN.id},
|
$PHONE_NUMBER_SHARING INTEGER DEFAULT ${PhoneNumberSharingState.UNKNOWN.id},
|
||||||
$PHONE_NUMBER_DISCOVERABLE INTEGER DEFAULT ${PhoneNumberDiscoverableState.UNKNOWN.id}
|
$PHONE_NUMBER_DISCOVERABLE INTEGER DEFAULT ${PhoneNumberDiscoverableState.UNKNOWN.id},
|
||||||
|
$PNI_SIGNATURE_VERIFIED INTEGER DEFAULT 0
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -829,7 +832,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
val recipientId: RecipientId
|
val recipientId: RecipientId
|
||||||
if (id < 0) {
|
if (id < 0) {
|
||||||
Log.w(TAG, "[applyStorageSyncContactInsert] Failed to insert. Possibly merging.")
|
Log.w(TAG, "[applyStorageSyncContactInsert] Failed to insert. Possibly merging.")
|
||||||
recipientId = getAndPossiblyMergePnpVerified(insert.aci.orNull(), insert.pni.orNull(), insert.number.orNull())
|
recipientId = getAndPossiblyMerge(aci = insert.aci.orNull(), pni = insert.pni.orNull(), e164 = insert.number.orNull(), pniVerified = insert.isPniSignatureVerified)
|
||||||
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(recipientId))
|
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(recipientId))
|
||||||
} else {
|
} else {
|
||||||
recipientId = RecipientId.from(id)
|
recipientId = RecipientId.from(id)
|
||||||
@@ -867,7 +870,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
var recipientId = getByColumn(STORAGE_SERVICE_ID, Base64.encodeWithPadding(update.old.id.raw)).get()
|
var recipientId = getByColumn(STORAGE_SERVICE_ID, Base64.encodeWithPadding(update.old.id.raw)).get()
|
||||||
|
|
||||||
Log.w(TAG, "[applyStorageSyncContactUpdate] Found user $recipientId. Possibly merging.")
|
Log.w(TAG, "[applyStorageSyncContactUpdate] Found user $recipientId. Possibly merging.")
|
||||||
recipientId = getAndPossiblyMergePnpVerified(update.new.aci.orElse(null), update.new.pni.orElse(null), update.new.number.orElse(null))
|
recipientId = getAndPossiblyMerge(aci = update.new.aci.orElse(null), pni = update.new.pni.orElse(null), e164 = update.new.number.orElse(null), pniVerified = update.new.isPniSignatureVerified)
|
||||||
|
|
||||||
Log.w(TAG, "[applyStorageSyncContactUpdate] Merged into $recipientId")
|
Log.w(TAG, "[applyStorageSyncContactUpdate] Merged into $recipientId")
|
||||||
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(recipientId))
|
db.update(TABLE_NAME, values, ID_WHERE, SqlUtil.buildArgs(recipientId))
|
||||||
@@ -1113,6 +1116,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
SYSTEM_NICKNAME,
|
SYSTEM_NICKNAME,
|
||||||
"$TABLE_NAME.$STORAGE_SERVICE_PROTO",
|
"$TABLE_NAME.$STORAGE_SERVICE_PROTO",
|
||||||
"$TABLE_NAME.$UNREGISTERED_TIMESTAMP",
|
"$TABLE_NAME.$UNREGISTERED_TIMESTAMP",
|
||||||
|
"$TABLE_NAME.$PNI_SIGNATURE_VERIFIED",
|
||||||
"${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY}",
|
"${GroupTable.TABLE_NAME}.${GroupTable.V2_MASTER_KEY}",
|
||||||
"${ThreadTable.TABLE_NAME}.${ThreadTable.ARCHIVED}",
|
"${ThreadTable.TABLE_NAME}.${ThreadTable.ARCHIVED}",
|
||||||
"${ThreadTable.TABLE_NAME}.${ThreadTable.READ}",
|
"${ThreadTable.TABLE_NAME}.${ThreadTable.READ}",
|
||||||
@@ -2372,7 +2376,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val finalId: RecipientId = writePnpChangeSetToDisk(changeSet, pni)
|
val finalId: RecipientId = writePnpChangeSetToDisk(changeSet, pni, pniVerified)
|
||||||
|
|
||||||
return ProcessPnpTupleResult(
|
return ProcessPnpTupleResult(
|
||||||
finalId = finalId,
|
finalId = finalId,
|
||||||
@@ -2386,7 +2390,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, inputPni: PNI?): RecipientId {
|
fun writePnpChangeSetToDisk(changeSet: PnpChangeSet, inputPni: PNI?, pniVerified: Boolean): RecipientId {
|
||||||
var hadThreadMerge = false
|
var hadThreadMerge = false
|
||||||
for (operation in changeSet.operations) {
|
for (operation in changeSet.operations) {
|
||||||
@Exhaustive
|
@Exhaustive
|
||||||
@@ -2402,7 +2406,10 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
is PnpOperation.RemovePni -> {
|
is PnpOperation.RemovePni -> {
|
||||||
writableDatabase
|
writableDatabase
|
||||||
.update(TABLE_NAME)
|
.update(TABLE_NAME)
|
||||||
.values(PNI_COLUMN to null)
|
.values(
|
||||||
|
PNI_COLUMN to null,
|
||||||
|
PNI_SIGNATURE_VERIFIED to 0
|
||||||
|
)
|
||||||
.where("$ID = ?", operation.recipientId)
|
.where("$ID = ?", operation.recipientId)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
@@ -2413,7 +2420,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
.values(
|
.values(
|
||||||
ACI_COLUMN to operation.aci.toString(),
|
ACI_COLUMN to operation.aci.toString(),
|
||||||
REGISTERED to RegisteredState.REGISTERED.id,
|
REGISTERED to RegisteredState.REGISTERED.id,
|
||||||
UNREGISTERED_TIMESTAMP to 0
|
UNREGISTERED_TIMESTAMP to 0,
|
||||||
|
PNI_SIGNATURE_VERIFIED to pniVerified.toInt()
|
||||||
)
|
)
|
||||||
.where("$ID = ?", operation.recipientId)
|
.where("$ID = ?", operation.recipientId)
|
||||||
.run()
|
.run()
|
||||||
@@ -2433,14 +2441,15 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
.values(
|
.values(
|
||||||
PNI_COLUMN to operation.pni.toString(),
|
PNI_COLUMN to operation.pni.toString(),
|
||||||
REGISTERED to RegisteredState.REGISTERED.id,
|
REGISTERED to RegisteredState.REGISTERED.id,
|
||||||
UNREGISTERED_TIMESTAMP to 0
|
UNREGISTERED_TIMESTAMP to 0,
|
||||||
|
PNI_SIGNATURE_VERIFIED to 0
|
||||||
)
|
)
|
||||||
.where("$ID = ?", operation.recipientId)
|
.where("$ID = ?", operation.recipientId)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
is PnpOperation.Merge -> {
|
is PnpOperation.Merge -> {
|
||||||
val mergeResult: MergeResult = merge(operation.primaryId, operation.secondaryId, inputPni)
|
val mergeResult: MergeResult = merge(operation.primaryId, operation.secondaryId, inputPni, pniVerified)
|
||||||
hadThreadMerge = hadThreadMerge || mergeResult.neededThreadMerge
|
hadThreadMerge = hadThreadMerge || mergeResult.neededThreadMerge
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2519,7 +2528,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
}
|
}
|
||||||
|
|
||||||
is PnpIdResolver.PnpInsert -> {
|
is PnpIdResolver.PnpInsert -> {
|
||||||
val id: Long = writableDatabase.insert(TABLE_NAME, null, buildContentValuesForNewUser(changeSet.id.e164, changeSet.id.pni, changeSet.id.aci))
|
val id: Long = writableDatabase.insert(TABLE_NAME, null, buildContentValuesForNewUser(changeSet.id.e164, changeSet.id.pni, changeSet.id.aci, pniVerified))
|
||||||
RecipientId.from(id)
|
RecipientId.from(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3843,7 +3852,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
* Merges one ACI recipient with an E164 recipient. It is assumed that the E164 recipient does
|
* Merges one ACI recipient with an E164 recipient. It is assumed that the E164 recipient does
|
||||||
* *not* have an ACI.
|
* *not* have an ACI.
|
||||||
*/
|
*/
|
||||||
private fun merge(primaryId: RecipientId, secondaryId: RecipientId, newPni: PNI? = null): MergeResult {
|
private fun merge(primaryId: RecipientId, secondaryId: RecipientId, newPni: PNI? = null, pniVerified: Boolean): MergeResult {
|
||||||
ensureInTransaction()
|
ensureInTransaction()
|
||||||
val db = writableDatabase
|
val db = writableDatabase
|
||||||
val primaryRecord = getRecord(primaryId)
|
val primaryRecord = getRecord(primaryId)
|
||||||
@@ -3904,7 +3913,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
SYSTEM_CONTACT_URI to secondaryRecord.systemContactUri,
|
SYSTEM_CONTACT_URI to secondaryRecord.systemContactUri,
|
||||||
PROFILE_SHARING to (primaryRecord.profileSharing || secondaryRecord.profileSharing),
|
PROFILE_SHARING to (primaryRecord.profileSharing || secondaryRecord.profileSharing),
|
||||||
CAPABILITIES to max(primaryRecord.capabilities.rawBits, secondaryRecord.capabilities.rawBits),
|
CAPABILITIES to max(primaryRecord.capabilities.rawBits, secondaryRecord.capabilities.rawBits),
|
||||||
MENTION_SETTING to if (primaryRecord.mentionSetting != MentionSetting.ALWAYS_NOTIFY) primaryRecord.mentionSetting.id else secondaryRecord.mentionSetting.id
|
MENTION_SETTING to if (primaryRecord.mentionSetting != MentionSetting.ALWAYS_NOTIFY) primaryRecord.mentionSetting.id else secondaryRecord.mentionSetting.id,
|
||||||
|
PNI_SIGNATURE_VERIFIED to pniVerified.toInt()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (primaryRecord.profileSharing || secondaryRecord.profileSharing) {
|
if (primaryRecord.profileSharing || secondaryRecord.profileSharing) {
|
||||||
@@ -3929,13 +3939,14 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
check(writableDatabase.inTransaction()) { "Must be in a transaction!" }
|
check(writableDatabase.inTransaction()) { "Must be in a transaction!" }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildContentValuesForNewUser(e164: String?, pni: PNI?, aci: ACI?): ContentValues {
|
private fun buildContentValuesForNewUser(e164: String?, pni: PNI?, aci: ACI?, pniVerified: Boolean): ContentValues {
|
||||||
check(e164 != null || pni != null || aci != null) { "Must provide some sort of identifier!" }
|
check(e164 != null || pni != null || aci != null) { "Must provide some sort of identifier!" }
|
||||||
|
|
||||||
val values = contentValuesOf(
|
val values = contentValuesOf(
|
||||||
E164 to e164,
|
E164 to e164,
|
||||||
ACI_COLUMN to aci?.toString(),
|
ACI_COLUMN to aci?.toString(),
|
||||||
PNI_COLUMN to pni?.toString(),
|
PNI_COLUMN to pni?.toString(),
|
||||||
|
PNI_SIGNATURE_VERIFIED to pniVerified.toInt(),
|
||||||
STORAGE_SERVICE_ID to Base64.encodeWithPadding(StorageSyncHelper.generateKey()),
|
STORAGE_SERVICE_ID to Base64.encodeWithPadding(StorageSyncHelper.generateKey()),
|
||||||
AVATAR_COLOR to AvatarColorHash.forAddress((aci ?: pni)?.toString(), e164).serialize()
|
AVATAR_COLOR to AvatarColorHash.forAddress((aci ?: pni)?.toString(), e164).serialize()
|
||||||
)
|
)
|
||||||
@@ -3971,6 +3982,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
|||||||
put(MUTE_UNTIL, contact.muteUntil)
|
put(MUTE_UNTIL, contact.muteUntil)
|
||||||
put(STORAGE_SERVICE_ID, Base64.encodeWithPadding(contact.id.raw))
|
put(STORAGE_SERVICE_ID, Base64.encodeWithPadding(contact.id.raw))
|
||||||
put(HIDDEN, contact.isHidden)
|
put(HIDDEN, contact.isHidden)
|
||||||
|
put(PNI_SIGNATURE_VERIFIED, contact.isPniSignatureVerified.toInt())
|
||||||
|
|
||||||
if (contact.hasUnknownFields()) {
|
if (contact.hasUnknownFields()) {
|
||||||
put(STORAGE_SERVICE_PROTO, Base64.encodeWithPadding(Objects.requireNonNull(contact.serializeUnknownFields())))
|
put(STORAGE_SERVICE_PROTO, Base64.encodeWithPadding(Objects.requireNonNull(contact.serializeUnknownFields())))
|
||||||
|
|||||||
@@ -209,25 +209,16 @@ object RecipientTableCursorUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getSyncExtras(cursor: Cursor): RecipientRecord.SyncExtras {
|
fun getSyncExtras(cursor: Cursor): RecipientRecord.SyncExtras {
|
||||||
val storageProtoRaw = cursor.optionalString(RecipientTable.STORAGE_SERVICE_PROTO).orElse(null)
|
|
||||||
val storageProto = if (storageProtoRaw != null) Base64.decodeOrThrow(storageProtoRaw) else null
|
|
||||||
val archived = cursor.optionalBoolean(ThreadTable.ARCHIVED).orElse(false)
|
|
||||||
val forcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.FORCED_UNREAD.serialize() }.orElse(false)
|
|
||||||
val groupMasterKey = cursor.optionalBlob(GroupTable.V2_MASTER_KEY).map { GroupUtil.requireMasterKey(it) }.orElse(null)
|
|
||||||
val identityKey = cursor.optionalString(RecipientTable.IDENTITY_KEY).map { Base64.decodeOrThrow(it) }.orElse(null)
|
|
||||||
val identityStatus = cursor.optionalInt(RecipientTable.IDENTITY_STATUS).map { VerifiedStatus.forState(it) }.orElse(VerifiedStatus.DEFAULT)
|
|
||||||
val unregisteredTimestamp = cursor.optionalLong(RecipientTable.UNREGISTERED_TIMESTAMP).orElse(0)
|
|
||||||
val systemNickname = cursor.optionalString(RecipientTable.SYSTEM_NICKNAME).orElse(null)
|
|
||||||
|
|
||||||
return RecipientRecord.SyncExtras(
|
return RecipientRecord.SyncExtras(
|
||||||
storageProto = storageProto,
|
storageProto = cursor.optionalString(RecipientTable.STORAGE_SERVICE_PROTO).orElse(null)?.let { Base64.decodeOrThrow(it) },
|
||||||
groupMasterKey = groupMasterKey,
|
groupMasterKey = cursor.optionalBlob(GroupTable.V2_MASTER_KEY).map { GroupUtil.requireMasterKey(it) }.orElse(null),
|
||||||
identityKey = identityKey,
|
identityKey = cursor.optionalString(RecipientTable.IDENTITY_KEY).map { Base64.decodeOrThrow(it) }.orElse(null),
|
||||||
identityStatus = identityStatus,
|
identityStatus = cursor.optionalInt(RecipientTable.IDENTITY_STATUS).map { VerifiedStatus.forState(it) }.orElse(VerifiedStatus.DEFAULT),
|
||||||
isArchived = archived,
|
isArchived = cursor.optionalBoolean(ThreadTable.ARCHIVED).orElse(false),
|
||||||
isForcedUnread = forcedUnread,
|
isForcedUnread = cursor.optionalInt(ThreadTable.READ).map { status: Int -> status == ThreadTable.ReadStatus.FORCED_UNREAD.serialize() }.orElse(false),
|
||||||
unregisteredTimestamp = unregisteredTimestamp,
|
unregisteredTimestamp = cursor.optionalLong(RecipientTable.UNREGISTERED_TIMESTAMP).orElse(0),
|
||||||
systemNickname = systemNickname
|
systemNickname = cursor.optionalString(RecipientTable.SYSTEM_NICKNAME).orElse(null),
|
||||||
|
pniSignatureVerified = cursor.optionalBoolean(RecipientTable.PNI_SIGNATURE_VERIFIED).orElse(false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V214_PhoneNumberSha
|
|||||||
import org.thoughtcrime.securesms.database.helpers.migration.V215_RemoveAttachmentUniqueId
|
import org.thoughtcrime.securesms.database.helpers.migration.V215_RemoveAttachmentUniqueId
|
||||||
import org.thoughtcrime.securesms.database.helpers.migration.V216_PhoneNumberDiscoverable
|
import org.thoughtcrime.securesms.database.helpers.migration.V216_PhoneNumberDiscoverable
|
||||||
import org.thoughtcrime.securesms.database.helpers.migration.V217_MessageTableExtrasColumn
|
import org.thoughtcrime.securesms.database.helpers.migration.V217_MessageTableExtrasColumn
|
||||||
|
import org.thoughtcrime.securesms.database.helpers.migration.V218_RecipientPniSignatureVerified
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
|
* Contains all of the database migrations for [SignalDatabase]. Broken into a separate file for cleanliness.
|
||||||
@@ -152,10 +153,11 @@ object SignalDatabaseMigrations {
|
|||||||
214 to V214_PhoneNumberSharingColumn,
|
214 to V214_PhoneNumberSharingColumn,
|
||||||
215 to V215_RemoveAttachmentUniqueId,
|
215 to V215_RemoveAttachmentUniqueId,
|
||||||
216 to V216_PhoneNumberDiscoverable,
|
216 to V216_PhoneNumberDiscoverable,
|
||||||
217 to V217_MessageTableExtrasColumn
|
217 to V217_MessageTableExtrasColumn,
|
||||||
|
218 to V218_RecipientPniSignatureVerified
|
||||||
)
|
)
|
||||||
|
|
||||||
const val DATABASE_VERSION = 217
|
const val DATABASE_VERSION = 218
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2024 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thoughtcrime.securesms.database.helpers.migration
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a pni_signature_verified column to the recipient table, letting us track whether the ACI/PNI association is verified and sync that to storage service.
|
||||||
|
*/
|
||||||
|
@Suppress("ClassName")
|
||||||
|
object V218_RecipientPniSignatureVerified : SignalDatabaseMigration {
|
||||||
|
override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||||
|
db.execSQL("ALTER TABLE recipient ADD COLUMN pni_signature_verified INTEGER DEFAULT 0")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -111,7 +111,8 @@ data class RecipientRecord(
|
|||||||
val isArchived: Boolean,
|
val isArchived: Boolean,
|
||||||
val isForcedUnread: Boolean,
|
val isForcedUnread: Boolean,
|
||||||
val unregisteredTimestamp: Long,
|
val unregisteredTimestamp: Long,
|
||||||
val systemNickname: String?
|
val systemNickname: String?,
|
||||||
|
val pniSignatureVerified: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Capabilities(
|
data class Capabilities(
|
||||||
|
|||||||
@@ -216,8 +216,9 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||||||
String systemGivenName = SignalStore.account().isPrimaryDevice() ? local.getSystemGivenName().orElse("") : remote.getSystemGivenName().orElse("");
|
String systemGivenName = SignalStore.account().isPrimaryDevice() ? local.getSystemGivenName().orElse("") : remote.getSystemGivenName().orElse("");
|
||||||
String systemFamilyName = SignalStore.account().isPrimaryDevice() ? local.getSystemFamilyName().orElse("") : remote.getSystemFamilyName().orElse("");
|
String systemFamilyName = SignalStore.account().isPrimaryDevice() ? local.getSystemFamilyName().orElse("") : remote.getSystemFamilyName().orElse("");
|
||||||
String systemNickname = remote.getSystemNickname().orElse("");
|
String systemNickname = remote.getSystemNickname().orElse("");
|
||||||
boolean matchesRemote = doParamsMatch(remote, unknownFields, aci, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, systemNickname, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
|
boolean pniSignatureVerified = remote.isPniSignatureVerified() || local.isPniSignatureVerified();
|
||||||
boolean matchesLocal = doParamsMatch(local, unknownFields, aci, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, systemNickname, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
|
boolean matchesRemote = doParamsMatch(remote, unknownFields, aci, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, systemNickname, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden, pniSignatureVerified);
|
||||||
|
boolean matchesLocal = doParamsMatch(local, unknownFields, aci, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, systemNickname, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden, pniSignatureVerified);
|
||||||
|
|
||||||
if (matchesRemote) {
|
if (matchesRemote) {
|
||||||
return remote;
|
return remote;
|
||||||
@@ -244,6 +245,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||||||
.setHideStory(hideStory)
|
.setHideStory(hideStory)
|
||||||
.setUnregisteredTimestamp(unregisteredTimestamp)
|
.setUnregisteredTimestamp(unregisteredTimestamp)
|
||||||
.setHidden(hidden)
|
.setHidden(hidden)
|
||||||
|
.setPniSignatureVerified(pniSignatureVerified)
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,7 +297,8 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||||||
long muteUntil,
|
long muteUntil,
|
||||||
boolean hideStory,
|
boolean hideStory,
|
||||||
long unregisteredTimestamp,
|
long unregisteredTimestamp,
|
||||||
boolean hidden)
|
boolean hidden,
|
||||||
|
boolean pniSignatureVerified)
|
||||||
{
|
{
|
||||||
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
|
||||||
Objects.equals(contact.getAci().orElse(null), aci) &&
|
Objects.equals(contact.getAci().orElse(null), aci) &&
|
||||||
@@ -317,6 +320,7 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
|
|||||||
contact.getMuteUntil() == muteUntil &&
|
contact.getMuteUntil() == muteUntil &&
|
||||||
contact.shouldHideStory() == hideStory &&
|
contact.shouldHideStory() == hideStory &&
|
||||||
contact.getUnregisteredTimestamp() == unregisteredTimestamp &&
|
contact.getUnregisteredTimestamp() == unregisteredTimestamp &&
|
||||||
contact.isHidden() == hidden;
|
contact.isHidden() == hidden &&
|
||||||
|
contact.isPniSignatureVerified() == pniSignatureVerified;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ public final class StorageSyncModels {
|
|||||||
.setUnregisteredTimestamp(recipient.getSyncExtras().getUnregisteredTimestamp())
|
.setUnregisteredTimestamp(recipient.getSyncExtras().getUnregisteredTimestamp())
|
||||||
.setHidden(recipient.getHiddenState() != Recipient.HiddenState.NOT_HIDDEN)
|
.setHidden(recipient.getHiddenState() != Recipient.HiddenState.NOT_HIDDEN)
|
||||||
.setUsername(recipient.getUsername())
|
.setUsername(recipient.getUsername())
|
||||||
|
.setPniSignatureVerified(recipient.getSyncExtras().getPniSignatureVerified())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -68,14 +68,15 @@ object RecipientDatabaseTestUtils {
|
|||||||
about: String? = null,
|
about: String? = null,
|
||||||
aboutEmoji: String? = null,
|
aboutEmoji: String? = null,
|
||||||
syncExtras: RecipientRecord.SyncExtras = RecipientRecord.SyncExtras(
|
syncExtras: RecipientRecord.SyncExtras = RecipientRecord.SyncExtras(
|
||||||
null,
|
storageProto = null,
|
||||||
null,
|
groupMasterKey = null,
|
||||||
null,
|
identityKey = null,
|
||||||
IdentityTable.VerifiedStatus.DEFAULT,
|
identityStatus = IdentityTable.VerifiedStatus.DEFAULT,
|
||||||
false,
|
isArchived = false,
|
||||||
false,
|
isForcedUnread = false,
|
||||||
0,
|
unregisteredTimestamp = 0,
|
||||||
null
|
systemNickname = null,
|
||||||
|
pniSignatureVerified = false
|
||||||
),
|
),
|
||||||
extras: Recipient.Extras? = null,
|
extras: Recipient.Extras? = null,
|
||||||
hasGroupsInCommon: Boolean = false,
|
hasGroupsInCommon: Boolean = false,
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ public final class SignalContactRecord implements SignalRecord {
|
|||||||
diff.add("Hidden");
|
diff.add("Hidden");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isPniSignatureVerified() != that.isPniSignatureVerified()) {
|
||||||
|
diff.add("PniSignatureVerified");
|
||||||
|
}
|
||||||
|
|
||||||
if (!Objects.equals(this.hasUnknownFields(), that.hasUnknownFields())) {
|
if (!Objects.equals(this.hasUnknownFields(), that.hasUnknownFields())) {
|
||||||
diff.add("UnknownFields");
|
diff.add("UnknownFields");
|
||||||
}
|
}
|
||||||
@@ -265,6 +269,10 @@ public final class SignalContactRecord implements SignalRecord {
|
|||||||
return proto.hidden;
|
return proto.hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPniSignatureVerified() {
|
||||||
|
return proto.pniSignatureVerified;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the same record, but stripped of the PNI field. Only used while PNP is in development.
|
* Returns the same record, but stripped of the PNI field. Only used while PNP is in development.
|
||||||
*/
|
*/
|
||||||
@@ -401,6 +409,11 @@ public final class SignalContactRecord implements SignalRecord {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setPniSignatureVerified(boolean verified) {
|
||||||
|
builder.pniSignatureVerified(verified);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private static ContactRecord.Builder parseUnknowns(byte[] serializedUnknowns) {
|
private static ContactRecord.Builder parseUnknowns(byte[] serializedUnknowns) {
|
||||||
try {
|
try {
|
||||||
return ContactRecord.ADAPTER.decode(serializedUnknowns).newBuilder();
|
return ContactRecord.ADAPTER.decode(serializedUnknowns).newBuilder();
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ message ContactRecord {
|
|||||||
string systemFamilyName = 18;
|
string systemFamilyName = 18;
|
||||||
string systemNickname = 19;
|
string systemNickname = 19;
|
||||||
bool hidden = 20;
|
bool hidden = 20;
|
||||||
// NEXT ID: 21
|
bool pniSignatureVerified = 21;
|
||||||
|
// NEXT ID: 22
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupV1Record {
|
message GroupV1Record {
|
||||||
|
|||||||
Reference in New Issue
Block a user