1use oxidite_db::sqlx;
2use oxidite_db::sqlx::FromRow;
3
4#[derive(FromRow, Clone, Debug)]
5pub struct Role {
6 pub id: i64,
7 pub name: String,
8 pub description: Option<String>,
9 pub created_at: i64,
10 pub updated_at: i64,
11}
12
13#[derive(FromRow, Clone, Debug)]
14pub struct Permission {
15 pub id: i64,
16 pub name: String,
17 pub resource: String,
18 pub action: String,
19 pub description: Option<String>,
20 pub created_at: i64,
21 pub updated_at: i64,
22}
23
24impl Role {
25 pub async fn permissions(&self, db: &impl oxidite_db::Database) -> oxidite_db::Result<Vec<Permission>> {
27 let query = oxidite_db::sqlx::query(
28 "SELECT p.* FROM permissions p
29 INNER JOIN role_permissions rp ON p.id = rp.permission_id
30 WHERE rp.role_id = ?"
31 )
32 .bind(self.id);
33
34 let rows = db.fetch_all(query).await?;
35 let mut permissions = Vec::new();
36
37 for row in rows {
38 permissions.push(sqlx::FromRow::from_row(&row)?);
39 }
40
41 Ok(permissions)
42 }
43
44 pub async fn has_permission(&self, db: &impl oxidite_db::Database, permission_name: &str) -> oxidite_db::Result<bool> {
46 let query = oxidite_db::sqlx::query(
47 "SELECT 1 FROM permissions p
48 INNER JOIN role_permissions rp ON p.id = rp.permission_id
49 WHERE rp.role_id = ? AND p.name = ?
50 LIMIT 1"
51 )
52 .bind(self.id)
53 .bind(permission_name);
54 Ok(db.fetch_one(query).await?.is_some())
55 }
56}
57
58impl Permission {
59 pub fn matches(&self, resource: &str, action: &str) -> bool {
61 self.resource == resource && self.action == action
62 }
63}