oxidite_auth/
authorization.rs

1use oxidite_core::{OxiditeRequest, Result as OxiditeResult, Error};
2use oxidite_db::Database;
3use std::sync::Arc;
4use crate::rbac::{Role, Permission};
5
6/// Middleware to require a specific role
7pub struct RequireRole {
8    role_name: String,
9    db: Arc<dyn Database>,
10}
11
12impl RequireRole {
13    pub fn new(role_name: impl Into<String>, db: Arc<dyn Database>) -> Self {
14        Self {
15            role_name: role_name.into(),
16            db,
17        }
18    }
19    
20    pub async fn check(&self, req: &OxiditeRequest) -> OxiditeResult<bool> {
21        // Get user_id from request extensions (set by auth middleware)
22        let user_id = req.extensions()
23            .get::<i64>()
24            .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
25        
26        // Check if user has the required role
27        let query = format!(
28            "SELECT r.* FROM roles r 
29             INNER JOIN user_roles ur ON r.id = ur.role_id 
30             WHERE ur.user_id = {} AND r.name = '{}'",
31            user_id, self.role_name
32        );
33        
34        let rows = self.db.query(&query).await
35            .map_err(|_| Error::InternalServerError("Database error".to_string()))?;
36        
37        Ok(!rows.is_empty())
38    }
39}
40
41/// Middleware to require a specific permission
42pub struct RequirePermission {
43    permission_name: String,
44    db: Arc<dyn Database>,
45}
46
47impl RequirePermission {
48    pub fn new(permission_name: impl Into<String>, db: Arc<dyn Database>) -> Self {
49        Self {
50            permission_name: permission_name.into(),
51            db,
52        }
53    }
54    
55    pub async fn check(&self, req: &OxiditeRequest) -> OxiditeResult<bool> {
56        // Get user_id from request extensions
57        let user_id = req.extensions()
58            .get::<i64>()
59            .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
60        
61        // Check if user has the required permission through any of their roles
62        let query = format!(
63            "SELECT p.* FROM permissions p 
64             INNER JOIN role_permissions rp ON p.id = rp.permission_id 
65             INNER JOIN user_roles ur ON rp.role_id = ur.role_id 
66             WHERE ur.user_id = {} AND p.name = '{}'",
67            user_id, self.permission_name
68        );
69        
70        let rows = self.db.query(&query).await
71            .map_err(|_| Error::InternalServerError("Database error".to_string()))?;
72        
73        Ok(!rows.is_empty())
74    }
75}
76
77/// Utility functions for authorization checks
78pub struct AuthorizationService {
79    db: Arc<dyn Database>,
80}
81
82impl AuthorizationService {
83    pub fn new(db: Arc<dyn Database>) -> Self {
84        Self { db }
85    }
86    
87    /// Check if user has a specific role
88    pub async fn user_has_role(&self, user_id: i64, role_name: &str) -> oxidite_db::Result<bool> {
89        let query = format!(
90            "SELECT COUNT(*) as count FROM user_roles ur 
91             INNER JOIN roles r ON ur.role_id = r.id 
92             WHERE ur.user_id = {} AND r.name = '{}'",
93            user_id, role_name
94        );
95        
96        let rows = self.db.query(&query).await?;
97        Ok(!rows.is_empty())
98    }
99    
100    /// Check if user has a specific permission
101    pub async fn user_can(&self, user_id: i64, permission_name: &str) -> oxidite_db::Result<bool> {
102        let query = format!(
103            "SELECT COUNT(*) as count FROM permissions p 
104             INNER JOIN role_permissions rp ON p.id = rp.permission_id 
105             INNER JOIN user_roles ur ON rp.role_id = ur.role_id 
106             WHERE ur.user_id = {} AND p.name = '{}'",
107            user_id, permission_name
108        );
109        
110        let rows = self.db.query(&query).await?;
111        Ok(!rows.is_empty())
112    }
113    
114    /// Get all roles for a user
115    pub async fn user_roles(&self, user_id: i64) -> oxidite_db::Result<Vec<Role>> {
116        use oxidite_db::sqlx::FromRow;
117        
118        let query = format!(
119            "SELECT r.* FROM roles r 
120             INNER JOIN user_roles ur ON r.id = ur.role_id 
121             WHERE ur.user_id = {}",
122            user_id
123        );
124        
125        let rows = self.db.query(&query).await?;
126        let mut roles = Vec::new();
127        
128        for row in rows {
129            roles.push(Role::from_row(&row)?);
130        }
131        
132        Ok(roles)
133    }
134    
135    /// Get all permissions for a user (through their roles)
136    pub async fn user_permissions(&self, user_id: i64) -> oxidite_db::Result<Vec<Permission>> {
137        use oxidite_db::sqlx::FromRow;
138        
139        let query = format!(
140            "SELECT DISTINCT p.* FROM permissions p 
141             INNER JOIN role_permissions rp ON p.id = rp.permission_id 
142             INNER JOIN user_roles ur ON rp.role_id = ur.role_id 
143             WHERE ur.user_id = {}",
144            user_id
145        );
146        
147        let rows = self.db.query(&query).await?;
148        let mut permissions = Vec::new();
149        
150        for row in rows {
151            permissions.push(Permission::from_row(&row)?);
152        }
153        
154        Ok(permissions)
155    }
156    
157    /// Assign role to user
158    pub async fn assign_role(&self, user_id: i64, role_id: i64) -> oxidite_db::Result<()> {
159        let query = format!(
160            "INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES ({}, {})",
161            user_id, role_id
162        );
163        self.db.execute(&query).await?;
164        Ok(())
165    }
166    
167    /// Remove role from user
168    pub async fn remove_role(&self, user_id: i64, role_id: i64) -> oxidite_db::Result<()> {
169        let query = format!(
170            "DELETE FROM user_roles WHERE user_id = {} AND role_id = {}",
171            user_id, role_id
172        );
173        self.db.execute(&query).await?;
174        Ok(())
175    }
176}