1use std::{
2 collections::{BTreeMap, BTreeSet},
3 error::Error,
4 fmt,
5};
6
7use serde::{Deserialize, Serialize};
8
9use super::{
10 PermissionEntry, PermissionKeyError, PermissionMetadataEntry, PermissionMetadataExpression,
11 PermissionMetadataExpressionError, PermissionMetadataSet, PermissionMetadataValue,
12 PermissionRuleExpression, PermissionRuleExpressionError, PermissionSegment, PermissionSet,
13};
14
15pub(crate) const OP_GROUP: &str = "op";
17
18#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
20#[serde(default, deny_unknown_fields)]
21pub struct PermissionGroupsConfig {
22 pub default_groups: Vec<String>,
24 pub groups: BTreeMap<String, PermissionGroupConfig>,
26}
27
28impl Default for PermissionGroupsConfig {
29 fn default() -> Self {
30 let mut groups = BTreeMap::new();
31 groups.insert("default".to_owned(), PermissionGroupConfig::default());
32 groups.insert(
33 OP_GROUP.to_owned(),
34 PermissionGroupConfig {
35 allow: vec!["*".to_owned()],
36 ..PermissionGroupConfig::default()
37 },
38 );
39 Self {
40 default_groups: vec!["default".to_owned()],
41 groups,
42 }
43 }
44}
45
46#[derive(Clone, Debug, Default, Deserialize, Serialize, PartialEq, Eq)]
48#[serde(default, deny_unknown_fields)]
49pub struct PermissionGroupConfig {
50 pub priority: i32,
52 pub inherits: Vec<String>,
54 pub allow: Vec<String>,
56 pub deny: Vec<String>,
58 pub metadata: Vec<PermissionMetadataRuleConfig>,
60}
61
62#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
64#[serde(deny_unknown_fields)]
65pub struct PermissionMetadataRuleConfig {
66 pub key: String,
68 pub value: PermissionMetadataValue,
70}
71
72#[derive(Clone, Debug, PartialEq, Eq)]
74pub struct PermissionGroups {
75 default_groups: Vec<String>,
76 groups: BTreeMap<String, PermissionGroup>,
77}
78
79impl PermissionGroups {
80 pub fn from_config(config: PermissionGroupsConfig) -> Result<Self, PermissionConfigError> {
86 for group in &config.default_groups {
87 validate_group_name(group)?;
88 if !config.groups.contains_key(group) {
89 return Err(PermissionConfigError::MissingDefaultGroup(group.to_owned()));
90 }
91 }
92 if !config.groups.contains_key(OP_GROUP) {
93 return Err(PermissionConfigError::MissingRequiredGroup(
94 OP_GROUP.to_owned(),
95 ));
96 }
97 validate_group_inheritance(&config)?;
98
99 let mut groups = BTreeMap::new();
100 for (name, group) in config.groups {
101 validate_group_name(&name)?;
102 let permissions = parse_group_permissions(&name, group.allow, group.deny)?;
103 let metadata = parse_group_metadata(&name, group.metadata)?;
104 groups.insert(
105 name,
106 PermissionGroup {
107 priority: group.priority,
108 inherits: group.inherits,
109 permissions,
110 metadata,
111 },
112 );
113 }
114 Ok(Self {
115 default_groups: config.default_groups,
116 groups,
117 })
118 }
119
120 #[must_use]
122 pub fn default_groups(&self) -> &[String] {
123 &self.default_groups
124 }
125
126 #[must_use]
128 pub const fn groups(&self) -> &BTreeMap<String, PermissionGroup> {
129 &self.groups
130 }
131
132 #[must_use]
134 pub fn contains_group(&self, group: &str) -> bool {
135 self.groups.contains_key(group)
136 }
137
138 #[must_use]
142 pub fn effective_permissions(
143 &self,
144 assigned_groups: &[String],
145 subject_permissions: &PermissionSet,
146 ) -> PermissionSet {
147 let mut effective = PermissionSet::new();
148 let mut appended_groups = BTreeSet::new();
149
150 for group in &self.default_groups {
151 self.append_group(group, &mut effective, &mut appended_groups);
152 }
153 for group in assigned_groups {
154 self.append_group(group, &mut effective, &mut appended_groups);
155 }
156 for entry in subject_permissions.entries() {
157 effective.push(entry.clone());
158 }
159 effective
160 }
161
162 #[must_use]
166 pub fn effective_metadata(
167 &self,
168 assigned_groups: &[String],
169 subject_metadata: &PermissionMetadataSet,
170 ) -> PermissionMetadataSet {
171 let mut effective = PermissionMetadataSet::new();
172 let mut appended_groups = BTreeSet::new();
173
174 for group in &self.default_groups {
175 self.append_group_metadata(group, &mut effective, &mut appended_groups);
176 }
177 for group in assigned_groups {
178 self.append_group_metadata(group, &mut effective, &mut appended_groups);
179 }
180 for entry in subject_metadata.entries() {
181 effective.push(entry.clone());
182 }
183 effective
184 }
185
186 fn append_group(
187 &self,
188 group_name: &str,
189 effective: &mut PermissionSet,
190 appended_groups: &mut BTreeSet<String>,
191 ) {
192 if !appended_groups.insert(group_name.to_owned()) {
193 return;
194 }
195 let Some(group) = self.groups.get(group_name) else {
196 return;
197 };
198 for parent in &group.inherits {
199 self.append_group(parent, effective, appended_groups);
200 }
201 for entry in group.permissions.entries() {
202 effective.push_group(entry.clone(), group_name, group.priority);
203 }
204 }
205
206 fn append_group_metadata(
207 &self,
208 group_name: &str,
209 effective: &mut PermissionMetadataSet,
210 appended_groups: &mut BTreeSet<String>,
211 ) {
212 if !appended_groups.insert(group_name.to_owned()) {
213 return;
214 }
215 let Some(group) = self.groups.get(group_name) else {
216 return;
217 };
218 for parent in &group.inherits {
219 self.append_group_metadata(parent, effective, appended_groups);
220 }
221 for entry in group.metadata.entries() {
222 effective.push_group(entry.clone(), group_name, group.priority);
223 }
224 }
225}
226
227fn parse_group_permissions(
228 group: &str,
229 allow: Vec<String>,
230 deny: Vec<String>,
231) -> Result<PermissionSet, PermissionConfigError> {
232 let mut permissions = PermissionSet::new();
233 for rule in allow {
234 let expression = parse_group_rule(group, rule)?;
235 let (key, context) = expression.into_parts();
236 permissions.push(PermissionEntry::allow_with_context(key, context));
237 }
238 for rule in deny {
239 let expression = parse_group_rule(group, rule)?;
240 let (key, context) = expression.into_parts();
241 permissions.push(PermissionEntry::deny_with_context(key, context));
242 }
243 Ok(permissions)
244}
245
246fn parse_group_metadata(
247 group: &str,
248 rules: Vec<PermissionMetadataRuleConfig>,
249) -> Result<PermissionMetadataSet, PermissionConfigError> {
250 let mut metadata = PermissionMetadataSet::new();
251 for rule in rules {
252 let expression = PermissionMetadataExpression::parse(rule.key).map_err(|source| {
253 PermissionConfigError::InvalidMetadataExpression {
254 group: group.to_owned(),
255 source,
256 }
257 })?;
258 let (key, context) = expression.into_parts();
259 metadata.push(PermissionMetadataEntry::new_with_context(
260 key, context, rule.value,
261 ));
262 }
263 Ok(metadata)
264}
265
266fn parse_group_rule(
267 group: &str,
268 rule: String,
269) -> Result<PermissionRuleExpression, PermissionConfigError> {
270 PermissionRuleExpression::parse(rule).map_err(|source| {
271 PermissionConfigError::InvalidPermissionExpression {
272 group: group.to_owned(),
273 source,
274 }
275 })
276}
277
278fn validate_group_name(group: &str) -> Result<(), PermissionConfigError> {
279 PermissionSegment::parse(group).map_or_else(
280 |source| {
281 Err(PermissionConfigError::InvalidGroupName {
282 group: group.to_owned(),
283 source,
284 })
285 },
286 |_| Ok(()),
287 )
288}
289
290fn validate_group_inheritance(
291 config: &PermissionGroupsConfig,
292) -> Result<(), PermissionConfigError> {
293 for (group, group_config) in &config.groups {
294 for parent in &group_config.inherits {
295 validate_group_name(parent)?;
296 if !config.groups.contains_key(parent) {
297 return Err(PermissionConfigError::MissingInheritedGroup {
298 group: group.to_owned(),
299 inherited: parent.to_owned(),
300 });
301 }
302 }
303 }
304
305 let mut visited = BTreeSet::new();
306 let mut stack = Vec::new();
307 for (group, group_config) in &config.groups {
308 validate_group_inheritance_node(group, group_config, config, &mut visited, &mut stack)?;
309 }
310 Ok(())
311}
312
313fn validate_group_inheritance_node<'a>(
314 group: &'a str,
315 group_config: &'a PermissionGroupConfig,
316 config: &'a PermissionGroupsConfig,
317 visited: &mut BTreeSet<&'a str>,
318 stack: &mut Vec<&'a str>,
319) -> Result<(), PermissionConfigError> {
320 if visited.contains(group) {
321 return Ok(());
322 }
323 if let Some(cycle_start) = stack.iter().position(|ancestor| *ancestor == group) {
324 let mut cycle = stack[cycle_start..]
325 .iter()
326 .map(|group| (*group).to_owned())
327 .collect::<Vec<_>>();
328 cycle.push(group.to_owned());
329 return Err(PermissionConfigError::InheritedGroupCycle(cycle));
330 }
331
332 stack.push(group);
333 for parent in &group_config.inherits {
334 let Some(parent_config) = config.groups.get(parent) else {
335 return Err(PermissionConfigError::MissingInheritedGroup {
336 group: group.to_owned(),
337 inherited: parent.to_owned(),
338 });
339 };
340 validate_group_inheritance_node(parent, parent_config, config, visited, stack)?;
341 }
342 stack.pop();
343 visited.insert(group);
344 Ok(())
345}
346
347#[derive(Clone, Debug, PartialEq, Eq)]
349pub struct PermissionGroup {
350 priority: i32,
351 inherits: Vec<String>,
352 permissions: PermissionSet,
353 metadata: PermissionMetadataSet,
354}
355
356impl PermissionGroup {
357 #[must_use]
359 pub const fn priority(&self) -> i32 {
360 self.priority
361 }
362
363 #[must_use]
365 pub fn inherits(&self) -> &[String] {
366 &self.inherits
367 }
368
369 #[must_use]
371 pub const fn permissions(&self) -> &PermissionSet {
372 &self.permissions
373 }
374
375 #[must_use]
377 pub const fn metadata(&self) -> &PermissionMetadataSet {
378 &self.metadata
379 }
380}
381
382#[derive(Clone, Debug, PartialEq, Eq)]
384pub enum PermissionConfigError {
385 MissingDefaultGroup(String),
387 MissingRequiredGroup(String),
389 MissingInheritedGroup {
391 group: String,
393 inherited: String,
395 },
396 InheritedGroupCycle(Vec<String>),
398 InvalidPermissionExpression {
400 group: String,
402 source: PermissionRuleExpressionError,
404 },
405 InvalidMetadataExpression {
407 group: String,
409 source: PermissionMetadataExpressionError,
411 },
412 InvalidGroupName {
414 group: String,
416 source: PermissionKeyError,
418 },
419}
420
421impl fmt::Display for PermissionConfigError {
422 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
423 match self {
424 Self::MissingDefaultGroup(group) => {
425 write!(
426 formatter,
427 "default permission group '{group}' is not configured"
428 )
429 }
430 Self::MissingRequiredGroup(group) => {
431 write!(
432 formatter,
433 "required permission group '{group}' is not configured"
434 )
435 }
436 Self::MissingInheritedGroup { group, inherited } => write!(
437 formatter,
438 "permission group '{group}' inherits unknown group '{inherited}'"
439 ),
440 Self::InheritedGroupCycle(cycle) => write!(
441 formatter,
442 "permission group inheritance cycle: {}",
443 cycle.join(" -> ")
444 ),
445 Self::InvalidPermissionExpression { group, source } => write!(
446 formatter,
447 "permission group '{group}' contains invalid permission expression: {source}"
448 ),
449 Self::InvalidMetadataExpression { group, source } => write!(
450 formatter,
451 "permission group '{group}' contains invalid metadata expression: {source}"
452 ),
453 Self::InvalidGroupName { group, source } => {
454 write!(
455 formatter,
456 "permission group name '{group}' is invalid: {source}"
457 )
458 }
459 }
460 }
461}
462
463impl Error for PermissionConfigError {}