1use crate::db::Db;
2use crate::error::AuthError;
3use crate::types::{Role, RoleId, RoleName, 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("role name already exists".into());
12 }
13 }
14 AuthError::Database(err)
15}
16
17impl Db {
18 pub async fn create_role(
20 &self,
21 name: &RoleName,
22 description: Option<&str>,
23 ) -> Result<Role, AuthError> {
24 let id = RoleId::new();
25 sqlx::query_as::<_, Role>(
26 "INSERT INTO allowthem_roles (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_role(&self, id: &RoleId) -> Result<Option<Role>, AuthError> {
40 sqlx::query_as::<_, Role>(
41 "SELECT id, name, description, created_at FROM allowthem_roles 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_role_by_name(&self, name: &RoleName) -> Result<Option<Role>, AuthError> {
51 sqlx::query_as::<_, Role>(
52 "SELECT id, name, description, created_at FROM allowthem_roles WHERE name = ?",
53 )
54 .bind(name)
55 .fetch_optional(self.pool())
56 .await
57 .map_err(AuthError::Database)
58 }
59
60 pub async fn list_roles(&self) -> Result<Vec<Role>, AuthError> {
62 sqlx::query_as::<_, Role>(
63 "SELECT id, name, description, created_at FROM allowthem_roles ORDER BY created_at",
64 )
65 .fetch_all(self.pool())
66 .await
67 .map_err(AuthError::Database)
68 }
69
70 pub async fn delete_role(&self, id: &RoleId) -> Result<bool, AuthError> {
74 let result = sqlx::query("DELETE FROM allowthem_roles WHERE id = ?")
75 .bind(*id)
76 .execute(self.pool())
77 .await
78 .map_err(AuthError::Database)?;
79 Ok(result.rows_affected() > 0)
80 }
81
82 pub async fn assign_role(&self, user_id: &UserId, role_id: &RoleId) -> Result<(), AuthError> {
84 sqlx::query("INSERT OR IGNORE INTO allowthem_user_roles (user_id, role_id) VALUES (?, ?)")
85 .bind(*user_id)
86 .bind(*role_id)
87 .execute(self.pool())
88 .await
89 .map_err(AuthError::Database)?;
90 Ok(())
91 }
92
93 pub async fn unassign_role(
96 &self,
97 user_id: &UserId,
98 role_id: &RoleId,
99 ) -> Result<bool, AuthError> {
100 let result =
101 sqlx::query("DELETE FROM allowthem_user_roles WHERE user_id = ? AND role_id = ?")
102 .bind(*user_id)
103 .bind(*role_id)
104 .execute(self.pool())
105 .await
106 .map_err(AuthError::Database)?;
107 Ok(result.rows_affected() > 0)
108 }
109
110 pub async fn has_role(
112 &self,
113 user_id: &UserId,
114 role_name: &RoleName,
115 ) -> Result<bool, AuthError> {
116 let count: i64 = sqlx::query_scalar(
117 "SELECT COUNT(*) \
118 FROM allowthem_user_roles ur \
119 JOIN allowthem_roles r ON r.id = ur.role_id \
120 WHERE ur.user_id = ? AND r.name = ?",
121 )
122 .bind(*user_id)
123 .bind(role_name)
124 .fetch_one(self.pool())
125 .await
126 .map_err(AuthError::Database)?;
127 Ok(count > 0)
128 }
129
130 pub async fn get_user_roles(&self, user_id: &UserId) -> Result<Vec<Role>, AuthError> {
132 sqlx::query_as::<_, Role>(
133 "SELECT r.id, r.name, r.description, r.created_at \
134 FROM allowthem_roles r \
135 JOIN allowthem_user_roles ur ON ur.role_id = r.id \
136 WHERE ur.user_id = ? \
137 ORDER BY r.created_at",
138 )
139 .bind(*user_id)
140 .fetch_all(self.pool())
141 .await
142 .map_err(AuthError::Database)
143 }
144}