libsignal_service/groups_v2/
model.rs

1use std::{convert::TryFrom, convert::TryInto};
2
3use libsignal_protocol::{Aci, Pni, ServiceId};
4use serde::{Deserialize, Serialize};
5use zkgroup::profiles::{ExpiringProfileKeyCredential, ProfileKey};
6
7use crate::sender::GroupV2Id;
8
9use super::GroupDecodingError;
10
11/// A candidate member for group creation/modification.
12///
13/// If credential is Some, member will be added with a presentation.
14/// If credential is None, member will be added as pending (invite).
15#[derive(Clone)]
16pub struct GroupMemberCandidate {
17    pub service_id: ServiceId,
18    pub credential: Option<ExpiringProfileKeyCredential>,
19}
20
21impl std::fmt::Debug for GroupMemberCandidate {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        f.debug_struct("GroupCandidate")
24            .field("service_id", &self.service_id)
25            .field(
26                "credential",
27                &self.credential.as_ref().map(|_| "[credential]"),
28            )
29            .finish()
30    }
31}
32
33#[derive(Copy, Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
34pub enum Role {
35    Unknown,
36    Default,
37    Administrator,
38}
39
40#[derive(derive_more::Debug, Clone, Deserialize, Serialize)]
41pub struct Member {
42    #[serde(with = "aci_serde")]
43    pub aci: Aci,
44    pub role: Role,
45    #[debug(ignore)]
46    pub profile_key: ProfileKey,
47    pub joined_at_version: u32,
48}
49
50impl PartialEq for Member {
51    fn eq(&self, other: &Self) -> bool {
52        self.aci == other.aci
53    }
54}
55
56mod aci_serde {
57    use super::*;
58    use serde::{Deserializer, Serializer};
59
60    pub fn serialize<S>(p: &Aci, s: S) -> Result<S::Ok, S::Error>
61    where
62        S: Serializer,
63    {
64        s.serialize_str(&p.service_id_string())
65    }
66
67    pub fn deserialize<'de, D>(d: D) -> Result<Aci, D::Error>
68    where
69        D: Deserializer<'de>,
70    {
71        // We have to go through String deserialization,
72        // because Aci does not implement Deserialize (duh).
73        let s = std::borrow::Cow::<str>::deserialize(d)?;
74        match Aci::parse_from_service_id_string(&s) {
75            Some(aci) => Ok(aci),
76            None => Err(serde::de::Error::custom("Invalid ACI string")),
77        }
78    }
79}
80
81#[derive(Clone, Debug, PartialEq, Eq)]
82pub struct PendingMember {
83    pub address: ServiceId,
84    pub role: Role,
85    pub added_by_aci: Aci,
86    pub timestamp: u64,
87}
88
89#[derive(derive_more::Debug, Clone)]
90pub struct RequestingMember {
91    pub aci: Aci,
92    #[debug(ignore)]
93    pub profile_key: ProfileKey,
94    pub timestamp: u64,
95}
96
97impl PartialEq for RequestingMember {
98    fn eq(&self, other: &Self) -> bool {
99        self.aci == other.aci
100    }
101}
102
103#[derive(Debug, Clone)]
104pub struct BannedMember {
105    pub user_id: ServiceId,
106    pub timestamp: u64,
107}
108
109impl PartialEq for BannedMember {
110    fn eq(&self, other: &Self) -> bool {
111        self.user_id == other.user_id
112    }
113}
114
115#[derive(derive_more::Debug, Clone)]
116pub struct PromotedMember {
117    pub aci: Aci,
118    pub pni: Pni,
119    #[debug(ignore)]
120    pub profile_key: ProfileKey,
121}
122
123impl PartialEq for PromotedMember {
124    fn eq(&self, other: &Self) -> bool {
125        self.aci == other.aci && self.pni == other.pni
126    }
127}
128
129#[derive(Copy, Debug, Clone, PartialEq, Serialize, Deserialize)]
130pub enum AccessRequired {
131    Unknown,
132    Any,
133    Member,
134    Administrator,
135    Unsatisfiable,
136}
137
138#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
139pub struct AccessControl {
140    pub attributes: AccessRequired,
141    pub members: AccessRequired,
142    pub add_from_invite_link: AccessRequired,
143    pub member_label: AccessRequired,
144}
145
146#[derive(Debug, Clone, PartialEq)]
147pub struct Group {
148    pub title: String,
149    pub avatar: String,
150    pub disappearing_messages_timer: Option<Timer>,
151    pub access_control: Option<AccessControl>,
152    pub version: u32,
153    pub members: Vec<Member>,
154    pub members_pending_profile_key: Vec<PendingMember>,
155    pub members_pending_admin_approval: Vec<RequestingMember>,
156    pub invite_link_password: Vec<u8>,
157    pub description_text: Option<String>,
158    pub announcements_only: bool,
159    pub members_banned: Vec<BannedMember>,
160}
161
162#[derive(Debug, Clone)]
163pub struct GroupChanges {
164    pub group_id: GroupV2Id,
165    pub editor: Aci,
166    pub version: u32,
167    pub changes: Vec<GroupChange>,
168    pub change_epoch: u32,
169}
170
171#[derive(derive_more::Debug, Clone)]
172pub enum GroupChange {
173    NewMember(Member),
174    DeleteMember(Aci),
175    ModifyMemberRole {
176        aci: Aci,
177        role: Role,
178    },
179    ModifyMemberProfileKey {
180        aci: Aci,
181        #[debug(ignore)]
182        profile_key: ProfileKey,
183    },
184    NewPendingMember(PendingMember),
185    DeletePendingMember(ServiceId),
186    PromotePendingMember {
187        address: ServiceId,
188        #[debug(ignore)]
189        profile_key: ProfileKey,
190    },
191    Title(String),
192    Avatar(String),
193    Timer(Option<Timer>),
194    AttributeAccess(AccessRequired),
195    MemberAccess(AccessRequired),
196    InviteLinkAccess(AccessRequired),
197    NewRequestingMember(RequestingMember),
198    DeleteRequestingMember(Aci),
199    PromoteRequestingMember {
200        aci: Aci,
201        role: Role,
202    },
203    InviteLinkPassword(String),
204    Description(Option<String>),
205    AnnouncementOnly(bool),
206    AddBannedMember(BannedMember),
207    DeleteBannedMember(ServiceId),
208    PromotePendingPniAciMemberProfileKey(PromotedMember),
209    MemberLabel {
210        user_id: ServiceId,
211        label_emoji: String,
212        label_string: String,
213    },
214    MemberLabelAccess(AccessRequired),
215}
216
217#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
218pub struct Timer {
219    pub duration: u32,
220}
221
222// Conversion from and to protobuf definitions
223
224impl TryFrom<i32> for Role {
225    type Error = GroupDecodingError;
226
227    fn try_from(value: i32) -> Result<Self, Self::Error> {
228        use crate::proto::member::Role as ProtoRole;
229        match crate::proto::member::Role::try_from(value) {
230            Ok(ProtoRole::Unknown) => Ok(Role::Unknown),
231            Ok(ProtoRole::Default) => Ok(Role::Default),
232            Ok(ProtoRole::Administrator) => Ok(Role::Administrator),
233            Err(_e) => Err(GroupDecodingError::WrongEnumValue),
234        }
235    }
236}
237
238impl From<Role> for i32 {
239    fn from(val: Role) -> Self {
240        use crate::proto::member::Role::*;
241        match val {
242            Role::Unknown => Unknown,
243            Role::Default => Default,
244            Role::Administrator => Administrator,
245        }
246        .into()
247    }
248}
249
250impl TryFrom<i32> for AccessRequired {
251    type Error = GroupDecodingError;
252
253    fn try_from(value: i32) -> Result<Self, Self::Error> {
254        use crate::proto::access_control::AccessRequired as ProtoAccessRequired;
255        match crate::proto::access_control::AccessRequired::try_from(value) {
256            Ok(ProtoAccessRequired::Unknown) => Ok(AccessRequired::Unknown),
257            Ok(ProtoAccessRequired::Any) => Ok(AccessRequired::Any),
258            Ok(ProtoAccessRequired::Member) => Ok(AccessRequired::Member),
259            Ok(ProtoAccessRequired::Administrator) => {
260                Ok(AccessRequired::Administrator)
261            },
262            Ok(ProtoAccessRequired::Unsatisfiable) => {
263                Ok(AccessRequired::Unsatisfiable)
264            },
265            Err(_e) => Err(GroupDecodingError::WrongEnumValue),
266        }
267    }
268}
269
270impl From<AccessRequired> for i32 {
271    fn from(val: AccessRequired) -> Self {
272        use crate::proto::access_control::AccessRequired::*;
273        match val {
274            AccessRequired::Unknown => Unknown,
275            AccessRequired::Any => Any,
276            AccessRequired::Member => Member,
277            AccessRequired::Administrator => Administrator,
278            AccessRequired::Unsatisfiable => Unsatisfiable,
279        }
280        .into()
281    }
282}
283
284impl TryFrom<crate::proto::AccessControl> for AccessControl {
285    type Error = GroupDecodingError;
286
287    fn try_from(
288        value: crate::proto::AccessControl,
289    ) -> Result<Self, Self::Error> {
290        Ok(Self {
291            attributes: value.attributes.try_into()?,
292            members: value.members.try_into()?,
293            add_from_invite_link: value.add_from_invite_link.try_into()?,
294            member_label: value.member_label.try_into()?,
295        })
296    }
297}