parse_rs/
acl.rs

1// src/acl.rs
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::collections::HashMap;
4
5/// Represents a Parse Access Control List (ACL).
6///
7/// ACLs are used to control permissions for reading and writing ParseObjects.
8/// Permissions can be granted to the public, individual users (by user ID),
9/// or roles.
10#[derive(Debug, Clone, PartialEq)]
11pub struct ParseACL {
12    permissions: HashMap<String, AccessLevel>,
13}
14
15/// Defines the access level for a user or role.
16#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
17struct AccessLevel {
18    #[serde(skip_serializing_if = "Option::is_none")]
19    read: Option<bool>,
20    #[serde(skip_serializing_if = "Option::is_none")]
21    write: Option<bool>,
22}
23
24impl ParseACL {
25    /// Creates a new, empty `ParseACL`.
26    /// By default, no one has any permissions.
27    pub fn new() -> Self {
28        ParseACL {
29            permissions: HashMap::new(),
30        }
31    }
32
33    /// Sets public read access.
34    ///
35    /// # Arguments
36    /// * `allowed`: `true` to allow public read access, `false` to disallow.
37    pub fn set_public_read_access(&mut self, allowed: bool) {
38        self.permissions
39            .entry("*".to_string())
40            .or_insert_with(|| AccessLevel {
41                read: None,
42                write: None,
43            })
44            .read = Some(allowed);
45    }
46
47    /// Sets public write access.
48    ///
49    /// # Arguments
50    /// * `allowed`: `true` to allow public write access, `false` to disallow.
51    pub fn set_public_write_access(&mut self, allowed: bool) {
52        self.permissions
53            .entry("*".to_string())
54            .or_insert_with(|| AccessLevel {
55                read: None,
56                write: None,
57            })
58            .write = Some(allowed);
59    }
60
61    /// Sets read access for a specific user ID.
62    ///
63    /// # Arguments
64    /// * `user_id`: The object ID of the user.
65    /// * `allowed`: `true` to allow read access for this user, `false` to disallow.
66    pub fn set_user_read_access(&mut self, user_id: &str, allowed: bool) {
67        self.permissions
68            .entry(user_id.to_string())
69            .or_insert_with(|| AccessLevel {
70                read: None,
71                write: None,
72            })
73            .read = Some(allowed);
74    }
75
76    /// Sets write access for a specific user ID.
77    ///
78    /// # Arguments
79    /// * `user_id`: The object ID of the user.
80    /// * `allowed`: `true` to allow write access for this user, `false` to disallow.
81    pub fn set_user_write_access(&mut self, user_id: &str, allowed: bool) {
82        self.permissions
83            .entry(user_id.to_string())
84            .or_insert_with(|| AccessLevel {
85                read: None,
86                write: None,
87            })
88            .write = Some(allowed);
89    }
90
91    /// Sets read access for a specific role.
92    ///
93    /// # Arguments
94    /// * `role_name`: The name of the role (e.g., "Administrators").
95    /// * `allowed`: `true` to allow read access for this role, `false` to disallow.
96    pub fn set_role_read_access(&mut self, role_name: &str, allowed: bool) {
97        let role_key = format!("role:{}", role_name);
98        self.permissions
99            .entry(role_key)
100            .or_insert_with(|| AccessLevel {
101                read: None,
102                write: None,
103            })
104            .read = Some(allowed);
105    }
106
107    /// Sets write access for a specific role.
108    ///
109    /// # Arguments
110    /// * `role_name`: The name of the role (e.g., "Administrators").
111    /// * `allowed`: `true` to allow write access for this role, `false` to disallow.
112    pub fn set_role_write_access(&mut self, role_name: &str, allowed: bool) {
113        let role_key = format!("role:{}", role_name);
114        self.permissions
115            .entry(role_key)
116            .or_insert_with(|| AccessLevel {
117                read: None,
118                write: None,
119            })
120            .write = Some(allowed);
121    }
122
123    // --- Getter methods ---
124
125    /// Gets whether the public is allowed to read this object.
126    pub fn get_public_read_access(&self) -> bool {
127        self.permissions
128            .get("*")
129            .and_then(|access| access.read)
130            .unwrap_or(false)
131    }
132
133    /// Gets whether the public is allowed to write this object.
134    pub fn get_public_write_access(&self) -> bool {
135        self.permissions
136            .get("*")
137            .and_then(|access| access.write)
138            .unwrap_or(false)
139    }
140
141    /// Gets whether the given user is allowed to read this object.
142    pub fn get_user_read_access(&self, user_id: &str) -> bool {
143        self.permissions
144            .get(user_id)
145            .and_then(|access| access.read)
146            .unwrap_or(false)
147    }
148
149    /// Gets whether the given user is allowed to write this object.
150    pub fn get_user_write_access(&self, user_id: &str) -> bool {
151        self.permissions
152            .get(user_id)
153            .and_then(|access| access.write)
154            .unwrap_or(false)
155    }
156
157    /// Gets whether users belonging to the given role are allowed to read this object.
158    pub fn get_role_read_access(&self, role_name: &str) -> bool {
159        let role_key = format!("role:{}", role_name);
160        self.permissions
161            .get(&role_key)
162            .and_then(|access| access.read)
163            .unwrap_or(false)
164    }
165
166    /// Gets whether users belonging to the given role are allowed to write this object.
167    pub fn get_role_write_access(&self, role_name: &str) -> bool {
168        let role_key = format!("role:{}", role_name);
169        self.permissions
170            .get(&role_key)
171            .and_then(|al| al.write)
172            .unwrap_or(false)
173    }
174}
175
176impl Default for ParseACL {
177    fn default() -> Self {
178        Self::new()
179    }
180}
181
182// Custom serialization for ParseACL to match the Parse Server format
183impl Serialize for ParseACL {
184    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
185    where
186        S: Serializer,
187    {
188        // Clean up permissions before serializing to remove entries that are effectively false
189        // However, Parse Server expects explicit false if one permission is set and the other is not.
190        // For simplicity, we'll serialize what's there. The server handles defaults.
191        // A more robust cleanup might be needed if specific server behavior is targeted.
192        serializer.collect_map(self.permissions.iter().filter_map(|(k, v)| {
193            // Only include if there's at least one explicit permission
194            if v.read.is_some() || v.write.is_some() {
195                Some((k, v))
196            } else {
197                None
198            }
199        }))
200    }
201}
202
203// Custom deserialization for ParseACL
204impl<'de> Deserialize<'de> for ParseACL {
205    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206    where
207        D: Deserializer<'de>,
208    {
209        let map = HashMap::<String, AccessLevel>::deserialize(deserializer)?;
210        Ok(ParseACL { permissions: map })
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    use super::*;
217    use serde_json;
218
219    #[test]
220    fn test_acl_new() {
221        let acl = ParseACL::new();
222        assert!(acl.permissions.is_empty());
223    }
224
225    #[test]
226    fn test_set_public_read_access() {
227        let mut acl = ParseACL::new();
228        acl.set_public_read_access(true);
229        assert_eq!(acl.permissions.get("*").unwrap().read, Some(true));
230        assert_eq!(acl.permissions.get("*").unwrap().write, None);
231
232        acl.set_public_read_access(false);
233        assert_eq!(acl.permissions.get("*").unwrap().read, Some(false));
234    }
235
236    #[test]
237    fn test_set_public_write_access() {
238        let mut acl = ParseACL::new();
239        acl.set_public_write_access(true);
240        assert_eq!(acl.permissions.get("*").unwrap().write, Some(true));
241        assert_eq!(acl.permissions.get("*").unwrap().read, None);
242
243        acl.set_public_write_access(false);
244        assert_eq!(acl.permissions.get("*").unwrap().write, Some(false));
245    }
246
247    #[test]
248    fn test_set_user_read_access() {
249        let mut acl = ParseACL::new();
250        acl.set_user_read_access("userId123", true);
251        assert_eq!(acl.permissions.get("userId123").unwrap().read, Some(true));
252    }
253
254    #[test]
255    fn test_set_user_write_access() {
256        let mut acl = ParseACL::new();
257        acl.set_user_write_access("userId123", true);
258        assert_eq!(acl.permissions.get("userId123").unwrap().write, Some(true));
259    }
260
261    #[test]
262    fn test_set_role_read_access() {
263        let mut acl = ParseACL::new();
264        acl.set_role_read_access("Admin", true);
265        assert_eq!(acl.permissions.get("role:Admin").unwrap().read, Some(true));
266    }
267
268    #[test]
269    fn test_set_role_write_access() {
270        let mut acl = ParseACL::new();
271        acl.set_role_write_access("Editor", true);
272        assert_eq!(
273            acl.permissions.get("role:Editor").unwrap().write,
274            Some(true)
275        );
276    }
277
278    #[test]
279    fn test_acl_serialization_empty() {
280        let acl = ParseACL::new();
281        let json_string = serde_json::to_string(&acl).unwrap();
282        assert_eq!(json_string, "{}");
283    }
284
285    #[test]
286    fn test_acl_serialization_public_read() {
287        let mut acl = ParseACL::new();
288        acl.set_public_read_access(true);
289        let json_string = serde_json::to_string(&acl).unwrap();
290        assert_eq!(json_string, "{\"*\":{\"read\":true}}");
291    }
292
293    #[test]
294    fn test_acl_serialization_public_write() {
295        let mut acl = ParseACL::new();
296        acl.set_public_write_access(true);
297        let json_string = serde_json::to_string(&acl).unwrap();
298        assert_eq!(json_string, "{\"*\":{\"write\":true}}");
299    }
300
301    #[test]
302    fn test_acl_serialization_public_read_write() {
303        let mut acl = ParseACL::new();
304        acl.set_public_read_access(true);
305        acl.set_public_write_access(false);
306        let json_string = serde_json::to_string(&acl).unwrap();
307        assert_eq!(json_string, "{\"*\":{\"read\":true,\"write\":false}}");
308    }
309
310    #[test]
311    fn test_acl_serialization_user_and_role() {
312        let mut acl = ParseACL::new();
313        acl.set_user_read_access("user1", true);
314        acl.set_role_write_access("Admin", true);
315        let json_string = serde_json::to_string(&acl).unwrap();
316        // HashMap order is not guaranteed, so check for content
317        assert!(json_string.contains("\"user1\":{\"read\":true}"));
318        assert!(json_string.contains("\"role:Admin\":{\"write\":true}"));
319        assert!(json_string.starts_with("{") && json_string.ends_with("}"));
320    }
321
322    #[test]
323    fn test_acl_deserialization_empty() {
324        let json_string = "{}";
325        let acl: ParseACL = serde_json::from_str(json_string).unwrap();
326        assert!(acl.permissions.is_empty());
327    }
328
329    #[test]
330    fn test_acl_deserialization_public_read() {
331        let json_string = "{\"*\":{\"read\":true}}";
332        let acl: ParseACL = serde_json::from_str(json_string).unwrap();
333        assert_eq!(acl.permissions.get("*").unwrap().read, Some(true));
334        assert_eq!(acl.permissions.get("*").unwrap().write, None);
335    }
336
337    #[test]
338    fn test_acl_deserialization_user_and_role() {
339        let json_string = "{\"user1\":{\"read\":true},\"role:Admin\":{\"write\":true}}";
340        let acl: ParseACL = serde_json::from_str(json_string).unwrap();
341        assert_eq!(acl.permissions.get("user1").unwrap().read, Some(true));
342        assert_eq!(acl.permissions.get("role:Admin").unwrap().write, Some(true));
343    }
344
345    #[test]
346    fn test_acl_default() {
347        let acl: ParseACL = Default::default();
348        assert!(acl.permissions.is_empty());
349    }
350}