oxidite_auth/
authorization.rs1use oxidite_core::{OxiditeRequest, Result as OxiditeResult, Error};
2use oxidite_db::Database;
3use oxidite_db::sqlx::Row;
4use std::sync::Arc;
5use crate::rbac::{Role, Permission};
6
7pub struct RequireRole {
9 role_name: String,
10 db: Arc<dyn Database>,
11}
12
13impl RequireRole {
14 pub fn new(role_name: impl Into<String>, db: Arc<dyn Database>) -> Self {
15 Self {
16 role_name: role_name.into(),
17 db,
18 }
19 }
20
21 pub async fn check(&self, req: &OxiditeRequest) -> OxiditeResult<bool> {
22 let user_id = req.extensions()
24 .get::<i64>()
25 .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
26
27 let query = oxidite_db::sqlx::query(
29 "SELECT 1 FROM roles r
30 INNER JOIN user_roles ur ON r.id = ur.role_id
31 WHERE ur.user_id = ? AND r.name = ?
32 LIMIT 1"
33 )
34 .bind(*user_id)
35 .bind(&self.role_name);
36
37 let row = self.db.fetch_one(query).await
38 .map_err(|_| Error::InternalServerError("Database error".to_string()))?;
39
40 Ok(row.is_some())
41 }
42}
43
44pub struct RequirePermission {
46 permission_name: String,
47 db: Arc<dyn Database>,
48}
49
50impl RequirePermission {
51 pub fn new(permission_name: impl Into<String>, db: Arc<dyn Database>) -> Self {
52 Self {
53 permission_name: permission_name.into(),
54 db,
55 }
56 }
57
58 pub async fn check(&self, req: &OxiditeRequest) -> OxiditeResult<bool> {
59 let user_id = req.extensions()
61 .get::<i64>()
62 .ok_or_else(|| Error::Unauthorized("User not authenticated".to_string()))?;
63
64 let query = oxidite_db::sqlx::query(
66 "SELECT 1 FROM permissions p
67 INNER JOIN role_permissions rp ON p.id = rp.permission_id
68 INNER JOIN user_roles ur ON rp.role_id = ur.role_id
69 WHERE ur.user_id = ? AND p.name = ?
70 LIMIT 1"
71 )
72 .bind(*user_id)
73 .bind(&self.permission_name);
74
75 let row = self.db.fetch_one(query).await
76 .map_err(|_| Error::InternalServerError("Database error".to_string()))?;
77
78 Ok(row.is_some())
79 }
80}
81
82pub struct AuthorizationService {
84 db: Arc<dyn Database>,
85}
86
87impl AuthorizationService {
88 pub fn new(db: Arc<dyn Database>) -> Self {
89 Self { db }
90 }
91
92 pub async fn user_has_role(&self, user_id: i64, role_name: &str) -> oxidite_db::Result<bool> {
94 let query = oxidite_db::sqlx::query(
95 "SELECT COUNT(*) as count FROM user_roles ur
96 INNER JOIN roles r ON ur.role_id = r.id
97 WHERE ur.user_id = ? AND r.name = ?"
98 )
99 .bind(user_id)
100 .bind(role_name);
101
102 let row = self.db.fetch_one(query).await?;
103 Ok(row
104 .and_then(|r| r.try_get::<i64, _>("count").ok())
105 .unwrap_or(0) > 0)
106 }
107
108 pub async fn user_can(&self, user_id: i64, permission_name: &str) -> oxidite_db::Result<bool> {
110 let query = oxidite_db::sqlx::query(
111 "SELECT COUNT(*) as count FROM permissions p
112 INNER JOIN role_permissions rp ON p.id = rp.permission_id
113 INNER JOIN user_roles ur ON rp.role_id = ur.role_id
114 WHERE ur.user_id = ? AND p.name = ?"
115 )
116 .bind(user_id)
117 .bind(permission_name);
118
119 let row = self.db.fetch_one(query).await?;
120 Ok(row
121 .and_then(|r| r.try_get::<i64, _>("count").ok())
122 .unwrap_or(0) > 0)
123 }
124
125 pub async fn user_roles(&self, user_id: i64) -> oxidite_db::Result<Vec<Role>> {
127 use oxidite_db::sqlx::FromRow;
128
129 let query = oxidite_db::sqlx::query(
130 "SELECT r.* FROM roles r
131 INNER JOIN user_roles ur ON r.id = ur.role_id
132 WHERE ur.user_id = ?"
133 )
134 .bind(user_id);
135
136 let rows = self.db.fetch_all(query).await?;
137 let mut roles = Vec::new();
138
139 for row in rows {
140 roles.push(Role::from_row(&row)?);
141 }
142
143 Ok(roles)
144 }
145
146 pub async fn user_permissions(&self, user_id: i64) -> oxidite_db::Result<Vec<Permission>> {
148 use oxidite_db::sqlx::FromRow;
149
150 let query = oxidite_db::sqlx::query(
151 "SELECT DISTINCT p.* FROM permissions p
152 INNER JOIN role_permissions rp ON p.id = rp.permission_id
153 INNER JOIN user_roles ur ON rp.role_id = ur.role_id
154 WHERE ur.user_id = ?"
155 )
156 .bind(user_id);
157
158 let rows = self.db.fetch_all(query).await?;
159 let mut permissions = Vec::new();
160
161 for row in rows {
162 permissions.push(Permission::from_row(&row)?);
163 }
164
165 Ok(permissions)
166 }
167
168 pub async fn assign_role(&self, user_id: i64, role_id: i64) -> oxidite_db::Result<()> {
170 let exists_query = oxidite_db::sqlx::query(
172 "SELECT 1 FROM user_roles WHERE user_id = ? AND role_id = ? LIMIT 1"
173 )
174 .bind(user_id)
175 .bind(role_id);
176
177 if self.db.fetch_one(exists_query).await?.is_none() {
178 let insert_query = oxidite_db::sqlx::query(
179 "INSERT INTO user_roles (user_id, role_id) VALUES (?, ?)"
180 )
181 .bind(user_id)
182 .bind(role_id);
183 self.db.execute_query(insert_query).await?;
184 }
185 Ok(())
186 }
187
188 pub async fn remove_role(&self, user_id: i64, role_id: i64) -> oxidite_db::Result<()> {
190 let query = oxidite_db::sqlx::query(
191 "DELETE FROM user_roles WHERE user_id = ? AND role_id = ?"
192 )
193 .bind(user_id)
194 .bind(role_id);
195 self.db.execute_query(query).await?;
196 Ok(())
197 }
198}