Build a `StoredRole` ready for replication via `CatalogEntry::PutRole`, without writing to redb or the in-memory cache. Performs the same validation `create_role` does (built-in name rejection, duplicate check, parent existence).
(
&self,
name: &str,
tenant_id: TenantId,
parent: Option<&str>,
)
| 125 | /// `create_role` does (built-in name rejection, duplicate |
| 126 | /// check, parent existence). |
| 127 | pub fn prepare_role( |
| 128 | &self, |
| 129 | name: &str, |
| 130 | tenant_id: TenantId, |
| 131 | parent: Option<&str>, |
| 132 | ) -> crate::Result<StoredRole> { |
| 133 | if is_builtin(name) { |
| 134 | return Err(crate::Error::BadRequest { |
| 135 | detail: format!("'{name}' is a built-in role and cannot be created"), |
| 136 | }); |
| 137 | } |
| 138 | let roles = self.roles.read().map_err(|e| crate::Error::Internal { |
| 139 | detail: format!("role store lock poisoned: {e}"), |
| 140 | })?; |
| 141 | if roles.contains_key(name) { |
| 142 | return Err(crate::Error::BadRequest { |
| 143 | detail: format!("role '{name}' already exists"), |
| 144 | }); |
| 145 | } |
| 146 | if let Some(parent_name) = parent { |
| 147 | validate_parent(name, parent_name, &roles)?; |
| 148 | } |
| 149 | let now = std::time::SystemTime::now() |
| 150 | .duration_since(std::time::UNIX_EPOCH) |
| 151 | .unwrap_or_default() |
| 152 | .as_secs(); |
| 153 | Ok(StoredRole { |
| 154 | name: name.to_string(), |
| 155 | tenant_id: tenant_id.as_u64(), |
| 156 | parent: parent.unwrap_or("").to_string(), |
| 157 | created_at: now, |
| 158 | }) |
| 159 | } |
| 160 | |
| 161 | /// Create a custom role. Returns error if it already exists or would create a cycle. |
| 162 | pub fn create_role( |
no test coverage detected