allowthem_core/
permissions.rs1use crate::db::Db;
2use crate::error::AuthError;
3use crate::types::{Permission, PermissionId, PermissionName, RoleId, UserId};
4
5fn map_unique_violation(err: sqlx::Error) -> AuthError {
8 if let sqlx::Error::Database(ref db_err) = err {
9 let msg = db_err.message();
10 if msg.contains("UNIQUE constraint failed") && msg.contains("name") {
11 return AuthError::Conflict("permission name already exists".into());
12 }
13 }
14 AuthError::Database(err)
15}
16
17impl Db {
18 pub async fn create_permission(
20 &self,
21 name: &PermissionName,
22 description: Option<&str>,
23 ) -> Result<Permission, AuthError> {
24 let id = PermissionId::new();
25 sqlx::query_as::<_, Permission>(
26 "INSERT INTO allowthem_permissions (id, name, description) \
27 VALUES (?, ?, ?) \
28 RETURNING id, name, description, created_at",
29 )
30 .bind(id)
31 .bind(name)
32 .bind(description)
33 .fetch_one(self.pool())
34 .await
35 .map_err(map_unique_violation)
36 }
37
38 pub async fn get_permission(&self, id: &PermissionId) -> Result<Option<Permission>, AuthError> {
40 sqlx::query_as::<_, Permission>(
41 "SELECT id, name, description, created_at FROM allowthem_permissions WHERE id = ?",
42 )
43 .bind(*id)
44 .fetch_optional(self.pool())
45 .await
46 .map_err(AuthError::Database)
47 }
48
49 pub async fn get_permission_by_name(
51 &self,
52 name: &PermissionName,
53 ) -> Result<Option<Permission>, AuthError> {
54 sqlx::query_as::<_, Permission>(
55 "SELECT id, name, description, created_at FROM allowthem_permissions WHERE name = ?",
56 )
57 .bind(name)
58 .fetch_optional(self.pool())
59 .await
60 .map_err(AuthError::Database)
61 }
62
63 pub async fn list_permissions(&self) -> Result<Vec<Permission>, AuthError> {
65 sqlx::query_as::<_, Permission>(
66 "SELECT id, name, description, created_at \
67 FROM allowthem_permissions \
68 ORDER BY created_at",
69 )
70 .fetch_all(self.pool())
71 .await
72 .map_err(AuthError::Database)
73 }
74
75 pub async fn delete_permission(&self, id: &PermissionId) -> Result<bool, AuthError> {
79 let result = sqlx::query("DELETE FROM allowthem_permissions WHERE id = ?")
80 .bind(*id)
81 .execute(self.pool())
82 .await
83 .map_err(AuthError::Database)?;
84 Ok(result.rows_affected() > 0)
85 }
86
87 pub async fn assign_permission_to_role(
89 &self,
90 role_id: &RoleId,
91 permission_id: &PermissionId,
92 ) -> Result<(), AuthError> {
93 sqlx::query(
94 "INSERT OR IGNORE INTO allowthem_role_permissions (role_id, permission_id) \
95 VALUES (?, ?)",
96 )
97 .bind(*role_id)
98 .bind(*permission_id)
99 .execute(self.pool())
100 .await
101 .map_err(AuthError::Database)?;
102 Ok(())
103 }
104
105 pub async fn assign_permission_to_user(
107 &self,
108 user_id: &UserId,
109 permission_id: &PermissionId,
110 ) -> Result<(), AuthError> {
111 sqlx::query(
112 "INSERT OR IGNORE INTO allowthem_user_permissions (user_id, permission_id) \
113 VALUES (?, ?)",
114 )
115 .bind(*user_id)
116 .bind(*permission_id)
117 .execute(self.pool())
118 .await
119 .map_err(AuthError::Database)?;
120 Ok(())
121 }
122
123 pub async fn unassign_permission_from_role(
125 &self,
126 role_id: &RoleId,
127 permission_id: &PermissionId,
128 ) -> Result<bool, AuthError> {
129 let result = sqlx::query(
130 "DELETE FROM allowthem_role_permissions WHERE role_id = ? AND permission_id = ?",
131 )
132 .bind(*role_id)
133 .bind(*permission_id)
134 .execute(self.pool())
135 .await
136 .map_err(AuthError::Database)?;
137 Ok(result.rows_affected() > 0)
138 }
139
140 pub async fn unassign_permission_from_user(
142 &self,
143 user_id: &UserId,
144 permission_id: &PermissionId,
145 ) -> Result<bool, AuthError> {
146 let result = sqlx::query(
147 "DELETE FROM allowthem_user_permissions WHERE user_id = ? AND permission_id = ?",
148 )
149 .bind(*user_id)
150 .bind(*permission_id)
151 .execute(self.pool())
152 .await
153 .map_err(AuthError::Database)?;
154 Ok(result.rows_affected() > 0)
155 }
156
157 pub async fn has_permission(
160 &self,
161 user_id: &UserId,
162 perm_name: &PermissionName,
163 ) -> Result<bool, AuthError> {
164 let exists: bool = sqlx::query_scalar(
165 "SELECT EXISTS(
166 SELECT 1
167 FROM allowthem_user_permissions up
168 JOIN allowthem_permissions p ON p.id = up.permission_id
169 WHERE up.user_id = ? AND p.name = ?
170 UNION ALL
171 SELECT 1
172 FROM allowthem_role_permissions rp
173 JOIN allowthem_user_roles ur ON ur.role_id = rp.role_id
174 JOIN allowthem_permissions p ON p.id = rp.permission_id
175 WHERE ur.user_id = ? AND p.name = ?
176 )",
177 )
178 .bind(*user_id)
179 .bind(perm_name)
180 .bind(*user_id)
181 .bind(perm_name)
182 .fetch_one(self.pool())
183 .await
184 .map_err(AuthError::Database)?;
185 Ok(exists)
186 }
187
188 pub async fn get_user_permissions(
191 &self,
192 user_id: &UserId,
193 ) -> Result<Vec<Permission>, AuthError> {
194 sqlx::query_as::<_, Permission>(
195 "SELECT DISTINCT p.id, p.name, p.description, p.created_at
196 FROM allowthem_permissions p
197 WHERE p.id IN (
198 SELECT permission_id FROM allowthem_user_permissions WHERE user_id = ?
199 UNION
200 SELECT rp.permission_id
201 FROM allowthem_role_permissions rp
202 JOIN allowthem_user_roles ur ON ur.role_id = rp.role_id
203 WHERE ur.user_id = ?
204 )
205 ORDER BY p.name",
206 )
207 .bind(*user_id)
208 .bind(*user_id)
209 .fetch_all(self.pool())
210 .await
211 .map_err(AuthError::Database)
212 }
213}