Create a custom role. Returns error if it already exists or would create a cycle.
(
&self,
name: &str,
tenant_id: TenantId,
parent: Option<&str>,
catalog: Option<&SystemCatalog>,
)
| 160 | |
| 161 | /// Create a custom role. Returns error if it already exists or would create a cycle. |
| 162 | pub fn create_role( |
| 163 | &self, |
| 164 | name: &str, |
| 165 | tenant_id: TenantId, |
| 166 | parent: Option<&str>, |
| 167 | catalog: Option<&SystemCatalog>, |
| 168 | ) -> crate::Result<()> { |
| 169 | // Reject built-in role names. |
| 170 | if is_builtin(name) { |
| 171 | return Err(crate::Error::BadRequest { |
| 172 | detail: format!("'{name}' is a built-in role and cannot be created"), |
| 173 | }); |
| 174 | } |
| 175 | |
| 176 | let mut roles = self.roles.write().map_err(|e| crate::Error::Internal { |
| 177 | detail: format!("role store lock poisoned: {e}"), |
| 178 | })?; |
| 179 | |
| 180 | if roles.contains_key(name) { |
| 181 | return Err(crate::Error::BadRequest { |
| 182 | detail: format!("role '{name}' already exists"), |
| 183 | }); |
| 184 | } |
| 185 | |
| 186 | // Validate parent exists (built-in or custom) and enforce depth/cycle rules. |
| 187 | if let Some(parent_name) = parent { |
| 188 | validate_parent(name, parent_name, &roles)?; |
| 189 | } |
| 190 | |
| 191 | let now = std::time::SystemTime::now() |
| 192 | .duration_since(std::time::UNIX_EPOCH) |
| 193 | .unwrap_or_default() |
| 194 | .as_secs(); |
| 195 | |
| 196 | let role = CustomRole { |
| 197 | name: name.to_string(), |
| 198 | tenant_id, |
| 199 | parent: parent.map(|s| s.to_string()), |
| 200 | created_at: now, |
| 201 | }; |
| 202 | |
| 203 | if let Some(catalog) = catalog { |
| 204 | catalog.put_role(&StoredRole { |
| 205 | name: name.to_string(), |
| 206 | tenant_id: tenant_id.as_u64(), |
| 207 | parent: parent.unwrap_or("").to_string(), |
| 208 | created_at: now, |
| 209 | })?; |
| 210 | } |
| 211 | |
| 212 | roles.insert(name.to_string(), role); |
| 213 | Ok(()) |
| 214 | } |
| 215 | |
| 216 | /// Drop a custom role. |
| 217 | pub fn drop_role(&self, name: &str, catalog: Option<&SystemCatalog>) -> crate::Result<bool> { |