1use serde::{Deserialize, Deserializer, Serialize, de};
2use std::{collections::HashSet, error, fmt};
3
4use crate::security::Permission;
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
8pub enum PosixAclQualifier {
9 UserObj,
11 User(u32),
13 GroupObj,
15 Group(u32),
17 Mask,
19 Other,
21}
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
25pub struct PosixAce {
26 pub(crate) qualifier: PosixAclQualifier,
28 pub(crate) permissions: Permission,
30}
31
32impl PosixAce {
33 pub const fn new(qualifier: PosixAclQualifier, permissions: Permission) -> Self {
35 Self {
36 qualifier,
37 permissions,
38 }
39 }
40 pub const fn qualifier(self) -> PosixAclQualifier {
42 self.qualifier
43 }
44 pub const fn permissions(self) -> Permission {
46 self.permissions
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
52pub struct PosixAcl {
53 entries: Vec<PosixAce>,
54}
55
56impl PosixAcl {
57 pub fn new(entries: Vec<PosixAce>) -> Result<Self, PosixAclError> {
59 validate(&entries)?;
60 Ok(Self { entries })
61 }
62
63 pub fn entries(&self) -> &[PosixAce] {
65 &self.entries
66 }
67}
68
69impl<'de> Deserialize<'de> for PosixAcl {
70 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
71 where
72 D: Deserializer<'de>,
73 {
74 #[derive(Deserialize)]
75 struct Wire {
76 entries: Vec<PosixAce>,
77 }
78 let wire = Wire::deserialize(deserializer)?;
79 Self::new(wire.entries).map_err(de::Error::custom)
80 }
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Eq)]
85#[non_exhaustive]
86pub enum PosixAclError {
87 Empty,
89 Missing(PosixAclQualifier),
91 Duplicate(PosixAclQualifier),
93 MissingMask,
95}
96
97impl fmt::Display for PosixAclError {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 match self {
100 Self::Empty => f.write_str("ACL must contain entries"),
101 Self::Missing(qualifier) => write!(f, "ACL is missing {qualifier} entry"),
102 Self::Duplicate(qualifier) => write!(f, "ACL contains duplicate {qualifier} entries"),
103 Self::MissingMask => f.write_str("ACL with named entries must contain a mask"),
104 }
105 }
106}
107
108impl error::Error for PosixAclError {}
109
110impl fmt::Display for PosixAclQualifier {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 match self {
113 Self::UserObj => f.write_str("owner user"),
114 Self::User(id) => write!(f, "user {id}"),
115 Self::GroupObj => f.write_str("owner group"),
116 Self::Group(id) => write!(f, "group {id}"),
117 Self::Mask => f.write_str("mask"),
118 Self::Other => f.write_str("other"),
119 }
120 }
121}
122
123fn validate(entries: &[PosixAce]) -> Result<(), PosixAclError> {
124 if entries.is_empty() {
125 return Err(PosixAclError::Empty);
126 }
127 let mut qualifiers = HashSet::new();
128 let mut named = false;
129 for entry in entries {
130 if !qualifiers.insert(entry.qualifier) {
131 return Err(PosixAclError::Duplicate(entry.qualifier));
132 }
133 named |= matches!(
134 entry.qualifier,
135 PosixAclQualifier::User(_) | PosixAclQualifier::Group(_)
136 );
137 }
138 for required in [
139 PosixAclQualifier::UserObj,
140 PosixAclQualifier::GroupObj,
141 PosixAclQualifier::Other,
142 ] {
143 if !qualifiers.contains(&required) {
144 return Err(PosixAclError::Missing(required));
145 }
146 }
147 if named && !qualifiers.contains(&PosixAclQualifier::Mask) {
148 return Err(PosixAclError::MissingMask);
149 }
150 Ok(())
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 fn ace(qualifier: PosixAclQualifier) -> PosixAce {
158 PosixAce {
159 qualifier,
160 permissions: Permission::empty(),
161 }
162 }
163
164 #[test]
165 fn validates_acl_shape() {
166 assert_eq!(PosixAcl::new(Vec::new()), Err(PosixAclError::Empty));
167 assert_eq!(
168 PosixAcl::new(vec![
169 ace(PosixAclQualifier::UserObj),
170 ace(PosixAclQualifier::GroupObj),
171 ace(PosixAclQualifier::Other),
172 ace(PosixAclQualifier::User(1)),
173 ]),
174 Err(PosixAclError::MissingMask)
175 );
176 PosixAcl::new(vec![
177 ace(PosixAclQualifier::UserObj),
178 ace(PosixAclQualifier::GroupObj),
179 ace(PosixAclQualifier::Other),
180 ace(PosixAclQualifier::User(1)),
181 ace(PosixAclQualifier::Mask),
182 ])
183 .unwrap();
184 }
185
186 #[test]
187 fn serde_round_trip_preserves_entries() {
188 let acl = PosixAcl::new(vec![
189 ace(PosixAclQualifier::Other),
190 ace(PosixAclQualifier::Mask),
191 ace(PosixAclQualifier::GroupObj),
192 ace(PosixAclQualifier::User(42)),
193 ace(PosixAclQualifier::UserObj),
194 ])
195 .unwrap();
196 let bytes = postcard::to_stdvec(&acl).unwrap();
197 assert_eq!(postcard::from_bytes::<PosixAcl>(&bytes).unwrap(), acl);
198 }
199}