1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! Access-control roles for graph admin and query surfaces.
use crate::error::PostgresGraphError;
/// Role required for graph operations.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum GraphRole {
/// Read-only graph queries.
Reader,
/// Registration, build, sync, and maintenance.
Admin,
}
impl GraphRole {
/// Returns Ok when `self` satisfies `required`.
///
/// # Errors
///
/// Returns [`PostgresGraphError::AccessDenied`] when the role is insufficient.
///
/// # Performance
///
/// This method is `O(1)`.
pub const fn satisfies(self, required: Self) -> Result<(), PostgresGraphError> {
match (self, required) {
(Self::Admin, _) | (Self::Reader, Self::Reader) => Ok(()),
(Self::Reader, Self::Admin) => Err(PostgresGraphError::AccessDenied {
required,
actual: self,
}),
}
}
/// Returns Ok when `self` satisfies `required`.
///
/// # Errors
///
/// Returns [`PostgresGraphError::AccessDenied`] when the role is insufficient.
///
/// # Performance
///
/// This method is `O(1)`.
pub const fn require(self, required: Self) -> Result<(), PostgresGraphError> {
self.satisfies(required)
}
}
#[cfg(kani)]
mod proofs {
use super::GraphRole;
/// Admin satisfies every required role.
#[kani::proof]
fn admin_satisfies_all() {
let admin = GraphRole::Admin;
assert!(admin.satisfies(GraphRole::Reader).is_ok());
assert!(admin.satisfies(GraphRole::Admin).is_ok());
}
/// Reader satisfies reader but not admin.
#[kani::proof]
fn reader_lattice() {
let reader = GraphRole::Reader;
assert!(reader.satisfies(GraphRole::Reader).is_ok());
assert!(reader.satisfies(GraphRole::Admin).is_err());
}
}