1use crate::db::Db;
2use crate::error::AuthError;
3use crate::event_sink::AuthEvent;
4use crate::handle::AllowThem;
5use crate::types::{Permission, PermissionId, PermissionName, RoleId, UserId};
6
7fn map_unique_violation(err: sqlx::Error) -> AuthError {
10 if let sqlx::Error::Database(ref db_err) = err {
11 let msg = db_err.message();
12 if msg.contains("UNIQUE constraint failed") && msg.contains("name") {
13 return AuthError::Conflict("permission name already exists".into());
14 }
15 }
16 AuthError::Database(err)
17}
18
19impl Db {
20 pub async fn create_permission(
22 &self,
23 name: &PermissionName,
24 description: Option<&str>,
25 ) -> Result<Permission, AuthError> {
26 let id = PermissionId::new();
27 sqlx::query_as::<_, Permission>(
28 "INSERT INTO allowthem_permissions (id, name, description) \
29 VALUES (?, ?, ?) \
30 RETURNING id, name, description, created_at",
31 )
32 .bind(id)
33 .bind(name)
34 .bind(description)
35 .fetch_one(self.pool())
36 .await
37 .map_err(map_unique_violation)
38 }
39
40 pub async fn get_permission(&self, id: &PermissionId) -> Result<Option<Permission>, AuthError> {
42 sqlx::query_as::<_, Permission>(
43 "SELECT id, name, description, created_at FROM allowthem_permissions WHERE id = ?",
44 )
45 .bind(*id)
46 .fetch_optional(self.pool())
47 .await
48 .map_err(AuthError::Database)
49 }
50
51 pub async fn get_permission_by_name(
53 &self,
54 name: &PermissionName,
55 ) -> Result<Option<Permission>, AuthError> {
56 sqlx::query_as::<_, Permission>(
57 "SELECT id, name, description, created_at FROM allowthem_permissions WHERE name = ?",
58 )
59 .bind(name)
60 .fetch_optional(self.pool())
61 .await
62 .map_err(AuthError::Database)
63 }
64
65 pub async fn list_permissions(&self) -> Result<Vec<Permission>, AuthError> {
67 sqlx::query_as::<_, Permission>(
68 "SELECT id, name, description, created_at \
69 FROM allowthem_permissions \
70 ORDER BY created_at",
71 )
72 .fetch_all(self.pool())
73 .await
74 .map_err(AuthError::Database)
75 }
76
77 pub async fn delete_permission(&self, id: &PermissionId) -> Result<bool, AuthError> {
81 let result = sqlx::query("DELETE FROM allowthem_permissions WHERE id = ?")
82 .bind(*id)
83 .execute(self.pool())
84 .await
85 .map_err(AuthError::Database)?;
86 Ok(result.rows_affected() > 0)
87 }
88
89 pub async fn assign_permission_to_role(
91 &self,
92 role_id: &RoleId,
93 permission_id: &PermissionId,
94 ) -> Result<(), AuthError> {
95 sqlx::query(
96 "INSERT OR IGNORE INTO allowthem_role_permissions (role_id, permission_id) \
97 VALUES (?, ?)",
98 )
99 .bind(*role_id)
100 .bind(*permission_id)
101 .execute(self.pool())
102 .await
103 .map_err(AuthError::Database)?;
104 Ok(())
105 }
106
107 pub async fn assign_permission_to_user(
109 &self,
110 user_id: &UserId,
111 permission_id: &PermissionId,
112 ) -> Result<(), AuthError> {
113 sqlx::query(
114 "INSERT OR IGNORE INTO allowthem_user_permissions (user_id, permission_id) \
115 VALUES (?, ?)",
116 )
117 .bind(*user_id)
118 .bind(*permission_id)
119 .execute(self.pool())
120 .await
121 .map_err(AuthError::Database)?;
122 Ok(())
123 }
124
125 pub async fn unassign_permission_from_role(
127 &self,
128 role_id: &RoleId,
129 permission_id: &PermissionId,
130 ) -> Result<bool, AuthError> {
131 let result = sqlx::query(
132 "DELETE FROM allowthem_role_permissions WHERE role_id = ? AND permission_id = ?",
133 )
134 .bind(*role_id)
135 .bind(*permission_id)
136 .execute(self.pool())
137 .await
138 .map_err(AuthError::Database)?;
139 Ok(result.rows_affected() > 0)
140 }
141
142 pub async fn unassign_permission_from_user(
144 &self,
145 user_id: &UserId,
146 permission_id: &PermissionId,
147 ) -> Result<bool, AuthError> {
148 let result = sqlx::query(
149 "DELETE FROM allowthem_user_permissions WHERE user_id = ? AND permission_id = ?",
150 )
151 .bind(*user_id)
152 .bind(*permission_id)
153 .execute(self.pool())
154 .await
155 .map_err(AuthError::Database)?;
156 Ok(result.rows_affected() > 0)
157 }
158
159 pub async fn has_permission(
162 &self,
163 user_id: &UserId,
164 perm_name: &PermissionName,
165 ) -> Result<bool, AuthError> {
166 let exists: bool = sqlx::query_scalar(
167 "SELECT EXISTS(
168 SELECT 1
169 FROM allowthem_user_permissions up
170 JOIN allowthem_permissions p ON p.id = up.permission_id
171 WHERE up.user_id = ? AND p.name = ?
172 UNION ALL
173 SELECT 1
174 FROM allowthem_role_permissions rp
175 JOIN allowthem_user_roles ur ON ur.role_id = rp.role_id
176 JOIN allowthem_permissions p ON p.id = rp.permission_id
177 WHERE ur.user_id = ? AND p.name = ?
178 )",
179 )
180 .bind(*user_id)
181 .bind(perm_name)
182 .bind(*user_id)
183 .bind(perm_name)
184 .fetch_one(self.pool())
185 .await
186 .map_err(AuthError::Database)?;
187 Ok(exists)
188 }
189
190 pub async fn get_user_permissions(
193 &self,
194 user_id: &UserId,
195 ) -> Result<Vec<Permission>, AuthError> {
196 sqlx::query_as::<_, Permission>(
197 "SELECT DISTINCT p.id, p.name, p.description, p.created_at
198 FROM allowthem_permissions p
199 WHERE p.id IN (
200 SELECT permission_id FROM allowthem_user_permissions WHERE user_id = ?
201 UNION
202 SELECT rp.permission_id
203 FROM allowthem_role_permissions rp
204 JOIN allowthem_user_roles ur ON ur.role_id = rp.role_id
205 WHERE ur.user_id = ?
206 )
207 ORDER BY p.name",
208 )
209 .bind(*user_id)
210 .bind(*user_id)
211 .fetch_all(self.pool())
212 .await
213 .map_err(AuthError::Database)
214 }
215
216 pub async fn list_role_permissions(
218 &self,
219 role_id: &RoleId,
220 ) -> Result<Vec<Permission>, AuthError> {
221 sqlx::query_as::<_, Permission>(
222 "SELECT p.id, p.name, p.description, p.created_at \
223 FROM allowthem_permissions p \
224 JOIN allowthem_role_permissions rp ON rp.permission_id = p.id \
225 WHERE rp.role_id = ? \
226 ORDER BY p.name",
227 )
228 .bind(*role_id)
229 .fetch_all(self.pool())
230 .await
231 .map_err(AuthError::Database)
232 }
233}
234
235impl AllowThem {
236 pub async fn assign_permission_to_user(
237 &self,
238 user_id: &UserId,
239 permission_id: &PermissionId,
240 ) -> Result<(), AuthError> {
241 self.db()
242 .assign_permission_to_user(user_id, permission_id)
243 .await?;
244 self.emit_event(AuthEvent::new(
245 "permission.assigned",
246 Some(*user_id),
247 serde_json::json!({ "user_id": user_id, "permission_id": permission_id }),
248 ))
249 .await;
250 Ok(())
251 }
252
253 pub async fn unassign_permission_from_user(
254 &self,
255 user_id: &UserId,
256 permission_id: &PermissionId,
257 ) -> Result<(), AuthError> {
258 self.db()
259 .unassign_permission_from_user(user_id, permission_id)
260 .await?;
261 self.emit_event(AuthEvent::new(
262 "permission.revoked",
263 Some(*user_id),
264 serde_json::json!({ "user_id": user_id, "permission_id": permission_id }),
265 ))
266 .await;
267 Ok(())
268 }
269}