0
0
mirror of https://github.com/signalapp/libsignal.git synced 2024-09-19 19:42:19 +02:00

Update to latest Backup.proto

- expireTimerVersion must not be 0 when an expirationTimer is set
- group members no longer have profile keys
This commit is contained in:
Jordan Rose 2024-09-17 13:54:05 -07:00
parent 6daa60f395
commit 26bf02e1e3
4 changed files with 27 additions and 38 deletions

View File

@ -69,6 +69,8 @@ pub enum ChatError {
NoRecipient(RecipientId),
/// cannot have a chat with recipient {0:?}, a {1:?}
InvalidRecipient(RecipientId, DestinationKind),
/// chat with {0:?} has an expirationTimerMs but no expireTimerVersion
MissingExpireTimerVersion(RecipientId),
/// chat item: {0}
ChatItem(#[from] ChatItemError),
/// {0:?} already appeared
@ -172,6 +174,7 @@ pub struct ChatData<M: Method + ReferencedTypes> {
#[serde(bound(serialize = "M::List<ChatItemData<M>>: serde::Serialize"))]
pub items: M::List<ChatItemData<M>>,
pub expiration_timer: Option<Duration>,
pub expiration_timer_version: u32,
pub mute_until: Option<Timestamp>,
pub style: Option<ChatStyle<M>>,
pub pinned_order: Option<PinOrder>,
@ -349,6 +352,7 @@ impl<
id: _,
recipientId,
expirationTimerMs,
expireTimerVersion,
muteUntilMs,
pinnedOrder,
archived,
@ -389,9 +393,15 @@ impl<
let mute_until = NonZeroU64::new(muteUntilMs)
.map(|t| Timestamp::from_millis(t.get(), "Chat.muteUntilMs"));
if expiration_timer.is_some() && expireTimerVersion == 0 {
return Err(ChatError::MissingExpireTimerVersion(recipient_id));
}
let expiration_timer_version = expireTimerVersion;
Ok(Self {
recipient,
expiration_timer,
expiration_timer_version,
mute_until,
items: Default::default(),
style,
@ -869,6 +879,7 @@ mod test {
recipient: TestContext::test_recipient().clone(),
items: Vec::default(),
expiration_timer: None,
expiration_timer_version: 0,
mute_until: None,
style: None,
pinned_order: None,
@ -879,7 +890,12 @@ mod test {
);
}
#[test_case(|x| x.expirationTimerMs = 123456 => Ok(()); "with_expiration_timer")]
#[test_case(|x| {
x.expirationTimerMs = 123456;
x.expireTimerVersion = 3;
} => Ok(()); "with_expiration_timer")]
#[test_case(|x| x.expirationTimerMs = 123456 => Err(ChatError::MissingExpireTimerVersion(TestContext::SELF_ID)); "with_expiration_timer_only")]
#[test_case(|x| x.expireTimerVersion = 3 => Ok(()); "with_expire_timer_version_only")]
#[test_case(|x| x.muteUntilMs = MillisecondsSinceEpoch::TEST_VALUE.0 => Ok(()); "with mute until")]
#[test_case(
|x| x.pinnedOrder = TestContext::DUPLICATE_PINNED_ORDER.0.get() =>

View File

@ -4,7 +4,6 @@
//
use libsignal_core::{Aci, ServiceId, WrongKindOfServiceIdError};
use zkgroup::ProfileKeyBytes;
use super::GroupError;
use crate::backup::serialize::{self, SerializeOrder};
@ -23,8 +22,6 @@ pub struct GroupMember {
#[serde(serialize_with = "serialize::service_id_as_string")]
pub user_id: Aci,
pub role: Role,
#[serde(with = "hex")]
pub profile_key: ProfileKeyBytes,
pub joined_at_version: u32,
pub(super) _limit_construction_to_module: (),
}
@ -42,7 +39,6 @@ impl TryFrom<proto::group::Member> for GroupMember {
let proto::group::Member {
userId,
role,
profileKey,
joinedAtVersion,
special_fields: _,
} = value;
@ -61,14 +57,11 @@ impl TryFrom<proto::group::Member> for GroupMember {
proto::group::member::Role::DEFAULT => Role::Default,
proto::group::member::Role::ADMINISTRATOR => Role::Administrator,
};
let profile_key = ProfileKeyBytes::try_from(profileKey)
.map_err(|_| GroupError::MemberInvalidProfileKey)?;
let joined_at_version = joinedAtVersion;
Ok(GroupMember {
user_id,
role,
profile_key,
joined_at_version,
_limit_construction_to_module: (),
})
@ -108,7 +101,6 @@ impl TryFrom<proto::group::MemberPendingProfileKey> for GroupMemberPendingProfil
let proto::group::Member {
userId,
role,
profileKey,
joinedAtVersion,
special_fields: _,
} = member
@ -125,9 +117,6 @@ impl TryFrom<proto::group::MemberPendingProfileKey> for GroupMemberPendingProfil
proto::group::member::Role::DEFAULT => Role::Default,
proto::group::member::Role::ADMINISTRATOR => Role::Administrator,
};
if !profileKey.is_empty() {
return Err(GroupError::MemberPendingProfileKeyHasProfileKey);
}
let joined_at_version = joinedAtVersion;
let added_by_user_id = ServiceId::parse_from_service_id_binary(&addedByUserId)
@ -162,8 +151,6 @@ impl TryFrom<proto::group::MemberPendingProfileKey> for GroupMemberPendingProfil
pub struct GroupMemberPendingAdminApproval {
#[serde(serialize_with = "serialize::service_id_as_string")]
pub user_id: Aci,
#[serde(with = "hex")]
pub profile_key: ProfileKeyBytes,
pub timestamp: Timestamp,
pub(super) _limit_construction_to_module: (),
}
@ -180,7 +167,6 @@ impl TryFrom<proto::group::MemberPendingAdminApproval> for GroupMemberPendingAdm
fn try_from(member: proto::group::MemberPendingAdminApproval) -> Result<Self, Self::Error> {
let proto::group::MemberPendingAdminApproval {
userId,
profileKey,
timestamp,
special_fields: _,
} = member;
@ -196,13 +182,10 @@ impl TryFrom<proto::group::MemberPendingAdminApproval> for GroupMemberPendingAdm
found: e.actual,
},
)?;
let profile_key = ProfileKeyBytes::try_from(profileKey)
.map_err(|_| GroupError::MemberInvalidProfileKey)?;
let timestamp = Timestamp::from_millis(timestamp, "MemberPendingAdminApproval");
Ok(GroupMemberPendingAdminApproval {
user_id,
profile_key,
timestamp,
_limit_construction_to_module: (),
})
@ -262,7 +245,6 @@ mod tests {
Self {
userId: proto::Contact::TEST_ACI.to_vec(),
role: proto::group::member::Role::DEFAULT.into(),
profileKey: proto::Contact::TEST_PROFILE_KEY.to_vec(),
joinedAtVersion: 1,
..Default::default()
}
@ -274,7 +256,6 @@ mod tests {
Self {
user_id: Aci::from_uuid_bytes(proto::Contact::TEST_ACI),
role: Role::Default,
profile_key: proto::Contact::TEST_PROFILE_KEY,
joined_at_version: 1,
_limit_construction_to_module: (),
}
@ -293,7 +274,6 @@ mod tests {
#[test_case(|x| x.userId = vec![] => Err(GroupError::MemberInvalidServiceId { which: "member" }); "empty userId")]
#[test_case(|x| x.role = proto::group::member::Role::ADMINISTRATOR.into() => Ok(()); "administrator")]
#[test_case(|x| x.role = proto::group::member::Role::UNKNOWN.into() => Err(GroupError::MemberRoleUnknown); "role unknown")]
#[test_case(|x| x.profileKey = vec![] => Err(GroupError::MemberInvalidProfileKey); "empty profileKey")]
fn member(modifier: impl FnOnce(&mut proto::group::Member)) -> Result<(), GroupError> {
let mut member = proto::group::Member::test_data();
modifier(&mut member);
@ -305,11 +285,7 @@ mod tests {
pub(crate) fn test_data() -> Self {
Self {
member: Some(proto::group::Member {
profileKey: vec![],
..proto::group::Member::test_data()
})
.into(),
member: Some(proto::group::Member::test_data()).into(),
timestamp: MillisecondsSinceEpoch::TEST_VALUE.0,
addedByUserId: Self::INVITER_ACI.to_vec(),
..Default::default()
@ -348,7 +324,6 @@ mod tests {
#[test_case(|x| x.member.as_mut().unwrap().userId = vec![] => Err(GroupError::MemberInvalidServiceId { which: "invited member" }); "empty userId")]
#[test_case(|x| x.member.as_mut().unwrap().role = proto::group::member::Role::ADMINISTRATOR.into() => Ok(()); "administrator")]
#[test_case(|x| x.member.as_mut().unwrap().role = proto::group::member::Role::UNKNOWN.into() => Err(GroupError::MemberRoleUnknown); "role unknown")]
#[test_case(|x| x.member.as_mut().unwrap().profileKey = proto::Contact::TEST_PROFILE_KEY.to_vec() => Err(GroupError::MemberPendingProfileKeyHasProfileKey); "valid profileKey")]
#[test_case(|x| x.addedByUserId = Pni::from_uuid_bytes(proto::Contact::TEST_PNI).service_id_binary() => Err(GroupError::MemberInvalidAci { which: "inviter", found: ServiceIdKind::Pni }); "PNI inviter")]
#[test_case(|x| x.addedByUserId = vec![] => Err(GroupError::MemberInvalidServiceId { which: "inviter" }); "empty inviter")]
#[test_case(|x| x.addedByUserId = proto::Contact::TEST_ACI.to_vec() => Err(GroupError::MemberPendingProfileKeyWasInvitedBySelf); "self-invite")]
@ -364,7 +339,6 @@ mod tests {
pub(crate) fn test_data() -> Self {
Self {
userId: proto::Contact::TEST_ACI.to_vec(),
profileKey: proto::Contact::TEST_PROFILE_KEY.to_vec(),
timestamp: MillisecondsSinceEpoch::TEST_VALUE.0,
..Default::default()
}
@ -375,7 +349,6 @@ mod tests {
pub(crate) fn from_proto_test_data() -> Self {
Self {
user_id: Aci::from_uuid_bytes(proto::Contact::TEST_ACI),
profile_key: proto::Contact::TEST_PROFILE_KEY,
timestamp: Timestamp::test_value(),
_limit_construction_to_module: (),
}
@ -395,7 +368,6 @@ mod tests {
#[test_case(|x| x.userId = Pni::from_uuid_bytes(proto::Contact::TEST_PNI).service_id_binary() => Err(GroupError::MemberInvalidAci { which: "requesting member", found: ServiceIdKind::Pni }); "PNI userId")]
#[test_case(|x| x.userId = vec![] => Err(GroupError::MemberInvalidServiceId { which: "requesting member" }); "empty userId")]
#[test_case(|x| x.profileKey = vec![] => Err(GroupError::MemberInvalidProfileKey); "empty profileKey")]
fn member_pending_admin_approval(
modifier: impl FnOnce(&mut proto::group::MemberPendingAdminApproval),
) -> Result<(), GroupError> {

View File

@ -191,8 +191,8 @@ message Group {
bytes userId = 1;
Role role = 2;
bytes profileKey = 3;
reserved /*presentation*/ 4; // The field is deprecated in the context of static group state
reserved /*profileKey*/ 3; // This field is ignored in Backups, in favor of Contact frames for members
reserved /*presentation*/ 4; // This field is deprecated in the context of static group state
uint32 joinedAtVersion = 5;
}
@ -204,8 +204,8 @@ message Group {
message MemberPendingAdminApproval {
bytes userId = 1;
bytes profileKey = 2;
reserved /*presentation*/ 3; // The field is deprecated in the context of static group state
reserved /*profileKey*/ 2; // This field is ignored in Backups, in favor of Contact frames for members
reserved /*presentation*/ 3; // This field is deprecated in the context of static group state
uint64 timestamp = 4;
}
@ -243,6 +243,7 @@ message Chat {
bool markedUnread = 7;
bool dontNotifyForMentionsIfMuted = 8;
ChatStyle style = 9;
uint32 expireTimerVersion = 10;
}
/**
@ -1066,7 +1067,7 @@ message StickerPack {
message ChatStyle {
message Gradient {
uint32 angle = 1; // degrees
repeated fixed32 colors = 2;
repeated fixed32 colors = 2; // 0xAARRGGBB
repeated float positions = 3; // percent from 0 to 1
}
@ -1074,7 +1075,7 @@ message ChatStyle {
uint64 id = 1;
oneof color {
fixed32 solid = 2;
fixed32 solid = 2; // 0xAARRGGBB
Gradient gradient = 3;
}
}
@ -1135,6 +1136,8 @@ message ChatStyle {
oneof wallpaper {
WallpaperPreset wallpaperPreset = 1;
// This `FilePointer` is expected not to contain a `fileName`, `width`,
// `height`, or `caption`.
FilePointer wallpaperPhoto = 2;
}

View File

@ -113,13 +113,11 @@
{
"userId": "CujGyCcHTqeyJHQXIn04kA==", // Chewie's ACI
"role": "ADMINISTRATOR",
"profileKey": "cM4PAiE6xclFBl2wesio4S/tpbDfZHFpYf7BBAsnZI4=",
"joinedAtVersion": 0,
},
{
"userId": "X4xWjQEZR72BqruHybcZlQ==", // Han's ACI
"role": "ADMINISTRATOR",
"profileKey": "YtHHVK+Wo4nPcVpWhC3roMEDu2Tw6kYc9JpLRMq1Q94=",
"joinedAtVersion": 0,
},
],