safe_nd/messaging/
account.rs

1// Copyright 2019 MaidSafe.net limited.
2//
3// This SAFE Network Software is licensed to you under the MIT license <LICENSE-MIT
4// https://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
5// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
6// modified, or distributed except according to those terms. Please review the Licences for the
7// specific language governing permissions and limitations relating to use of the SAFE Network
8// Software.
9
10use super::{AuthorisationKind, CmdError, DataAuthKind, QueryResponse};
11use crate::{Error, PublicKey, Result, Signature, XorName};
12use serde::{Deserialize, Serialize};
13use std::fmt;
14
15/// Login packet size is limited .
16pub const MAX_LOGIN_PACKET_BYTES: usize = 1024 * 1024; // 1 MB
17
18/// Use this only while we don't
19/// have Authenticator as its own app.
20#[allow(clippy::large_enum_variant)]
21#[derive(Hash, Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
22pub enum AccountWrite {
23    /// Create a new account.
24    New(Account),
25    /// Update (overwrite) an Account.
26    Update(Account),
27}
28
29/// Use this only while we don't
30/// have Authenticator as its own app.
31#[allow(clippy::large_enum_variant)]
32#[derive(Hash, Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
33pub enum AccountRead {
34    /// Get an encrypted account.
35    Get(XorName),
36}
37
38impl AccountWrite {
39    /// Creates a Response containing an error, with the Response variant corresponding to the
40    /// Request variant.
41    pub fn error(&self, error: Error) -> CmdError {
42        use AccountWrite::*;
43        match *self {
44            New { .. } | Update { .. } => CmdError::Data(error),
45        }
46    }
47
48    /// Returns the type of authorisation needed for the request.
49    pub fn authorisation_kind(&self) -> AuthorisationKind {
50        use AccountWrite::*;
51        match *self {
52            New { .. } | Update { .. } => AuthorisationKind::Data(DataAuthKind::Write),
53        }
54    }
55
56    /// Returns the address of the destination for `request`.
57    pub fn dst_address(&self) -> XorName {
58        use AccountWrite::*;
59        match self {
60            New(account) => *account.address(),
61            Update(account) => *account.address(),
62        }
63    }
64}
65
66impl fmt::Debug for AccountWrite {
67    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
68        use AccountWrite::*;
69        write!(
70            formatter,
71            "Request::{}",
72            match *self {
73                New { .. } => "NewAccount",
74                Update { .. } => "UpdateAccount",
75            }
76        )
77    }
78}
79
80impl AccountRead {
81    // /// Get the `Type` of this request.
82    // pub fn get_type(&self) -> Type {
83    //     Type::PrivateRead
84    // }
85
86    /// Creates a Response containing an error, with the Response variant corresponding to the
87    /// Request variant.
88    pub fn error(&self, error: Error) -> QueryResponse {
89        QueryResponse::GetAccount(Err(error))
90    }
91
92    /// Returns the type of authorisation needed for the request.
93    pub fn authorisation_kind(&self) -> AuthorisationKind {
94        AuthorisationKind::Data(DataAuthKind::PrivateRead)
95    }
96
97    /// Returns the address of the destination for request.
98    pub fn dst_address(&self) -> XorName {
99        use AccountRead::*;
100        match self {
101            Get(ref name) => *name,
102        }
103    }
104}
105
106impl fmt::Debug for AccountRead {
107    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
108        write!(formatter, "Request::GetAccount")
109    }
110}
111
112/// Use this only while we don't
113/// have Authenticator as its own app.
114/// Containing arbitrary user's account information.
115#[derive(Debug, Hash, Eq, PartialEq, PartialOrd, Clone, Serialize, Deserialize)]
116pub struct Account {
117    address: XorName,
118    owner: PublicKey, // deterministically created from passwords
119    data: Vec<u8>,
120    signature: Signature,
121}
122
123impl Account {
124    /// Construct a new login packet.
125    pub fn new(
126        address: XorName,
127        owner: PublicKey,
128        data: Vec<u8>,
129        signature: Signature,
130    ) -> Result<Self> {
131        let account = Self {
132            address,
133            owner,
134            data,
135            signature,
136        };
137        if account.size_is_valid() {
138            Ok(account)
139        } else {
140            Err(Error::ExceededSize)
141        }
142    }
143
144    /// Returns true if the size of the data is valid.
145    pub fn size_is_valid(&self) -> bool {
146        self.data.len() <= MAX_LOGIN_PACKET_BYTES
147    }
148
149    /// Gets the address.
150    pub fn address(&self) -> &XorName {
151        &self.address
152    }
153
154    /// Gets the owner.
155    pub fn owner(&self) -> &PublicKey {
156        &self.owner
157    }
158
159    /// Returns the data.
160    pub fn data(&self) -> &[u8] {
161        &self.data
162    }
163
164    /// Returns the signature.
165    pub fn signature(&self) -> &Signature {
166        &self.signature
167    }
168
169    /// Convert this login packet into its data and signature.
170    pub fn into_data_and_signature(self) -> (Vec<u8>, Signature) {
171        (self.data, self.signature)
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::{Account, MAX_LOGIN_PACKET_BYTES};
178    use crate::{ClientFullId, Error};
179
180    #[test]
181    fn exceed_size_limit() {
182        let our_id = ClientFullId::new_ed25519(&mut rand::thread_rng());
183
184        let acc_data = vec![0; MAX_LOGIN_PACKET_BYTES + 1];
185        let signature = our_id.sign(&acc_data);
186
187        let res = Account::new(
188            rand::random(),
189            *our_id.public_id().public_key(),
190            acc_data,
191            signature,
192        );
193
194        match res {
195            Err(Error::ExceededSize) => (),
196            Ok(_) => panic!("Unexpected success"),
197            Err(e) => panic!("Unexpected error: {:?}", e),
198        }
199    }
200
201    #[test]
202    fn valid() {
203        let our_id = ClientFullId::new_ed25519(&mut rand::thread_rng());
204
205        let acc_data = vec![1; 16];
206        let signature = our_id.sign(&acc_data);
207
208        let res = Account::new(
209            rand::random(),
210            *our_id.public_id().public_key(),
211            acc_data.clone(),
212            signature,
213        );
214
215        match res {
216            Ok(ad) => {
217                assert_eq!(ad.data(), acc_data.as_slice());
218            }
219            Err(e) => panic!("Unexpected error: {:?}", e),
220        }
221    }
222}