role_system/
query.rs

1//! Query interface for the role system.
2//!
3//! This module provides a high-level query API for analyzing and inspecting
4//! the role system state. It offers complex queries for finding subjects,
5//! roles, permissions, and generating statistics.
6
7use crate::{
8    core::RoleSystem, error::Result, permission::Permission, storage::Storage, subject::Subject,
9};
10use std::collections::{HashMap, HashSet};
11
12/// Query interface for role system analysis.
13pub struct RoleQuery<'a, S: Storage> {
14    system: &'a RoleSystem<S>,
15}
16
17/// Trait for providing query capabilities.
18pub trait RoleSystemQuery<S: Storage> {
19    /// Get a query interface for this role system.
20    fn query(&self) -> RoleQuery<'_, S>;
21}
22
23impl<S: Storage> RoleSystemQuery<S> for RoleSystem<S> {
24    fn query(&self) -> RoleQuery<'_, S> {
25        RoleQuery { system: self }
26    }
27}
28
29impl<'a, S: Storage> RoleQuery<'a, S> {
30    /// Find all subjects that have the specified role.
31    pub fn find_subjects_with_role(&self, role_name: &str) -> Result<Vec<String>> {
32        let mut subjects = Vec::new();
33
34        for entry in self.system.subject_roles() {
35            let (subject_id, roles) = (entry.key(), entry.value());
36            if roles.contains(role_name) {
37                subjects.push(subject_id.clone());
38            }
39        }
40
41        Ok(subjects)
42    }
43
44    /// Find all subjects that have any of the specified roles.
45    pub fn subjects_with_any_role(&self, role_names: &[&str]) -> Result<Vec<String>> {
46        let mut subjects = Vec::new();
47        let role_set: HashSet<&str> = role_names.iter().copied().collect();
48
49        for entry in self.system.subject_roles() {
50            let (subject_id, roles) = (entry.key(), entry.value());
51            if roles.iter().any(|role| role_set.contains(role.as_str())) {
52                subjects.push(subject_id.clone());
53            }
54        }
55
56        Ok(subjects)
57    }
58
59    /// Find all subjects that have all of the specified roles.
60    pub fn subjects_with_all_roles(&self, role_names: &[&str]) -> Result<Vec<String>> {
61        let mut subjects = Vec::new();
62        let role_set: HashSet<&str> = role_names.iter().copied().collect();
63
64        for entry in self.system.subject_roles() {
65            let (subject_id, roles) = (entry.key(), entry.value());
66            let subject_role_set: HashSet<&str> = roles.iter().map(|s| s.as_str()).collect();
67            if role_set.is_subset(&subject_role_set) {
68                subjects.push(subject_id.clone());
69            }
70        }
71
72        Ok(subjects)
73    }
74
75    /// Get all effective permissions for a subject.
76    pub fn effective_permissions(&self, subject: &Subject) -> Result<Vec<Permission>> {
77        let mut permissions = Vec::new();
78        let subject_roles = self.system.get_subject_roles(subject)?;
79
80        for role_name in subject_roles {
81            if let Some(role) = self.system.get_role(&role_name)? {
82                for permission in role.permissions().permissions() {
83                    if !permissions.contains(permission) {
84                        permissions.push(permission.clone());
85                    }
86                }
87            }
88        }
89
90        Ok(permissions)
91    }
92
93    /// Find all roles that contain a specific permission.
94    pub fn roles_with_permission(&self, permission: &Permission) -> Result<Vec<String>> {
95        let mut matching_roles = Vec::new();
96        let role_names = self.system.storage().list_roles()?;
97
98        for role_name in role_names {
99            if let Some(role) = self.system.storage().get_role(&role_name)?
100                && role.permissions().permissions().contains(permission)
101            {
102                matching_roles.push(role_name);
103            }
104        }
105
106        Ok(matching_roles)
107    }
108
109    /// Get role hierarchy information.
110    pub fn role_hierarchy(&self) -> HashMap<String, Vec<String>> {
111        let mut hierarchy = HashMap::new();
112
113        for entry in self.system.role_hierarchy() {
114            let (child, parents) = (entry.key(), entry.value());
115            hierarchy.insert(child.clone(), parents.iter().cloned().collect());
116        }
117
118        hierarchy
119    }
120
121    /// Find all parent roles of a given role.
122    pub fn parent_roles(&self, child_role: &str) -> Vec<String> {
123        if let Some(parents) = self.system.role_hierarchy().get(child_role) {
124            parents.iter().cloned().collect()
125        } else {
126            Vec::new()
127        }
128    }
129
130    /// Find all child roles of a given role.
131    pub fn child_roles(&self, parent_role: &str) -> Vec<String> {
132        let mut children = Vec::new();
133
134        for entry in self.system.role_hierarchy() {
135            let (child, parents) = (entry.key(), entry.value());
136            if parents.contains(parent_role) {
137                children.push(child.clone());
138            }
139        }
140
141        children
142    }
143
144    /// Generate system statistics.
145    pub fn system_statistics(&self) -> Result<SystemStatistics> {
146        let roles = self.system.storage().list_roles()?;
147        let mut total_permissions = 0;
148
149        for role_name in &roles {
150            if let Some(role) = self.system.storage().get_role(role_name)? {
151                total_permissions += role.permissions().permissions().len();
152            }
153        }
154
155        // Calculate unique subjects
156        let mut unique_subjects = HashSet::new();
157        for entry in self.system.subject_roles() {
158            unique_subjects.insert(entry.key().clone());
159        }
160
161        Ok(SystemStatistics {
162            total_roles: roles.len(),
163            total_permissions,
164            total_subjects: unique_subjects.len(),
165            total_role_assignments: self.count_role_assignments()?,
166        })
167    }
168
169    /// Get permission coverage statistics.
170    pub fn permission_coverage(&self, subjects: &[Subject]) -> Result<PermissionCoverage> {
171        let mut permission_counts: HashMap<String, usize> = HashMap::new();
172
173        for subject in subjects {
174            let permissions = self.effective_permissions(subject)?;
175            for permission in permissions {
176                let perm_str = format!("{}:{}", permission.action(), permission.resource_type());
177                *permission_counts.entry(perm_str).or_insert(0) += 1;
178            }
179        }
180
181        let all_roles = self.system.storage().list_roles()?;
182        let mut total_possible_permissions = 0;
183
184        for role_name in &all_roles {
185            if let Some(role) = self.system.storage().get_role(role_name)? {
186                total_possible_permissions += role.permissions().permissions().len();
187            }
188        }
189
190        Ok(PermissionCoverage {
191            permission_usage: permission_counts,
192            total_possible_permissions,
193            subjects_analyzed: subjects.len(),
194        })
195    }
196
197    /// Find unused roles (roles with no subjects assigned).
198    pub fn unused_roles(&self) -> Result<Vec<String>> {
199        let all_roles = self.system.storage().list_roles()?;
200        let mut used_roles = HashSet::new();
201
202        for entry in self.system.subject_roles() {
203            for role in entry.value().iter() {
204                used_roles.insert(role.clone());
205            }
206        }
207
208        Ok(all_roles
209            .into_iter()
210            .filter(|role| !used_roles.contains(role))
211            .collect())
212    }
213
214    /// Get the maximum depth of the role hierarchy.
215    pub fn max_hierarchy_depth(&self) -> Result<usize> {
216        let roles = self.system.storage().list_roles()?;
217        let mut max_depth = 0;
218
219        for role_name in roles {
220            let depth = self.calculate_role_depth(&role_name, &mut HashSet::new(), 0)?;
221            max_depth = max_depth.max(depth);
222        }
223
224        Ok(max_depth)
225    }
226
227    /// Calculate the depth of a specific role in the hierarchy.
228    fn calculate_role_depth(
229        &self,
230        role: &str,
231        visited: &mut HashSet<String>,
232        current_depth: usize,
233    ) -> Result<usize> {
234        if current_depth >= self.system.config().max_hierarchy_depth {
235            return Ok(current_depth);
236        }
237
238        if !visited.insert(role.to_string()) {
239            return Ok(current_depth); // Cycle detected
240        }
241
242        let mut max_child_depth = current_depth;
243
244        if let Some(parents) = self.system.role_hierarchy().get(role) {
245            for parent in parents.iter() {
246                let parent_depth = self.calculate_role_depth(parent, visited, current_depth + 1)?;
247                max_child_depth = max_child_depth.max(parent_depth);
248            }
249        }
250
251        visited.remove(role);
252        Ok(max_child_depth)
253    }
254
255    /// Count total role assignments across all subjects.
256    fn count_role_assignments(&self) -> Result<usize> {
257        let mut total = 0;
258        for entry in self.system.subject_roles() {
259            total += entry.value().len();
260        }
261        Ok(total)
262    }
263}
264
265/// System-wide statistics.
266#[derive(Debug, Clone)]
267pub struct SystemStatistics {
268    /// Total number of roles defined.
269    pub total_roles: usize,
270    /// Total number of permissions across all roles.
271    pub total_permissions: usize,
272    /// Total number of subjects with role assignments.
273    pub total_subjects: usize,
274    /// Total number of role assignments.
275    pub total_role_assignments: usize,
276}
277
278/// Permission coverage analysis.
279#[derive(Debug, Clone)]
280pub struct PermissionCoverage {
281    /// How many subjects have each permission.
282    pub permission_usage: HashMap<String, usize>,
283    /// Total number of unique permissions available.
284    pub total_possible_permissions: usize,
285    /// Number of subjects analyzed.
286    pub subjects_analyzed: usize,
287}
288
289#[cfg(test)]
290mod tests {
291    use super::*;
292    use crate::{
293        core::RoleSystem, permission::Permission, role::Role, storage::MemoryStorage,
294        subject::Subject,
295    };
296
297    #[test]
298    fn test_query_interface() {
299        let mut system = RoleSystem::<MemoryStorage>::new();
300
301        // Create test data
302        let admin_role = Role::new("admin")
303            .add_permission(Permission::new("read", "documents"))
304            .add_permission(Permission::new("write", "documents"));
305
306        let user_role = Role::new("user").add_permission(Permission::new("read", "documents"));
307
308        system.register_role(admin_role).unwrap();
309        system.register_role(user_role).unwrap();
310
311        let admin_user = Subject::user("admin1");
312        let regular_user = Subject::user("user1");
313
314        system.assign_role(&admin_user, "admin").unwrap();
315        system.assign_role(&regular_user, "user").unwrap();
316
317        // Test queries
318        let query = system.query();
319
320        let admin_subjects = query.find_subjects_with_role("admin").unwrap();
321        assert_eq!(admin_subjects.len(), 1);
322        assert!(admin_subjects.contains(&"admin1".to_string()));
323
324        let user_subjects = query.find_subjects_with_role("user").unwrap();
325        assert_eq!(user_subjects.len(), 1);
326        assert!(user_subjects.contains(&"user1".to_string()));
327
328        let read_permission = Permission::new("read", "documents");
329        let roles_with_read = query.roles_with_permission(&read_permission).unwrap();
330        assert_eq!(roles_with_read.len(), 2);
331        assert!(roles_with_read.contains(&"admin".to_string()));
332        assert!(roles_with_read.contains(&"user".to_string()));
333
334        let stats = query.system_statistics().unwrap();
335        assert_eq!(stats.total_roles, 2);
336        assert_eq!(stats.total_subjects, 2);
337        assert_eq!(stats.total_role_assignments, 2);
338    }
339
340    #[test]
341    fn test_permission_coverage() {
342        let mut system = RoleSystem::<MemoryStorage>::new();
343
344        let role = Role::new("test").add_permission(Permission::new("read", "docs"));
345
346        system.register_role(role).unwrap();
347
348        let user = Subject::user("test_user");
349        system.assign_role(&user, "test").unwrap();
350
351        let query = system.query();
352        let coverage = query.permission_coverage(&[user]).unwrap();
353
354        assert_eq!(coverage.subjects_analyzed, 1);
355        assert!(coverage.permission_usage.contains_key("read:docs"));
356    }
357
358    #[test]
359    fn test_hierarchy_queries() {
360        let mut system = RoleSystem::<MemoryStorage>::new();
361
362        let parent_role = Role::new("parent");
363        let child_role = Role::new("child");
364
365        system.register_role(parent_role).unwrap();
366        system.register_role(child_role).unwrap();
367        system.add_role_inheritance("child", "parent").unwrap();
368
369        let query = system.query();
370
371        let parents = query.parent_roles("child");
372        assert_eq!(parents.len(), 1);
373        assert!(parents.contains(&"parent".to_string()));
374
375        let children = query.child_roles("parent");
376        assert_eq!(children.len(), 1);
377        assert!(children.contains(&"child".to_string()));
378    }
379}