1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! External Proposals
//!
//! Contains the types and methods to build external proposal to add/remove a client from a MLS group
//!
//! `Add` (from external sender), `Remove` & `ReInit are not yet implemented`

use crate::{
    binary_tree::LeafNodeIndex,
    extensions::SenderExtensionIndex,
    framing::{mls_auth_content::AuthenticatedContent, MlsMessageOut, PublicMessage},
    group::{
        errors::ProposeRemoveMemberError, mls_group::errors::ProposeAddMemberError, GroupEpoch,
        GroupId,
    },
    key_packages::KeyPackage,
    messages::{AddProposal, Proposal},
    storage::{OpenMlsProvider, StorageProvider},
};
use openmls_traits::signatures::Signer;

use super::proposals::RemoveProposal;

/// External Add Proposal where sender is [NewMemberProposal](crate::prelude::Sender::NewMemberProposal). A client
/// outside the group can request joining the group. This proposal should then be committed by a
/// group member. Note that this is unconstrained i.e. it works for any [MLS group](crate::group::MlsGroup).
/// This is not the case for the same external proposal with a [Preconfigured sender](crate::prelude::Sender::External)
pub struct JoinProposal;

/// External Proposal where sender is [External](crate::prelude::Sender::External). A party
/// outside the group can request to add or remove a member to the group. This proposal should then
/// be committed by a group member. The sender must be pre configured within the group through the [crate::extensions::ExternalSendersExtension]
pub struct ExternalProposal;

impl JoinProposal {
    /// Creates an external Add proposal. For clients requesting to be added to a group. This
    /// proposal will have to be committed later by a group member.
    ///
    /// # Arguments
    /// * `key_package` - of the joiner
    /// * `group_id` - unique group identifier of the group to join
    /// * `epoch` - group's epoch
    /// * `signer` - of the sender to sign the message
    #[allow(clippy::new_ret_no_self)]
    pub fn new<Storage: StorageProvider>(
        key_package: KeyPackage,
        group_id: GroupId,
        epoch: GroupEpoch,
        signer: &impl Signer,
    ) -> Result<MlsMessageOut, ProposeAddMemberError<Storage::Error>> {
        AuthenticatedContent::new_join_proposal(
            Proposal::Add(AddProposal { key_package }),
            group_id,
            epoch,
            signer,
        )
        .map(PublicMessage::from)
        .map(MlsMessageOut::from)
        .map_err(ProposeAddMemberError::from)
    }
}

impl ExternalProposal {
    /// Creates an external Remove proposal. For delivery services requesting to remove a client.
    /// This proposal will have to be committed later by a group member.
    ///
    /// # Arguments
    /// * `removed` - index of the client to remove
    /// * `group_id` - unique group identifier of the group to join
    /// * `epoch` - group's epoch
    /// * `signer` - of the sender to sign the message
    /// * `sender` - index of the sender of the proposal (in the [crate::extensions::ExternalSendersExtension] array
    /// from the Group Context)
    pub fn new_remove<Provider: OpenMlsProvider>(
        removed: LeafNodeIndex,
        group_id: GroupId,
        epoch: GroupEpoch,
        signer: &impl Signer,
        sender_index: SenderExtensionIndex,
    ) -> Result<MlsMessageOut, ProposeRemoveMemberError<Provider::StorageError>> {
        AuthenticatedContent::new_external_proposal(
            Proposal::Remove(RemoveProposal { removed }),
            group_id,
            epoch,
            signer,
            sender_index,
        )
        .map(PublicMessage::from)
        .map(MlsMessageOut::from)
        .map_err(ProposeRemoveMemberError::from)
    }
}