steel_core/permission/
manager.rs1use std::{error::Error, fmt, sync::Arc};
2
3use futures::future::BoxFuture;
4use steel_utils::locks::{AsyncMutex, SyncRwLock};
5
6use super::{
7 PermissionConfigError, PermissionGroups, PermissionGroupsConfig, PermissionMetadataSet,
8 PermissionSet,
9};
10
11pub trait PermissionGroupStore: Send + Sync {
13 fn save_groups(
15 &self,
16 config: PermissionGroupsConfig,
17 ) -> BoxFuture<'static, Result<(), PermissionGroupStoreError>>;
18}
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct PermissionGroupStoreError {
23 message: String,
24}
25
26impl PermissionGroupStoreError {
27 #[must_use]
29 pub fn new(message: impl Into<String>) -> Self {
30 Self {
31 message: message.into(),
32 }
33 }
34}
35
36impl fmt::Display for PermissionGroupStoreError {
37 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38 formatter.write_str(&self.message)
39 }
40}
41
42impl Error for PermissionGroupStoreError {}
43
44#[derive(Clone, Debug, PartialEq, Eq)]
45struct PermissionGroupManagerState {
46 config: PermissionGroupsConfig,
47 groups: PermissionGroups,
48}
49
50pub struct PermissionGroupManager {
52 updates: AsyncMutex<()>,
53 state: SyncRwLock<PermissionGroupManagerState>,
54 store: Option<Arc<dyn PermissionGroupStore>>,
55}
56
57impl fmt::Debug for PermissionGroupManager {
58 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
59 formatter
60 .debug_struct("PermissionGroupManager")
61 .field("updates", &self.updates)
62 .field("state", &self.state)
63 .field("store", &self.store.as_ref().map(|_| "<group store>"))
64 .finish()
65 }
66}
67
68impl PermissionGroupManager {
69 pub fn new(
75 config: PermissionGroupsConfig,
76 store: Option<Arc<dyn PermissionGroupStore>>,
77 ) -> Result<Self, PermissionConfigError> {
78 let groups = PermissionGroups::from_config(config.clone())?;
79 Ok(Self {
80 updates: AsyncMutex::new(()),
81 state: SyncRwLock::new(PermissionGroupManagerState { config, groups }),
82 store,
83 })
84 }
85
86 pub fn transient(config: PermissionGroupsConfig) -> Result<Self, PermissionConfigError> {
92 Self::new(config, None)
93 }
94
95 #[must_use]
97 pub fn config_snapshot(&self) -> PermissionGroupsConfig {
98 self.state.read().config.clone()
99 }
100
101 #[must_use]
103 pub fn contains_group(&self, group: &str) -> bool {
104 self.state.read().groups.contains_group(group)
105 }
106
107 #[must_use]
109 pub fn group_names(&self) -> Vec<String> {
110 self.state.read().groups.groups().keys().cloned().collect()
111 }
112
113 #[must_use]
115 pub fn effective_permissions(
116 &self,
117 assigned_groups: &[String],
118 subject_permissions: &PermissionSet,
119 ) -> PermissionSet {
120 self.state
121 .read()
122 .groups
123 .effective_permissions(assigned_groups, subject_permissions)
124 }
125
126 #[must_use]
128 pub fn effective_metadata(
129 &self,
130 assigned_groups: &[String],
131 subject_metadata: &PermissionMetadataSet,
132 ) -> PermissionMetadataSet {
133 self.state
134 .read()
135 .groups
136 .effective_metadata(assigned_groups, subject_metadata)
137 }
138
139 pub async fn replace_config(
145 &self,
146 config: PermissionGroupsConfig,
147 ) -> Result<(), PermissionGroupManagerError> {
148 let _guard = self.updates.lock().await;
149 self.replace_config_locked(config).await
150 }
151
152 pub async fn update_config(
158 &self,
159 update: impl FnOnce(&mut PermissionGroupsConfig) + Send,
160 ) -> Result<(), PermissionGroupManagerError> {
161 let _guard = self.updates.lock().await;
162 let mut config = self.state.read().config.clone();
163 update(&mut config);
164 self.replace_config_locked(config).await
165 }
166
167 pub async fn try_update_config<T, E>(
173 &self,
174 update: impl FnOnce(&mut PermissionGroupsConfig) -> Result<T, E> + Send,
175 ) -> Result<T, PermissionGroupUpdateError<E>>
176 where
177 T: Send,
178 E: Send,
179 {
180 let _guard = self.updates.lock().await;
181 let current = self.state.read().config.clone();
182 let mut config = current.clone();
183 let result = update(&mut config).map_err(PermissionGroupUpdateError::Edit)?;
184 if config == current {
185 return Ok(result);
186 }
187
188 self.replace_config_locked(config)
189 .await
190 .map_err(PermissionGroupUpdateError::Manager)?;
191 Ok(result)
192 }
193
194 async fn replace_config_locked(
195 &self,
196 config: PermissionGroupsConfig,
197 ) -> Result<(), PermissionGroupManagerError> {
198 let groups = PermissionGroups::from_config(config.clone())?;
199 if let Some(store) = &self.store {
200 store.save_groups(config.clone()).await?;
201 }
202 *self.state.write() = PermissionGroupManagerState { config, groups };
203 Ok(())
204 }
205}
206
207#[derive(Clone, Debug, PartialEq, Eq)]
209pub enum PermissionGroupManagerError {
210 Config(PermissionConfigError),
212 Store(PermissionGroupStoreError),
214}
215
216impl fmt::Display for PermissionGroupManagerError {
217 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
218 match self {
219 Self::Config(error) => write!(formatter, "invalid permission groups config: {error}"),
220 Self::Store(error) => write!(formatter, "failed to store permission groups: {error}"),
221 }
222 }
223}
224
225impl Error for PermissionGroupManagerError {}
226
227impl From<PermissionConfigError> for PermissionGroupManagerError {
228 fn from(value: PermissionConfigError) -> Self {
229 Self::Config(value)
230 }
231}
232
233impl From<PermissionGroupStoreError> for PermissionGroupManagerError {
234 fn from(value: PermissionGroupStoreError) -> Self {
235 Self::Store(value)
236 }
237}
238
239#[derive(Clone, Debug, PartialEq, Eq)]
241pub enum PermissionGroupUpdateError<E> {
242 Edit(E),
244 Manager(PermissionGroupManagerError),
246}
247
248impl<E: fmt::Display> fmt::Display for PermissionGroupUpdateError<E> {
249 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
250 match self {
251 Self::Edit(error) => error.fmt(formatter),
252 Self::Manager(error) => error.fmt(formatter),
253 }
254 }
255}
256
257impl<E> Error for PermissionGroupUpdateError<E> where E: Error + 'static {}
258
259impl<E> From<PermissionGroupManagerError> for PermissionGroupUpdateError<E> {
260 fn from(value: PermissionGroupManagerError) -> Self {
261 Self::Manager(value)
262 }
263}