Skip to main content

featherdb_core/
permissions.rs

1//! Table-level permissions for authorization
2//!
3//! This module provides the `Permissions` bitflags type used for controlling
4//! access to tables in FeatherDB.
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use std::fmt;
8
9bitflags::bitflags! {
10    /// Permission flags for table-level access control.
11    ///
12    /// These flags can be combined using bitwise OR to grant multiple permissions.
13    ///
14    /// # Example
15    ///
16    /// ```rust
17    /// use featherdb_core::Permissions;
18    ///
19    /// // Grant read-only access
20    /// let read_only = Permissions::SELECT;
21    ///
22    /// // Grant read-write access
23    /// let read_write = Permissions::SELECT | Permissions::INSERT | Permissions::UPDATE;
24    ///
25    /// // Grant full access
26    /// let full_access = Permissions::ALL;
27    ///
28    /// // Check if a permission is granted
29    /// assert!(full_access.contains(Permissions::DELETE));
30    /// ```
31    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
32    pub struct Permissions: u8 {
33        /// Permission to read data from the table (SELECT)
34        const SELECT = 0b0001;
35        /// Permission to insert new rows into the table (INSERT)
36        const INSERT = 0b0010;
37        /// Permission to modify existing rows in the table (UPDATE)
38        const UPDATE = 0b0100;
39        /// Permission to remove rows from the table (DELETE)
40        const DELETE = 0b1000;
41        /// All permissions (SELECT | INSERT | UPDATE | DELETE)
42        const ALL = Self::SELECT.bits() | Self::INSERT.bits() |
43                    Self::UPDATE.bits() | Self::DELETE.bits();
44    }
45}
46
47impl Permissions {
48    /// Create permissions from a list of permission names
49    ///
50    /// Valid names (case-insensitive): "SELECT", "INSERT", "UPDATE", "DELETE", "ALL"
51    ///
52    /// # Example
53    ///
54    /// ```rust
55    /// use featherdb_core::Permissions;
56    ///
57    /// let perms = Permissions::from_names(&["SELECT", "INSERT"]).unwrap();
58    /// assert!(perms.contains(Permissions::SELECT));
59    /// assert!(perms.contains(Permissions::INSERT));
60    /// assert!(!perms.contains(Permissions::DELETE));
61    /// ```
62    pub fn from_names<S: AsRef<str>>(names: &[S]) -> Result<Self, String> {
63        let mut perms = Permissions::empty();
64        for name in names {
65            let name_upper = name.as_ref().to_uppercase();
66            match name_upper.as_str() {
67                "SELECT" => perms |= Permissions::SELECT,
68                "INSERT" => perms |= Permissions::INSERT,
69                "UPDATE" => perms |= Permissions::UPDATE,
70                "DELETE" => perms |= Permissions::DELETE,
71                "ALL" => perms |= Permissions::ALL,
72                _ => return Err(format!("Unknown permission: {}", name.as_ref())),
73            }
74        }
75        Ok(perms)
76    }
77
78    /// Convert permissions to a list of permission names
79    ///
80    /// # Example
81    ///
82    /// ```rust
83    /// use featherdb_core::Permissions;
84    ///
85    /// let perms = Permissions::SELECT | Permissions::DELETE;
86    /// let names = perms.to_names();
87    /// assert!(names.contains(&"SELECT"));
88    /// assert!(names.contains(&"DELETE"));
89    /// ```
90    pub fn to_names(&self) -> Vec<&'static str> {
91        let mut names = Vec::new();
92        if self.contains(Permissions::SELECT) {
93            names.push("SELECT");
94        }
95        if self.contains(Permissions::INSERT) {
96            names.push("INSERT");
97        }
98        if self.contains(Permissions::UPDATE) {
99            names.push("UPDATE");
100        }
101        if self.contains(Permissions::DELETE) {
102            names.push("DELETE");
103        }
104        names
105    }
106
107    /// Get a human-readable description of the permissions
108    pub fn description(&self) -> String {
109        if *self == Permissions::ALL {
110            return "ALL".to_string();
111        }
112        if self.is_empty() {
113            return "NONE".to_string();
114        }
115        self.to_names().join(", ")
116    }
117}
118
119impl Default for Permissions {
120    fn default() -> Self {
121        Permissions::empty()
122    }
123}
124
125impl fmt::Display for Permissions {
126    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127        write!(f, "{}", self.description())
128    }
129}
130
131// Custom serialization - serialize as u8 for efficiency
132impl Serialize for Permissions {
133    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
134    where
135        S: Serializer,
136    {
137        serializer.serialize_u8(self.bits())
138    }
139}
140
141impl<'de> Deserialize<'de> for Permissions {
142    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
143    where
144        D: Deserializer<'de>,
145    {
146        let bits = u8::deserialize(deserializer)?;
147        Permissions::from_bits(bits)
148            .ok_or_else(|| serde::de::Error::custom(format!("Invalid permission bits: {}", bits)))
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use super::*;
155
156    #[test]
157    fn test_permissions_individual() {
158        assert_eq!(Permissions::SELECT.bits(), 0b0001);
159        assert_eq!(Permissions::INSERT.bits(), 0b0010);
160        assert_eq!(Permissions::UPDATE.bits(), 0b0100);
161        assert_eq!(Permissions::DELETE.bits(), 0b1000);
162    }
163
164    #[test]
165    fn test_permissions_all() {
166        let all = Permissions::ALL;
167        assert!(all.contains(Permissions::SELECT));
168        assert!(all.contains(Permissions::INSERT));
169        assert!(all.contains(Permissions::UPDATE));
170        assert!(all.contains(Permissions::DELETE));
171        assert_eq!(all.bits(), 0b1111);
172    }
173
174    #[test]
175    fn test_permissions_combine() {
176        let perms = Permissions::SELECT | Permissions::INSERT;
177        assert!(perms.contains(Permissions::SELECT));
178        assert!(perms.contains(Permissions::INSERT));
179        assert!(!perms.contains(Permissions::UPDATE));
180        assert!(!perms.contains(Permissions::DELETE));
181    }
182
183    #[test]
184    fn test_permissions_from_names() {
185        let perms = Permissions::from_names(&["SELECT", "INSERT"]).unwrap();
186        assert!(perms.contains(Permissions::SELECT));
187        assert!(perms.contains(Permissions::INSERT));
188        assert!(!perms.contains(Permissions::UPDATE));
189
190        // Case insensitive
191        let perms = Permissions::from_names(&["select", "DELETE"]).unwrap();
192        assert!(perms.contains(Permissions::SELECT));
193        assert!(perms.contains(Permissions::DELETE));
194
195        // ALL
196        let perms = Permissions::from_names(&["ALL"]).unwrap();
197        assert_eq!(perms, Permissions::ALL);
198
199        // Invalid name
200        let result = Permissions::from_names(&["INVALID"]);
201        assert!(result.is_err());
202    }
203
204    #[test]
205    fn test_permissions_to_names() {
206        let perms = Permissions::SELECT | Permissions::DELETE;
207        let names = perms.to_names();
208        assert_eq!(names.len(), 2);
209        assert!(names.contains(&"SELECT"));
210        assert!(names.contains(&"DELETE"));
211
212        let empty = Permissions::empty();
213        assert!(empty.to_names().is_empty());
214    }
215
216    #[test]
217    fn test_permissions_display() {
218        assert_eq!(Permissions::ALL.to_string(), "ALL");
219        assert_eq!(Permissions::empty().to_string(), "NONE");
220        assert_eq!(Permissions::SELECT.to_string(), "SELECT");
221        assert_eq!(
222            (Permissions::SELECT | Permissions::INSERT).to_string(),
223            "SELECT, INSERT"
224        );
225    }
226
227    #[test]
228    fn test_permissions_default() {
229        let perms = Permissions::default();
230        assert!(perms.is_empty());
231    }
232
233    #[test]
234    fn test_permissions_serialization() {
235        let perms = Permissions::SELECT | Permissions::UPDATE;
236        let json = serde_json::to_string(&perms).unwrap();
237        // Should serialize as u8 (5 = 0b0101)
238        assert_eq!(json, "5");
239        let deserialized: Permissions = serde_json::from_str(&json).unwrap();
240        assert_eq!(perms, deserialized);
241    }
242
243    #[test]
244    fn test_permissions_remove() {
245        let mut perms = Permissions::ALL;
246        perms.remove(Permissions::DELETE);
247        assert!(perms.contains(Permissions::SELECT));
248        assert!(perms.contains(Permissions::INSERT));
249        assert!(perms.contains(Permissions::UPDATE));
250        assert!(!perms.contains(Permissions::DELETE));
251    }
252}