backbone_auth/
permissions.rs1use anyhow::Result;
25use std::collections::HashMap;
26
27pub trait PermissionLike: Clone + Send + Sync {
35 fn name(&self) -> &str;
37
38 fn resource(&self) -> &str;
40
41 fn action(&self) -> &str;
43
44 fn description(&self) -> Option<&str> { None }
46}
47
48pub trait RoleLike: Clone + Send + Sync {
52 type Permission: PermissionLike;
54
55 fn name(&self) -> &str;
57
58 fn description(&self) -> Option<&str> { None }
60
61 fn permissions(&self) -> &[Self::Permission];
63}
64
65pub trait PermissionChecker: Send + Sync {
69 fn has_permission(&self, user_id: &str, permission: &str) -> Result<bool>;
71
72 fn has_role(&self, user_id: &str, role_name: &str) -> Result<bool>;
74
75 fn get_user_roles(&self, user_id: &str) -> Vec<String>;
77}
78
79#[derive(Debug, Clone, PartialEq, Eq, Hash)]
85pub struct SimplePermission {
86 pub name: String,
87 pub resource: String,
88 pub action: String,
89 pub description: Option<String>,
90}
91
92impl PermissionLike for SimplePermission {
93 fn name(&self) -> &str { &self.name }
94 fn resource(&self) -> &str { &self.resource }
95 fn action(&self) -> &str { &self.action }
96 fn description(&self) -> Option<&str> { self.description.as_deref() }
97}
98
99#[derive(Debug, Clone)]
101pub struct SimpleRole {
102 pub name: String,
103 pub description: Option<String>,
104 pub permissions: Vec<SimplePermission>,
105}
106
107impl RoleLike for SimpleRole {
108 type Permission = SimplePermission;
109
110 fn name(&self) -> &str { &self.name }
111 fn description(&self) -> Option<&str> { self.description.as_deref() }
112 fn permissions(&self) -> &[SimplePermission] { &self.permissions }
113}
114
115pub struct InMemoryPermissionService<R: RoleLike = SimpleRole> {
120 roles: HashMap<String, R>,
121 user_roles: HashMap<String, Vec<String>>, }
123
124impl InMemoryPermissionService<SimpleRole> {
125 pub fn new() -> Self {
127 let mut roles = HashMap::new();
128
129 roles.insert("admin".to_string(), SimpleRole {
131 name: "admin".to_string(),
132 description: Some("Administrator with full access".to_string()),
133 permissions: vec![
134 SimplePermission {
135 name: "admin:all".to_string(),
136 description: Some("Full administrative access".to_string()),
137 resource: "*".to_string(),
138 action: "*".to_string(),
139 },
140 ],
141 });
142
143 roles.insert("user".to_string(), SimpleRole {
144 name: "user".to_string(),
145 description: Some("Regular user with basic permissions".to_string()),
146 permissions: vec![
147 SimplePermission {
148 name: "user:read".to_string(),
149 description: Some("Read own user data".to_string()),
150 resource: "user".to_string(),
151 action: "read".to_string(),
152 },
153 SimplePermission {
154 name: "user:write".to_string(),
155 description: Some("Update own user data".to_string()),
156 resource: "user".to_string(),
157 action: "write".to_string(),
158 },
159 ],
160 });
161
162 Self {
163 roles,
164 user_roles: HashMap::new(),
165 }
166 }
167}
168
169impl<R: RoleLike> InMemoryPermissionService<R> {
170 pub fn empty() -> Self {
172 Self {
173 roles: HashMap::new(),
174 user_roles: HashMap::new(),
175 }
176 }
177
178 fn check_permission_internal(&self, user_id: &str, permission: &str) -> Result<bool> {
180 let user_role_names = match self.user_roles.get(user_id) {
181 Some(roles) => roles,
182 None => return Ok(false),
183 };
184
185 for role_name in user_role_names {
186 if let Some(role) = self.roles.get(role_name) {
187 for perm in role.permissions() {
188 if perm.name() == permission {
190 return Ok(true);
191 }
192
193 if perm.resource() == "*" && perm.action() == "*" {
195 return Ok(true);
196 }
197
198 let required_parts: Vec<&str> = permission.split(':').collect();
200 let perm_parts: Vec<&str> = perm.name().split(':').collect();
201
202 if required_parts.len() == 2 && perm_parts.len() == 2 {
203 let resource_match = perm_parts[0] == "*" || perm_parts[0] == required_parts[0];
204 let action_match = perm_parts[1] == "*" || perm_parts[1] == required_parts[1];
205
206 if resource_match && action_match {
207 return Ok(true);
208 }
209 }
210 }
211 }
212 }
213
214 Ok(false)
215 }
216
217 pub fn get_user_permissions(&self, user_id: &str) -> Result<Vec<SimplePermission>> {
219 let mut permissions = Vec::new();
220 let mut permission_names = std::collections::HashSet::new();
221
222 let user_role_names = match self.user_roles.get(user_id) {
223 Some(roles) => roles,
224 None => return Ok(permissions),
225 };
226
227 for role_name in user_role_names {
228 if let Some(role) = self.roles.get(role_name) {
229 for perm in role.permissions() {
230 if permission_names.insert(perm.name().to_string()) {
231 permissions.push(SimplePermission {
232 name: perm.name().to_string(),
233 resource: perm.resource().to_string(),
234 action: perm.action().to_string(),
235 description: perm.description().map(String::from),
236 });
237 }
238 }
239 }
240 }
241
242 Ok(permissions)
243 }
244
245 pub fn add_role(&mut self, role: R) -> Result<()> {
247 self.roles.insert(role.name().to_string(), role);
248 Ok(())
249 }
250
251 pub fn get_role(&self, role_name: &str) -> Option<&R> {
253 self.roles.get(role_name)
254 }
255
256 pub fn list_roles(&self) -> Vec<&R> {
258 self.roles.values().collect()
259 }
260
261 pub fn assign_role(&mut self, user_id: &str, role_name: &str) -> Result<()> {
263 if !self.roles.contains_key(role_name) {
264 return Err(anyhow::anyhow!("Role '{}' does not exist", role_name));
265 }
266
267 let user_roles = self.user_roles.entry(user_id.to_string()).or_default();
268
269 if user_roles.contains(&role_name.to_string()) {
270 return Err(anyhow::anyhow!("User already has role '{}'", role_name));
271 }
272
273 user_roles.push(role_name.to_string());
274 Ok(())
275 }
276
277 pub fn remove_role(&mut self, user_id: &str, role_name: &str) -> Result<()> {
279 let user_roles = self.user_roles.get_mut(user_id)
280 .ok_or_else(|| anyhow::anyhow!("User has no roles assigned"))?;
281
282 let original_len = user_roles.len();
283 user_roles.retain(|r| r != role_name);
284
285 if user_roles.len() == original_len {
286 return Err(anyhow::anyhow!("User does not have role '{}'", role_name));
287 }
288
289 if user_roles.is_empty() {
290 self.user_roles.remove(user_id);
291 }
292
293 Ok(())
294 }
295
296 fn has_role_internal(&self, user_id: &str, role_name: &str) -> bool {
298 self.user_roles.get(user_id)
299 .map(|roles| roles.contains(&role_name.to_string()))
300 .unwrap_or(false)
301 }
302
303 fn get_user_roles_internal(&self, user_id: &str) -> Vec<String> {
305 self.user_roles.get(user_id)
306 .cloned()
307 .unwrap_or_default()
308 }
309}
310
311impl<R: RoleLike> PermissionChecker for InMemoryPermissionService<R> {
313 fn has_permission(&self, user_id: &str, permission: &str) -> Result<bool> {
314 self.check_permission_internal(user_id, permission)
315 }
316
317 fn has_role(&self, user_id: &str, role_name: &str) -> Result<bool> {
318 Ok(self.has_role_internal(user_id, role_name))
319 }
320
321 fn get_user_roles(&self, user_id: &str) -> Vec<String> {
322 self.get_user_roles_internal(user_id)
323 }
324}
325
326impl Default for InMemoryPermissionService<SimpleRole> {
327 fn default() -> Self {
328 Self::new()
329 }
330}
331
332pub type PermissionService = InMemoryPermissionService<SimpleRole>;
341
342pub type Permission = SimplePermission;
346
347pub type Role = SimpleRole;