zookeeper_client/
acl.rs

1use compact_str::CompactString;
2
3/// Permission expresses rights accessors should have to operate on attached node.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
5pub struct Permission(i32);
6
7#[rustfmt::skip]
8impl Permission {
9    /// Permission to get data from a node and list its children.
10    pub const READ:     Permission = Permission(1);
11
12    /// Permission to set data for a node.
13    pub const WRITE:    Permission = Permission(2);
14
15    /// Permission to create a child node.
16    pub const CREATE:   Permission = Permission(4);
17
18    /// Permission to delete a child node.
19    pub const DELETE:   Permission = Permission(8);
20
21    /// Permission to set ACL permissions.
22    pub const ADMIN:    Permission = Permission(16);
23
24    /// Permission to do all above.
25    pub const ALL:      Permission = Permission(31);
26
27    /// Permission to do none of above.
28    pub const NONE:     Permission = Permission(0);
29
30    pub(crate) fn into_raw(self) -> i32 {
31        self.0
32    }
33
34    pub(crate) fn from_raw(raw: i32) -> Permission {
35        Permission(raw)
36    }
37
38    /// Test whether this permission has given permission. Same as `self & perm == perm`.
39    pub fn has(self, perm: Permission) -> bool {
40        (self.0 & perm.0) == perm.0
41    }
42}
43
44impl std::ops::BitAnd for Permission {
45    type Output = Self;
46
47    /// Compute common permission between the two.
48    fn bitand(self, rhs: Self) -> Self::Output {
49        Permission(self.0 & rhs.0)
50    }
51}
52
53impl std::ops::BitOr for Permission {
54    type Output = Self;
55
56    /// Compute sum permission of the two.
57    fn bitor(self, rhs: Self) -> Self::Output {
58        Permission(self.0 | rhs.0)
59    }
60}
61
62impl std::fmt::Display for Permission {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        if *self == Permission::ALL {
65            return f.write_str("ALL");
66        } else if *self == Permission::NONE {
67            return f.write_str("NONE");
68        }
69        [
70            (Permission::READ, "READ"),
71            (Permission::WRITE, "WRITE"),
72            (Permission::CREATE, "CREATE"),
73            (Permission::DELETE, "DELETE"),
74            (Permission::ADMIN, "ADMIN"),
75        ]
76        .into_iter()
77        .filter_map(|(perm, str)| if self.has(perm) { Some(str) } else { None })
78        .enumerate()
79        .try_for_each(|(i, str)| if i == 0 { f.write_str(str) } else { write!(f, "|{str}") })
80    }
81}
82
83/// Acl expresses node permission for node accessors.
84#[derive(Clone, Debug, PartialEq, Eq)]
85pub struct Acl {
86    permission: Permission,
87    auth_id: AuthId,
88}
89
90/// AuthId represents authenticated identity and expresses authentication requirement in [Acl].
91#[derive(Clone, Debug, PartialEq, Eq)]
92pub struct AuthId {
93    scheme: CompactString,
94    id: CompactString,
95}
96
97impl AuthId {
98    /// Constructs an auth id with specified id under given scheme.
99    pub fn new(scheme: &str, id: &str) -> AuthId {
100        AuthId { scheme: CompactString::new(scheme), id: CompactString::new(id) }
101    }
102
103    /// Const alternative to [AuthId::new].
104    pub const fn new_const(scheme: &'static str, id: &'static str) -> AuthId {
105        AuthId { scheme: CompactString::new_inline(scheme), id: CompactString::new_inline(id) }
106    }
107
108    /// Returns the scheme this auth id serve for.
109    pub fn scheme(&self) -> &str {
110        self.scheme.as_str()
111    }
112
113    /// Returns the identity this auth id represent for.
114    pub fn id(&self) -> &str {
115        self.id.as_str()
116    }
117
118    /// Auth id that could represent anyone in access.
119    pub const fn anyone() -> AuthId {
120        Self::new_const("world", "anyone")
121    }
122
123    /// Auth id that could only represent one that has same authes.
124    pub const fn authed() -> AuthId {
125        Self::new_const("auth", "")
126    }
127}
128
129/// AuthUser represents display info for authenticated identity.
130#[derive(Clone, Debug, PartialEq, Eq)]
131pub struct AuthUser {
132    scheme: CompactString,
133    user: CompactString,
134}
135
136impl AuthUser {
137    /// Constructs an auth user with auth scheme and user name.
138    pub fn new(scheme: &str, user: &str) -> AuthUser {
139        AuthUser { scheme: CompactString::new(scheme), user: CompactString::new(user) }
140    }
141
142    /// Returns authed scheme.
143    pub fn scheme(&self) -> &str {
144        self.scheme.as_str()
145    }
146
147    /// Returns authed user name.
148    pub fn user(&self) -> &str {
149        self.user.as_str()
150    }
151}
152
153static ANYONE_ALL: [Acl; 1] = [Acl::new_const(Permission::ALL, "world", "anyone")];
154static ANYONE_READ: [Acl; 1] = [Acl::new_const(Permission::READ, "world", "anyone")];
155static CREATOR_ALL: [Acl; 1] = [Acl::new_const(Permission::ALL, "auth", "")];
156
157impl Acl {
158    /// Constructs an acl with specified permission for given auth id.
159    pub fn new(permission: Permission, auth_id: AuthId) -> Acl {
160        Acl { permission, auth_id }
161    }
162
163    /// Const alternative to [Acl::new].
164    pub const fn new_const(permission: Permission, scheme: &'static str, id: &'static str) -> Acl {
165        Acl {
166            permission,
167            auth_id: AuthId { scheme: CompactString::new_inline(scheme), id: CompactString::new_inline(id) },
168        }
169    }
170
171    /// Returns the permission this auth id has.
172    pub fn permission(&self) -> Permission {
173        self.permission
174    }
175
176    /// Returns the auth id this acl serve for.
177    pub fn auth_id(&self) -> &AuthId {
178        &self.auth_id
179    }
180
181    /// Returns the scheme this auth id serve for.
182    pub fn scheme(&self) -> &str {
183        self.auth_id.scheme.as_str()
184    }
185
186    /// Returns the identity this auth id represent for.
187    pub fn id(&self) -> &str {
188        self.auth_id.id.as_str()
189    }
190}
191
192#[derive(Clone, Copy, Debug, PartialEq, Eq)]
193enum AclsInner<'a> {
194    AnyoneAll,
195    AnyoneRead,
196    CreatorAll,
197    Acls { acls: &'a [Acl] },
198}
199
200/// List of [Acl]s to carry different permissions for different auth identities.
201#[derive(Clone, Copy, Debug, PartialEq, Eq)]
202pub struct Acls<'a> {
203    inner: AclsInner<'a>,
204}
205
206impl<'a> Acls<'a> {
207    /// Wraps list of [Acl] as [Acls]. See also [Acls::from].
208    pub fn new(acls: &'a [Acl]) -> Self {
209        Self { inner: AclsInner::Acls { acls } }
210    }
211
212    /// Returns acls that expresses anyone can have full permissions over nodes created by this
213    /// session.
214    pub const fn anyone_all() -> Acls<'static> {
215        Acls { inner: AclsInner::AnyoneAll }
216    }
217
218    /// Returns acls that expresses anyone can read nodes created by this session.
219    pub const fn anyone_read() -> Acls<'static> {
220        Acls { inner: AclsInner::AnyoneRead }
221    }
222
223    /// Returns acls that expresses anyone who has same auth as creator can have full permissions
224    /// over nodes created by this session.
225    pub const fn creator_all() -> Acls<'static> {
226        Acls { inner: AclsInner::CreatorAll }
227    }
228}
229
230impl<'a> std::ops::Deref for Acls<'a> {
231    type Target = [Acl];
232
233    fn deref(&self) -> &'a [Acl] {
234        match self.inner {
235            AclsInner::AnyoneAll => &ANYONE_ALL,
236            AclsInner::AnyoneRead => &ANYONE_READ,
237            AclsInner::CreatorAll => &CREATOR_ALL,
238            AclsInner::Acls { acls } => acls,
239        }
240    }
241}
242
243impl<'a> From<&'a [Acl]> for Acls<'a> {
244    fn from(acls: &'a [Acl]) -> Self {
245        Self::new(acls)
246    }
247}
248
249#[cfg(test)]
250mod tests {
251    use pretty_assertions::assert_eq;
252
253    use super::*;
254
255    #[test]
256    fn permission_test() {
257        let all = Permission::READ | Permission::WRITE | Permission::CREATE | Permission::DELETE | Permission::ADMIN;
258
259        assert!(Permission::ALL.has(all));
260        assert_eq!(Permission::ALL & Permission::ALL, Permission::ALL);
261
262        assert_eq!(Permission::ALL & Permission::READ, Permission::READ);
263        assert_eq!(Permission::READ & Permission::READ, Permission::READ);
264        assert_eq!(Permission::CREATE & Permission::READ, Permission::NONE);
265    }
266
267    #[test]
268    fn permission_display() {
269        assert_eq!(Permission::ALL.to_string(), "ALL");
270        assert_eq!(Permission::ADMIN.to_string(), "ADMIN");
271
272        let perms = Permission::READ | Permission::WRITE | Permission::CREATE | Permission::DELETE;
273        assert_eq!(perms.to_string(), "READ|WRITE|CREATE|DELETE");
274    }
275
276    #[test]
277    fn test_acls() {
278        assert_eq!(&Acls::anyone_all() as &[Acl], &ANYONE_ALL);
279        assert_eq!(&Acls::anyone_read() as &[Acl], &ANYONE_READ);
280        assert_eq!(&Acls::creator_all() as &[Acl], &CREATOR_ALL);
281        assert_eq!(&Acls::new(&CREATOR_ALL) as &[Acl], &CREATOR_ALL);
282    }
283}