1use std::mem;
2
3use super::{PermissionContext, PermissionExpr, PermissionKey, PermissionRuleContext};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum PermissionState {
8 Allow,
10 Deny,
12}
13
14#[derive(Clone, Debug, PartialEq, Eq)]
16pub struct PermissionEntry {
17 key: PermissionKey,
18 context: PermissionRuleContext,
19 state: PermissionState,
20}
21
22impl PermissionEntry {
23 #[must_use]
25 pub const fn new(key: PermissionKey, state: PermissionState) -> Self {
26 Self {
27 key,
28 context: PermissionRuleContext::Global,
29 state,
30 }
31 }
32
33 #[must_use]
35 pub const fn new_with_context(
36 key: PermissionKey,
37 context: PermissionRuleContext,
38 state: PermissionState,
39 ) -> Self {
40 Self {
41 key,
42 context,
43 state,
44 }
45 }
46
47 #[must_use]
49 pub const fn allow(key: PermissionKey) -> Self {
50 Self::new(key, PermissionState::Allow)
51 }
52
53 #[must_use]
55 pub const fn allow_with_context(key: PermissionKey, context: PermissionRuleContext) -> Self {
56 Self::new_with_context(key, context, PermissionState::Allow)
57 }
58
59 #[must_use]
61 pub const fn deny(key: PermissionKey) -> Self {
62 Self::new(key, PermissionState::Deny)
63 }
64
65 #[must_use]
67 pub const fn deny_with_context(key: PermissionKey, context: PermissionRuleContext) -> Self {
68 Self::new_with_context(key, context, PermissionState::Deny)
69 }
70
71 #[must_use]
73 pub const fn key(&self) -> &PermissionKey {
74 &self.key
75 }
76
77 #[must_use]
79 pub const fn context(&self) -> &PermissionRuleContext {
80 &self.context
81 }
82
83 #[must_use]
85 pub const fn state(&self) -> PermissionState {
86 self.state
87 }
88}
89
90#[derive(Clone, Debug, Default, PartialEq, Eq)]
92pub struct PermissionSet {
93 entries: Vec<PermissionEntry>,
94 sources: Vec<PermissionResolutionSource>,
95}
96
97impl PermissionSet {
98 #[must_use]
100 pub const fn new() -> Self {
101 Self {
102 entries: Vec::new(),
103 sources: Vec::new(),
104 }
105 }
106
107 #[must_use]
109 pub fn from_entries(entries: impl IntoIterator<Item = PermissionEntry>) -> Self {
110 let entries = entries.into_iter().collect::<Vec<_>>();
111 let sources = vec![PermissionResolutionSource::Subject; entries.len()];
112 Self { entries, sources }
113 }
114
115 #[must_use]
117 pub fn entries(&self) -> &[PermissionEntry] {
118 &self.entries
119 }
120
121 pub fn push(&mut self, entry: PermissionEntry) {
123 self.push_with_source(entry, PermissionResolutionSource::Subject);
124 }
125
126 pub fn allow(&mut self, key: PermissionKey) {
128 self.push(PermissionEntry::allow(key));
129 }
130
131 pub fn allow_in(&mut self, key: PermissionKey, context: PermissionRuleContext) {
133 self.push(PermissionEntry::allow_with_context(key, context));
134 }
135
136 pub fn deny(&mut self, key: PermissionKey) {
138 self.push(PermissionEntry::deny(key));
139 }
140
141 pub fn deny_in(&mut self, key: PermissionKey, context: PermissionRuleContext) {
143 self.push(PermissionEntry::deny_with_context(key, context));
144 }
145
146 pub fn set(&mut self, key: PermissionKey, state: PermissionState) {
148 self.set_in(key, PermissionRuleContext::Global, state);
149 }
150
151 pub fn set_in(
153 &mut self,
154 key: PermissionKey,
155 context: PermissionRuleContext,
156 state: PermissionState,
157 ) {
158 self.retain_entries(|entry| entry.key != key || entry.context != context);
159 self.push(PermissionEntry::new_with_context(key, context, state));
160 }
161
162 pub fn unset(&mut self, key: &PermissionKey) -> bool {
164 self.unset_in(key, &PermissionRuleContext::Global)
165 }
166
167 pub fn unset_in(&mut self, key: &PermissionKey, context: &PermissionRuleContext) -> bool {
169 let old_len = self.entries.len();
170 self.retain_entries(|entry| entry.key() != key || entry.context() != context);
171 self.entries.len() != old_len
172 }
173
174 #[must_use]
176 pub fn resolve_key(&self, key: &PermissionKey) -> Option<PermissionState> {
177 self.resolve_key_in(key, &PermissionContext::global())
178 }
179
180 #[must_use]
182 pub fn resolve_key_in(
183 &self,
184 key: &PermissionKey,
185 context: &PermissionContext,
186 ) -> Option<PermissionState> {
187 self.best_key_candidate(key, context)
188 .map(|candidate| candidate.state)
189 }
190
191 #[must_use]
193 pub fn resolve_key_detailed(&self, key: &PermissionKey) -> Option<PermissionResolution> {
194 self.resolve_key_in_detailed(key, &PermissionContext::global())
195 }
196
197 #[must_use]
199 pub fn resolve_key_in_detailed(
200 &self,
201 key: &PermissionKey,
202 context: &PermissionContext,
203 ) -> Option<PermissionResolution> {
204 self.best_key_candidate(key, context)
205 .map(|candidate| self.permission_resolution(candidate))
206 }
207
208 #[must_use]
210 pub fn resolve_scoped_key(
211 &self,
212 parent: &PermissionKey,
213 key: &PermissionKey,
214 ) -> Option<PermissionState> {
215 self.resolve_scoped_key_in(parent, key, &PermissionContext::global())
216 }
217
218 #[must_use]
220 pub fn resolve_scoped_key_in(
221 &self,
222 parent: &PermissionKey,
223 key: &PermissionKey,
224 context: &PermissionContext,
225 ) -> Option<PermissionState> {
226 self.best_scoped_key_candidate(parent, key, context)
227 .map(|candidate| candidate.state)
228 }
229
230 #[must_use]
232 pub fn resolve_scoped_key_detailed(
233 &self,
234 parent: &PermissionKey,
235 key: &PermissionKey,
236 ) -> Option<PermissionResolution> {
237 self.resolve_scoped_key_in_detailed(parent, key, &PermissionContext::global())
238 }
239
240 #[must_use]
242 pub fn resolve_scoped_key_in_detailed(
243 &self,
244 parent: &PermissionKey,
245 key: &PermissionKey,
246 context: &PermissionContext,
247 ) -> Option<PermissionResolution> {
248 self.best_scoped_key_candidate(parent, key, context)
249 .map(|candidate| self.permission_resolution(candidate))
250 }
251
252 #[must_use]
254 pub fn allows_key(&self, key: &PermissionKey) -> bool {
255 self.resolve_key(key) == Some(PermissionState::Allow)
256 }
257
258 #[must_use]
260 pub fn allows_key_in(&self, key: &PermissionKey, context: &PermissionContext) -> bool {
261 self.resolve_key_in(key, context) == Some(PermissionState::Allow)
262 }
263
264 #[must_use]
266 pub fn allows_scoped_key(&self, parent: &PermissionKey, key: &PermissionKey) -> bool {
267 self.resolve_scoped_key(parent, key) == Some(PermissionState::Allow)
268 }
269
270 #[must_use]
272 pub fn allows_scoped_key_in(
273 &self,
274 parent: &PermissionKey,
275 key: &PermissionKey,
276 context: &PermissionContext,
277 ) -> bool {
278 self.resolve_scoped_key_in(parent, key, context) == Some(PermissionState::Allow)
279 }
280
281 #[must_use]
283 pub fn allows(&self, permission: &PermissionExpr) -> bool {
284 self.allows_in(permission, &PermissionContext::global())
285 }
286
287 #[must_use]
289 pub fn allows_in(&self, permission: &PermissionExpr, context: &PermissionContext) -> bool {
290 self.resolve_in(permission, context) == Some(PermissionState::Allow)
291 }
292
293 #[must_use]
295 pub fn resolve(&self, permission: &PermissionExpr) -> Option<PermissionState> {
296 self.resolve_in(permission, &PermissionContext::global())
297 }
298
299 #[must_use]
301 pub fn resolve_in(
302 &self,
303 permission: &PermissionExpr,
304 context: &PermissionContext,
305 ) -> Option<PermissionState> {
306 match permission {
307 PermissionExpr::Key(key) => self.resolve_key_in(key, context),
308 PermissionExpr::ScopedKey { parent, key } => {
309 self.resolve_scoped_key_in(parent, key, context)
310 }
311 PermissionExpr::All(children) => resolve_all(children, self, context),
312 PermissionExpr::Any(children) => resolve_any(children, self, context),
313 }
314 }
315
316 pub(super) fn push_group(&mut self, entry: PermissionEntry, group: &str, group_priority: i32) {
317 self.push_with_source(
318 entry,
319 PermissionResolutionSource::Group {
320 name: group.to_owned(),
321 priority: group_priority,
322 },
323 );
324 }
325
326 fn push_with_source(&mut self, entry: PermissionEntry, source: PermissionResolutionSource) {
327 self.entries.push(entry);
328 self.sources.push(source);
329 }
330
331 fn retain_entries(&mut self, mut keep: impl FnMut(&PermissionEntry) -> bool) {
332 let entries = mem::take(&mut self.entries);
333 let sources = mem::take(&mut self.sources);
334 for (entry, source) in entries.into_iter().zip(sources) {
335 if keep(&entry) {
336 self.entries.push(entry);
337 self.sources.push(source);
338 }
339 }
340 }
341
342 fn best_key_candidate(
343 &self,
344 key: &PermissionKey,
345 context: &PermissionContext,
346 ) -> Option<PermissionCandidate> {
347 let mut best = None;
348 for (index, (entry, source)) in self.entries.iter().zip(&self.sources).enumerate() {
349 if !entry.context.matches(context) || !entry.key.matches(key) {
350 continue;
351 }
352 push_candidate(
353 &mut best,
354 index,
355 entry.key.specificity(),
356 entry.context.specificity(),
357 source,
358 entry.state,
359 );
360 }
361 best
362 }
363
364 fn best_scoped_key_candidate(
365 &self,
366 parent: &PermissionKey,
367 key: &PermissionKey,
368 context: &PermissionContext,
369 ) -> Option<PermissionCandidate> {
370 let mut best = None;
371 let parent_scopes_key = parent.scopes(key);
372 for (index, (entry, source)) in self.entries.iter().zip(&self.sources).enumerate() {
373 if !entry.context.matches(context) {
374 continue;
375 }
376 let matches_parent = parent_scopes_key && entry.key.matches(parent);
377 let matches_key = entry.key.matches(key);
378 if !matches_parent && !matches_key {
379 continue;
380 }
381
382 let mut specificity = entry.key.specificity();
383 if matches_key && !matches_parent {
384 specificity += 1;
385 }
386 push_candidate(
387 &mut best,
388 index,
389 specificity,
390 entry.context.specificity(),
391 source,
392 entry.state,
393 );
394 }
395 best
396 }
397
398 fn permission_resolution(&self, candidate: PermissionCandidate) -> PermissionResolution {
399 PermissionResolution {
400 entry: self.entries[candidate.entry_index].clone(),
401 source: candidate.source,
402 key_specificity: candidate.key_specificity,
403 context_specificity: candidate.context_specificity,
404 }
405 }
406}
407
408fn resolve_all(
409 children: &[PermissionExpr],
410 permissions: &PermissionSet,
411 context: &PermissionContext,
412) -> Option<PermissionState> {
413 if children.is_empty() {
414 return Some(PermissionState::Allow);
415 }
416 let mut saw_unset = false;
417 for child in children {
418 match permissions.resolve_in(child, context) {
419 Some(PermissionState::Allow) => {}
420 Some(PermissionState::Deny) => return Some(PermissionState::Deny),
421 None => saw_unset = true,
422 }
423 }
424 if saw_unset {
425 None
426 } else {
427 Some(PermissionState::Allow)
428 }
429}
430
431fn resolve_any(
432 children: &[PermissionExpr],
433 permissions: &PermissionSet,
434 context: &PermissionContext,
435) -> Option<PermissionState> {
436 if children.is_empty() {
437 return Some(PermissionState::Deny);
438 }
439 let mut saw_unset = false;
440 for child in children {
441 match permissions.resolve_in(child, context) {
442 Some(PermissionState::Allow) => return Some(PermissionState::Allow),
443 Some(PermissionState::Deny) => {}
444 None => saw_unset = true,
445 }
446 }
447 if saw_unset {
448 None
449 } else {
450 Some(PermissionState::Deny)
451 }
452}
453
454#[derive(Clone, Debug, PartialEq, Eq)]
456pub enum PermissionResolutionSource {
457 Group {
459 name: String,
461 priority: i32,
463 },
464 Subject,
466}
467
468impl PermissionResolutionSource {
469 #[must_use]
471 pub fn group_name(&self) -> Option<&str> {
472 match self {
473 Self::Group { name, .. } => Some(name),
474 Self::Subject => None,
475 }
476 }
477
478 #[must_use]
480 pub const fn group_priority(&self) -> Option<i32> {
481 match self {
482 Self::Group { priority, .. } => Some(*priority),
483 Self::Subject => None,
484 }
485 }
486
487 pub(super) const fn rank(&self) -> usize {
488 match self {
489 Self::Group { .. } => 0,
490 Self::Subject => 1,
491 }
492 }
493
494 pub(super) const fn tie_priority(&self) -> i32 {
495 match self {
496 Self::Group { priority, .. } => *priority,
497 Self::Subject => 0,
498 }
499 }
500}
501
502#[derive(Clone, Debug, PartialEq, Eq)]
504pub struct PermissionResolution {
505 entry: PermissionEntry,
506 source: PermissionResolutionSource,
507 key_specificity: usize,
508 context_specificity: usize,
509}
510
511impl PermissionResolution {
512 #[must_use]
514 pub const fn entry(&self) -> &PermissionEntry {
515 &self.entry
516 }
517
518 #[must_use]
520 pub const fn source(&self) -> &PermissionResolutionSource {
521 &self.source
522 }
523
524 #[must_use]
526 pub const fn state(&self) -> PermissionState {
527 self.entry.state()
528 }
529
530 #[must_use]
532 pub const fn key(&self) -> &PermissionKey {
533 self.entry.key()
534 }
535
536 #[must_use]
538 pub const fn context(&self) -> &PermissionRuleContext {
539 self.entry.context()
540 }
541
542 #[must_use]
544 pub const fn key_specificity(&self) -> usize {
545 self.key_specificity
546 }
547
548 #[must_use]
550 pub const fn context_specificity(&self) -> usize {
551 self.context_specificity
552 }
553}
554
555#[derive(Clone, Debug, PartialEq, Eq)]
556struct PermissionCandidate {
557 entry_index: usize,
558 key_specificity: usize,
559 context_specificity: usize,
560 source: PermissionResolutionSource,
561 state: PermissionState,
562}
563
564impl PermissionCandidate {
565 const fn order(&self) -> (usize, usize, usize, i32) {
566 (
567 self.key_specificity,
568 self.context_specificity,
569 self.source.rank(),
570 self.source.tie_priority(),
571 )
572 }
573}
574
575fn push_candidate(
576 best: &mut Option<PermissionCandidate>,
577 entry_index: usize,
578 key_specificity: usize,
579 context_specificity: usize,
580 source: &PermissionResolutionSource,
581 state: PermissionState,
582) {
583 let candidate = PermissionCandidate {
584 entry_index,
585 key_specificity,
586 context_specificity,
587 source: source.clone(),
588 state,
589 };
590 match best {
591 None => *best = Some(candidate),
592 Some(current) if candidate.order() > current.order() => *best = Some(candidate),
593 Some(current)
594 if candidate.order() == current.order()
595 && current.state == PermissionState::Allow
596 && state == PermissionState::Deny =>
597 {
598 *best = Some(candidate);
599 }
600 _ => {}
601 }
602}