haematite_cli/serve/
access.rs1use std::collections::{BTreeMap, BTreeSet};
4use std::sync::Arc;
5
6use crate::serve::storage::Failure;
7
8type DeniedDocuments = BTreeMap<String, BTreeSet<String>>;
9
10pub struct Access {
34 denied_documents: Option<Arc<DeniedDocuments>>,
35}
36
37impl Access {
38 pub(in crate::serve) fn can_read_collection(&self, collection: &str) -> bool {
39 self.denied_documents
40 .as_ref()
41 .and_then(|denied| denied.get(collection))
42 .is_none_or(BTreeSet::is_empty)
43 }
44
45 pub(in crate::serve) fn can_read_store(&self) -> bool {
46 self.denied_documents
47 .as_ref()
48 .is_none_or(|denied| denied.values().all(BTreeSet::is_empty))
49 }
50
51 pub(in crate::serve) fn can_read_document(&self, collection: &str, id: &str) -> bool {
52 self.denied_documents
53 .as_ref()
54 .and_then(|denied| denied.get(collection))
55 .is_none_or(|documents| !documents.contains(id))
56 }
57}
58
59mod sealed {
60 pub trait Source {}
61}
62
63pub trait Source: sealed::Source + Send + Sync {
65 fn access(&self) -> Result<Access, Failure>;
70}
71
72pub struct Standalone;
74
75impl sealed::Source for Standalone {}
76
77impl Source for Standalone {
78 fn access(&self) -> Result<Access, Failure> {
79 Ok(Access {
80 denied_documents: None,
81 })
82 }
83}
84
85pub fn request(source: &dyn Source) -> Result<Access, Failure> {
90 source.access()
91}
92
93#[cfg(test)]
94pub(super) struct TestSource {
95 denied_documents: Arc<DeniedDocuments>,
96 calls: std::sync::atomic::AtomicUsize,
97 unavailable: bool,
98}
99
100#[cfg(test)]
101impl TestSource {
102 pub(super) fn denying(documents: &[(&str, &str)]) -> Self {
103 let mut denied: DeniedDocuments = BTreeMap::new();
104 for (collection, id) in documents {
105 denied
106 .entry((*collection).to_owned())
107 .or_default()
108 .insert((*id).to_owned());
109 }
110 Self {
111 denied_documents: Arc::new(denied),
112 calls: std::sync::atomic::AtomicUsize::new(0),
113 unavailable: false,
114 }
115 }
116
117 pub(super) fn allowing_collection(mut self, collection: &str) -> Self {
118 Arc::make_mut(&mut self.denied_documents).insert(collection.to_owned(), BTreeSet::new());
119 self
120 }
121
122 pub(super) fn unavailable() -> Self {
123 Self {
124 unavailable: true,
125 ..Self::denying(&[])
126 }
127 }
128
129 pub(super) fn calls(&self) -> usize {
130 self.calls.load(std::sync::atomic::Ordering::Relaxed)
131 }
132}
133
134#[cfg(test)]
135impl sealed::Source for TestSource {}
136
137#[cfg(test)]
138impl Source for TestSource {
139 fn access(&self) -> Result<Access, Failure> {
140 self.calls
141 .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
142 if self.unavailable {
143 return Err(Failure::new(
144 "access_unavailable",
145 "request authority could not be resolved",
146 ));
147 }
148 Ok(Access {
149 denied_documents: Some(Arc::clone(&self.denied_documents)),
150 })
151 }
152}