1use super::{
2 AccessType, RbacError, RbacUserId,
3 entity::{
4 permission::{self, ActiveModel as Permission, PermissionId},
5 resource::{self, ActiveModel as Resource, ResourceId},
6 role::{self, ActiveModel as Role, RoleId},
7 role_hierarchy::{self, ActiveModel as RoleHierarchy},
8 role_permission::{self, ActiveModel as RolePermission},
9 user_override::{self, ActiveModel as UserOverride},
10 user_role::{self, ActiveModel as UserRole},
11 },
12};
13use crate::{
14 AccessMode, EntityTrait, IsolationLevel, Set, TransactionSession, TransactionTrait,
15 error::DbErr, sea_query::OnConflict,
16};
17use std::collections::HashMap;
18
19#[derive(Debug)]
21pub struct RbacContext {
22 tables: HashMap<String, ResourceId>,
23 permissions: HashMap<String, PermissionId>,
24 roles: HashMap<String, RoleId>,
25}
26
27#[derive(Debug)]
28pub struct RbacAddRoleHierarchy {
29 pub super_role: &'static str,
30 pub role: &'static str,
31}
32
33#[derive(Debug)]
34pub struct RbacAddUserOverride {
35 pub user_id: i64,
36 pub table: &'static str,
37 pub action: &'static str,
38 pub grant: bool,
39}
40
41impl RbacContext {
42 pub fn load<C: TransactionTrait>(db: &C) -> Result<Self, DbErr> {
44 let txn = &db.begin_with_config(
46 Some(IsolationLevel::ReadCommitted),
47 Some(AccessMode::ReadOnly),
48 )?;
49
50 let tables = resource::Entity::find()
51 .all(txn)?
52 .into_iter()
53 .map(|t| (t.table, t.id))
54 .collect();
55
56 let permissions = permission::Entity::find()
57 .all(txn)?
58 .into_iter()
59 .map(|p| (p.action, p.id))
60 .collect();
61
62 let roles = role::Entity::find()
63 .all(txn)?
64 .into_iter()
65 .map(|r| (r.role, r.id))
66 .collect();
67
68 Ok(Self {
69 tables,
70 permissions,
71 roles,
72 })
73 }
74
75 pub fn add_tables<C: TransactionTrait>(
77 &mut self,
78 db: &C,
79 tables: &[&'static str],
80 ) -> Result<(), DbErr> {
81 let txn = db.begin()?;
82
83 for table_name in tables {
84 if let Some(table_id) = resource::Entity::insert(Resource {
85 table: Set(table_name.to_string()),
86 ..Default::default()
87 })
88 .on_conflict_do_nothing()
89 .exec(&txn)?
90 .last_insert_id()?
91 {
92 self.tables.insert(table_name.to_string(), table_id);
93 }
94 }
95
96 txn.commit()
97 }
98
99 pub fn add_crud_permissions<C: TransactionTrait>(&mut self, db: &C) -> Result<(), DbErr> {
101 let txn = db.begin()?;
102
103 for action in [
104 AccessType::Select,
105 AccessType::Insert,
106 AccessType::Update,
107 AccessType::Delete,
108 ] {
109 if let Some(permission_id) = permission::Entity::insert(Permission {
110 action: Set(action.as_str().to_owned()),
111 ..Default::default()
112 })
113 .on_conflict_do_nothing()
114 .exec(&txn)?
115 .last_insert_id()?
116 {
117 self.permissions
118 .insert(action.as_str().to_owned(), permission_id);
119 }
120 }
121
122 txn.commit()
123 }
124
125 pub fn add_roles<C: TransactionTrait>(
127 &mut self,
128 db: &C,
129 roles: &[&'static str],
130 ) -> Result<(), DbErr> {
131 let txn = db.begin()?;
132
133 for role in roles {
134 if let Some(role_id) = role::Entity::insert(Role {
135 role: Set(role.to_string()),
136 ..Default::default()
137 })
138 .on_conflict_do_nothing()
139 .exec(&txn)?
140 .last_insert_id()?
141 {
142 self.roles.insert(role.to_string(), role_id);
143 }
144 }
145
146 txn.commit()
147 }
148
149 pub fn get_role(&self, role: &'static str) -> Result<&RoleId, DbErr> {
150 self.roles
151 .get(role)
152 .ok_or_else(|| DbErr::RbacError(RbacError::RoleNotFound(role.to_string()).to_string()))
153 }
154
155 pub fn add_role_permissions<C: TransactionTrait>(
157 &mut self,
158 db: &C,
159 role: &'static str,
160 actions: &[&'static str],
161 tables: &[&'static str],
162 ) -> Result<(), DbErr> {
163 self.update_role_permissions(db, role, actions, tables, true)
164 }
165
166 pub fn remove_role_permissions<C: TransactionTrait>(
168 &mut self,
169 db: &C,
170 role: &'static str,
171 actions: &[&'static str],
172 tables: &[&'static str],
173 ) -> Result<(), DbErr> {
174 self.update_role_permissions(db, role, actions, tables, false)
175 }
176
177 fn update_role_permissions<C: TransactionTrait>(
178 &mut self,
179 db: &C,
180 role: &'static str,
181 actions: &[&'static str],
182 tables: &[&'static str],
183 grant: bool,
184 ) -> Result<(), DbErr> {
185 let txn = db.begin()?;
186
187 for table_name in tables {
188 for action in actions {
189 let model = RolePermission {
190 role_id: Set(*self.roles.get(role).ok_or_else(|| {
191 DbErr::RbacError(RbacError::RoleNotFound(role.to_string()).to_string())
192 })?),
193 permission_id: Set(*self.permissions.get(*action).ok_or_else(|| {
194 DbErr::RbacError(
195 RbacError::PermissionNotFound(action.to_string()).to_string(),
196 )
197 })?),
198 resource_id: Set(*self.tables.get(*table_name).ok_or_else(|| {
199 DbErr::RbacError(
200 RbacError::ResourceNotFound(table_name.to_string()).to_string(),
201 )
202 })?),
203 };
204 if grant {
205 role_permission::Entity::insert(model)
206 .on_conflict_do_nothing()
207 .exec(&txn)?;
208 } else {
209 role_permission::Entity::delete(model).exec(&txn)?;
210 }
211 }
212 }
213
214 txn.commit()
215 }
216
217 pub fn add_user_override<C: TransactionTrait>(
218 &mut self,
219 db: &C,
220 rows: &[RbacAddUserOverride],
221 ) -> Result<(), DbErr> {
222 let txn = db.begin()?;
223
224 for RbacAddUserOverride {
225 user_id,
226 table,
227 action,
228 grant,
229 } in rows
230 {
231 user_override::Entity::insert(UserOverride {
232 user_id: Set(RbacUserId(*user_id)),
233 permission_id: Set(*self.permissions.get(*action).ok_or_else(|| {
234 DbErr::RbacError(RbacError::PermissionNotFound(action.to_string()).to_string())
235 })?),
236 resource_id: Set(*self.tables.get(*table).ok_or_else(|| {
237 DbErr::RbacError(RbacError::ResourceNotFound(table.to_string()).to_string())
238 })?),
239 grant: Set(*grant),
240 })
241 .on_conflict(
242 OnConflict::columns([
243 user_override::Column::UserId,
244 user_override::Column::PermissionId,
245 user_override::Column::ResourceId,
246 ])
247 .update_column(user_override::Column::Grant)
248 .to_owned(),
249 )
250 .do_nothing()
251 .exec(&txn)?;
252 }
253
254 txn.commit()
255 }
256
257 pub fn add_role_hierarchy<C: TransactionTrait>(
258 &mut self,
259 db: &C,
260 rows: &[RbacAddRoleHierarchy],
261 ) -> Result<(), DbErr> {
262 let txn = db.begin()?;
263
264 for RbacAddRoleHierarchy { super_role, role } in rows {
265 role_hierarchy::Entity::insert(RoleHierarchy {
266 super_role_id: Set(*self.roles.get(*super_role).ok_or_else(|| {
267 DbErr::RbacError(RbacError::RoleNotFound(super_role.to_string()).to_string())
268 })?),
269 role_id: Set(*self.roles.get(*role).ok_or_else(|| {
270 DbErr::RbacError(RbacError::RoleNotFound(role.to_string()).to_string())
271 })?),
272 })
273 .on_conflict_do_nothing()
274 .exec(&txn)?;
275 }
276
277 txn.commit()
278 }
279
280 pub fn assign_user_role<C: TransactionTrait>(
284 &mut self,
285 db: &C,
286 rows: &[(i64, &'static str)],
287 ) -> Result<(), DbErr> {
288 let txn = db.begin()?;
289
290 for (user_id, role) in rows {
291 user_role::Entity::insert(UserRole {
292 user_id: Set(RbacUserId(*user_id)),
293 role_id: Set(*self.roles.get(*role).ok_or_else(|| {
294 DbErr::RbacError(RbacError::RoleNotFound(role.to_string()).to_string())
295 })?),
296 })
297 .on_conflict(
298 OnConflict::column(user_role::Column::UserId)
299 .update_column(user_role::Column::RoleId)
300 .to_owned(),
301 )
302 .do_nothing()
303 .exec(&txn)?;
304 }
305
306 txn.commit()
307 }
308}