oxidite_auth/
authorization.rs1use oxidite_core::{OxiditeRequest, Result as OxiditeResult, Error};
2use oxidite_db::Database;
3use std::sync::Arc;
4use crate::rbac::{Role, Permission};
5
6pub 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 let user_id = req.extensions()
23 .get::<i64>()
24 .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
25
26 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
41pub 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 let user_id = req.extensions()
58 .get::<i64>()
59 .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
60
61 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
77pub 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 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 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 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 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 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 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}