1use hashbrown::HashMap;
17use std::cell::Cell;
18use std::collections::BTreeSet;
19use std::ops::DerefMut;
20use std::sync::Arc;
21
22use concread::arcache::ARCacheBuilder;
23use concread::cowcell::*;
24use uuid::Uuid;
25
26use crate::entry::{Entry, EntryInit, EntryNew};
27use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent};
28use crate::filter::{Filter, FilterValid, ResolveFilterCache, ResolveFilterCacheReadTxn};
29use crate::modify::Modify;
30use crate::prelude::*;
31
32use self::profiles::{
33 AccessControlCreate, AccessControlCreateResolved, AccessControlDelete,
34 AccessControlDeleteResolved, AccessControlModify, AccessControlModifyResolved,
35 AccessControlReceiver, AccessControlReceiverCondition, AccessControlSearch,
36 AccessControlSearchResolved, AccessControlTarget, AccessControlTargetCondition,
37};
38
39use kanidm_proto::scim_v1::server::ScimAttributeEffectiveAccess;
40
41use self::create::{apply_create_access, CreateResult};
42use self::delete::{apply_delete_access, DeleteResult};
43use self::modify::{apply_modify_access, ModifyResult};
44use self::search::{apply_search_access, SearchResult};
45
46const ACP_RESOLVE_FILTER_CACHE_MAX: usize = 256;
47const ACP_RESOLVE_FILTER_CACHE_LOCAL: usize = 0;
48
49mod create;
50mod delete;
51mod migration;
52mod modify;
53pub mod profiles;
54mod protected;
55mod search;
56
57#[derive(Debug, Clone, PartialEq, Eq)]
58pub enum Access {
59 Grant,
60 Deny,
61 Allow(BTreeSet<Attribute>),
62}
63
64impl From<&Access> for ScimAttributeEffectiveAccess {
65 fn from(value: &Access) -> Self {
66 match value {
67 Access::Grant => Self::Grant,
68 Access::Deny => Self::Deny,
69 Access::Allow(set) => Self::Allow(set.clone()),
70 }
71 }
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum AccessClass {
76 Grant,
77 Deny,
78 Allow(BTreeSet<AttrString>),
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
82pub struct AccessEffectivePermission {
83 pub ident: Uuid,
85 pub target: Uuid,
87 pub delete: bool,
88 pub search: Access,
89 pub modify_pres: Access,
90 pub modify_rem: Access,
91 pub modify_pres_class: AccessClass,
92 pub modify_rem_class: AccessClass,
93}
94
95pub enum AccessBasicResult {
96 Deny,
98 Grant,
100 Ignore,
102}
103
104pub enum AccessSrchResult {
105 Deny,
107 Grant,
109 Ignore,
111 Allow { attr: BTreeSet<Attribute> },
120}
121
122pub enum AccessModResult<'a> {
123 Deny,
125 Ignore,
129 Constrain {
133 pres_attr: BTreeSet<Attribute>,
134 rem_attr: BTreeSet<Attribute>,
135 pres_cls: Option<BTreeSet<&'a str>>,
136 rem_cls: Option<BTreeSet<&'a str>>,
137 },
138 Allow {
140 pres_attr: BTreeSet<Attribute>,
141 rem_attr: BTreeSet<Attribute>,
142 pres_class: BTreeSet<&'a str>,
143 rem_class: BTreeSet<&'a str>,
144 },
145}
146
147#[derive(Clone)]
152struct AccessControlsInner {
153 acps_search: Vec<AccessControlSearch>,
154 acps_create: Vec<AccessControlCreate>,
155 acps_modify: Vec<AccessControlModify>,
156 acps_delete: Vec<AccessControlDelete>,
157 sync_agreements: HashMap<Uuid, BTreeSet<Attribute>>,
158 }
161
162pub struct AccessControls {
163 inner: CowCell<AccessControlsInner>,
164 acp_resolve_filter_cache: ResolveFilterCache,
166}
167
168fn resolve_access_conditions(
169 ident: &Identity,
170 ident_memberof: Option<&BTreeSet<Uuid>>,
171 receiver: &AccessControlReceiver,
172 target: &AccessControlTarget,
173 acp_resolve_filter_cache: &mut ResolveFilterCacheReadTxn<'_>,
174) -> Option<(AccessControlReceiverCondition, AccessControlTargetCondition)> {
175 let receiver_condition = match receiver {
176 AccessControlReceiver::Group(groups) => {
177 let group_check = ident_memberof
178 .map(|imo| {
180 trace!(?imo, ?groups);
181 imo.intersection(groups).next().is_some()
182 })
183 .unwrap_or_default();
184
185 if group_check {
186 AccessControlReceiverCondition::GroupChecked
187 } else {
188 return None;
190 }
191 }
192 AccessControlReceiver::EntryManager => AccessControlReceiverCondition::EntryManager,
193 AccessControlReceiver::None => return None,
194 };
196
197 let target_condition = match &target {
198 AccessControlTarget::Scope(filter) => filter
199 .resolve(ident, None, Some(acp_resolve_filter_cache))
200 .map_err(|e| {
201 admin_error!(?e, "A internal filter/event was passed for resolution!?!?");
202 e
203 })
204 .ok()
205 .map(AccessControlTargetCondition::Scope)?,
206 AccessControlTarget::None => return None,
207 };
208
209 Some((receiver_condition, target_condition))
210}
211
212pub trait AccessControlsTransaction<'a> {
213 fn get_search(&self) -> &Vec<AccessControlSearch>;
214 fn get_create(&self) -> &Vec<AccessControlCreate>;
215 fn get_modify(&self) -> &Vec<AccessControlModify>;
216 fn get_delete(&self) -> &Vec<AccessControlDelete>;
217 fn get_sync_agreements(&self) -> &HashMap<Uuid, BTreeSet<Attribute>>;
218
219 #[allow(clippy::mut_from_ref)]
220 fn get_acp_resolve_filter_cache(&self) -> &mut ResolveFilterCacheReadTxn<'a>;
221
222 #[instrument(level = "trace", name = "access::search_related_acp", skip_all)]
223 fn search_related_acp<'b>(
224 &'b self,
225 ident: &Identity,
226 attrs: Option<&BTreeSet<Attribute>>,
227 ) -> Vec<AccessControlSearchResolved<'b>> {
228 let search_state = self.get_search();
229 let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
230
231 let ident_memberof = ident.get_memberof();
259
260 let related_acp: Vec<AccessControlSearchResolved<'b>> = search_state
262 .iter()
263 .filter_map(|acs| {
264 let (receiver_condition, target_condition) = resolve_access_conditions(
280 ident,
281 ident_memberof,
282 &acs.acp.receiver,
283 &acs.acp.target,
284 acp_resolve_filter_cache,
285 )?;
286
287 Some(AccessControlSearchResolved {
288 acp: acs,
289 receiver_condition,
290 target_condition,
291 })
292 })
293 .collect();
294
295 let related_acp = if let Some(r_attrs) = attrs.as_ref() {
297 related_acp
298 .into_iter()
299 .filter(|acs| !acs.acp.attrs.is_disjoint(r_attrs))
300 .collect()
301 } else {
302 related_acp
304 };
305
306 related_acp
307 }
308
309 #[instrument(level = "debug", name = "access::filter_entries", skip_all)]
310 fn filter_entries(
311 &self,
312 ident: &Identity,
313 filter_orig: &Filter<FilterValid>,
314 entries: Vec<Arc<EntrySealedCommitted>>,
315 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
316 let requested_attrs: BTreeSet<Attribute> = filter_orig.get_attr_set();
321
322 if requested_attrs.is_empty() {
324 security_access!("denied ❌ - no attributes were requested in search, denying all entries from release");
325 return Ok(Vec::with_capacity(0));
326 }
327
328 let related_acp = self.search_related_acp(ident, None);
330
331 let entries_is_empty = entries.is_empty();
333 let allowed_entries: Vec<_> = entries
334 .into_iter()
335 .filter(|e| {
336 match apply_search_access(ident, related_acp.as_slice(), e) {
337 SearchResult::Deny => false,
338 SearchResult::Grant => true,
339 SearchResult::Allow(allowed_attrs) => {
340 let decision = requested_attrs.is_subset(&allowed_attrs);
342 security_debug!(
343 ?decision,
344 allowed = ?allowed_attrs,
345 requested = ?requested_attrs,
346 "search attribute decision",
347 );
348 decision
349 }
350 }
351 })
352 .collect();
353
354 if allowed_entries.is_empty() {
355 if !entries_is_empty {
356 security_access!("denied ❌ - no entries were released");
357 }
358 } else {
359 debug!("allowed search of {} entries ✅", allowed_entries.len());
360 }
361
362 Ok(allowed_entries)
363 }
364
365 #[inline(always)]
367 fn search_filter_entries(
368 &self,
369 se: &SearchEvent,
370 entries: Vec<Arc<EntrySealedCommitted>>,
371 ) -> Result<Vec<Arc<EntrySealedCommitted>>, OperationError> {
372 self.filter_entries(&se.ident, &se.filter_orig, entries)
373 }
374
375 #[instrument(
376 level = "debug",
377 name = "access::search_filter_entry_attributes",
378 skip_all
379 )]
380 fn search_filter_entry_attributes(
381 &self,
382 se: &SearchEvent,
383 entries: Vec<Arc<EntrySealedCommitted>>,
384 ) -> Result<Vec<EntryReducedCommitted>, OperationError> {
385 struct DoEffectiveCheck<'b> {
386 modify_related_acp: Vec<AccessControlModifyResolved<'b>>,
387 delete_related_acp: Vec<AccessControlDeleteResolved<'b>>,
388 sync_agmts: &'b HashMap<Uuid, BTreeSet<Attribute>>,
389 }
390
391 match &se.ident.origin {
392 IdentType::Internal(_) => {
393 security_critical!("IMPOSSIBLE STATE: Internal search in external interface?! Returning empty for safety.");
396 return Err(OperationError::InvalidState);
398 }
399 IdentType::Synch(_) => {
400 security_critical!("Blocking sync check");
401 return Err(OperationError::InvalidState);
402 }
403 IdentType::User(u) => u.entry.get_uuid(),
404 };
405
406 let do_effective_check = se.effective_access_check.then(|| {
410 debug!("effective permission check requested during reduction phase");
411
412 let modify_related_acp = self.modify_related_acp(&se.ident);
414 let delete_related_acp = self.delete_related_acp(&se.ident);
416
417 let sync_agmts = self.get_sync_agreements();
418
419 DoEffectiveCheck {
420 modify_related_acp,
421 delete_related_acp,
422 sync_agmts,
423 }
424 });
425
426 let search_related_acp = self.search_related_acp(&se.ident, se.attrs.as_ref());
428
429 let entries_is_empty = entries.is_empty();
431 let allowed_entries: Vec<_> = entries
432 .into_iter()
433 .filter_map(|entry| {
434 match apply_search_access(&se.ident, &search_related_acp, &entry) {
435 SearchResult::Deny => {
436 None
437 }
438 SearchResult::Grant => {
439 error!("An access module allowed full read, this is a BUG! Denying read to prevent data leaks.");
442 None
443 }
444 SearchResult::Allow(allowed_attrs) => {
445 debug!(
447 requested = ?se.attrs,
448 allowed = ?allowed_attrs,
449 "reduction",
450 );
451
452 let reduced_attrs = if let Some(requested) = se.attrs.as_ref() {
454 requested & &allowed_attrs
455 } else {
456 allowed_attrs
457 };
458
459 let effective_permissions = do_effective_check.as_ref().map(|do_check| {
460 self.entry_effective_permission_check(
461 &se.ident,
462 &entry,
463 &search_related_acp,
464 &do_check.modify_related_acp,
465 &do_check.delete_related_acp,
466 do_check.sync_agmts,
467 )
468 })
469 .map(Box::new);
470
471 Some(entry.reduce_attributes(&reduced_attrs, effective_permissions))
472 }
473 }
474
475 })
477 .collect();
478
479 if allowed_entries.is_empty() {
480 if !entries_is_empty {
481 security_access!("reduced to empty set on all entries ❌");
482 }
483 } else {
484 debug!(
485 "attribute set reduced on {} entries ✅",
486 allowed_entries.len()
487 );
488 }
489
490 Ok(allowed_entries)
491 }
492
493 #[instrument(level = "trace", name = "access::modify_related_acp", skip_all)]
494 fn modify_related_acp<'b>(&'b self, ident: &Identity) -> Vec<AccessControlModifyResolved<'b>> {
495 let modify_state = self.get_modify();
497 let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
498
499 let ident_memberof = ident.get_memberof();
500
501 let related_acp: Vec<_> = modify_state
504 .iter()
505 .filter_map(|acs| {
506 trace!(acs_name = ?acs.acp.name);
507 let (receiver_condition, target_condition) = resolve_access_conditions(
508 ident,
509 ident_memberof,
510 &acs.acp.receiver,
511 &acs.acp.target,
512 acp_resolve_filter_cache,
513 )?;
514
515 Some(AccessControlModifyResolved {
516 acp: acs,
517 receiver_condition,
518 target_condition,
519 })
520 })
521 .collect();
522
523 related_acp
524 }
525
526 #[instrument(level = "debug", name = "access::modify_allow_operation", skip_all)]
527 fn modify_allow_operation(
528 &self,
529 me: &ModifyEvent,
530 entries: &[Arc<EntrySealedCommitted>],
531 ) -> Result<bool, OperationError> {
532 let related_acp: Vec<_> = self.modify_related_acp(&me.ident);
535
536 let r = entries.iter().all(|e| {
537 self.modify_allow_operation_per_entry(&me.ident, &related_acp, e, &me.modlist)
538 });
539
540 if r {
541 debug!("allowed modify of {} entries ✅", entries.len());
542 } else {
543 security_access!("denied ❌ - modify may not proceed");
544 }
545 Ok(r)
546 }
547
548 #[instrument(
549 level = "debug",
550 name = "access::batch_modify_allow_operation",
551 skip_all
552 )]
553 fn batch_modify_allow_operation(
554 &self,
555 me: &BatchModifyEvent,
556 entries: &[Arc<EntrySealedCommitted>],
557 ) -> Result<bool, OperationError> {
558 let related_acp = self.modify_related_acp(&me.ident);
561
562 let r = entries.iter().all(|e| {
563 let Some(modlist) = me.modset.get(&e.get_uuid()) else {
567 security_access!(
568 "modlist not present for {}, failing operation.",
569 e.get_uuid()
570 );
571 return false;
572 };
573
574 self.modify_allow_operation_per_entry(&me.ident, &related_acp, e, modlist)
575 });
576
577 if r {
578 debug!("allowed modify of {} entries ✅", entries.len());
579 } else {
580 security_access!("denied ❌ - modifications may not proceed");
581 }
582 Ok(r)
583 }
584
585 fn modify_allow_operation_per_entry(
586 &self,
587 ident: &Identity,
588 related_acp: &[AccessControlModifyResolved<'_>],
589 entry: &Arc<EntrySealedCommitted>,
590 modlist: &ModifyList<ModifyValid>,
591 ) -> bool {
592 let disallow = modlist
593 .iter()
594 .any(|m| matches!(m, Modify::Purged(a) if a == Attribute::Class.as_ref()));
595
596 if disallow {
597 security_access!("Disallowing purge in modification");
598 return false;
599 }
600
601 let requested_pres: BTreeSet<Attribute> = modlist
603 .iter()
604 .filter_map(|m| match m {
605 Modify::Present(a, _) | Modify::Set(a, _) | Modify::Assert(a, _) => Some(a.clone()),
606 Modify::Removed(_, _) | Modify::Purged(_) => None,
607 })
608 .collect();
609
610 let requested_rem: BTreeSet<Attribute> = modlist
611 .iter()
612 .filter_map(|m| match m {
613 Modify::Removed(a, _) | Modify::Purged(a) | Modify::Set(a, _) => Some(a.clone()),
614 Modify::Present(_, _) | Modify::Assert(_, _) => None,
615 })
616 .collect();
617
618 let mut requested_pres_classes: BTreeSet<&str> = Default::default();
619 let mut requested_rem_classes: BTreeSet<&str> = Default::default();
620
621 for modify in modlist.iter() {
624 match modify {
625 Modify::Present(a, v) if a == Attribute::Class.as_ref() => {
626 requested_pres_classes.extend(v.to_str())
627 }
628 Modify::Removed(a, v) if a == Attribute::Class.as_ref() => {
629 requested_rem_classes.extend(v.to_str())
630 }
631 Modify::Set(a, v) if a == Attribute::Class.as_ref() => {
632 if let Some(current_classes) = entry.get_ava_as_iutf8(Attribute::Class) {
635 if let Some(requested_classes) = v.as_iutf8_set() {
636 requested_pres_classes.extend(
641 requested_classes
642 .difference(current_classes)
643 .map(|s| s.as_str()),
644 );
645 requested_rem_classes.extend(
646 current_classes
647 .difference(requested_classes)
648 .map(|s| s.as_str()),
649 );
650 } else {
651 error!("invalid valueset state - requested class set is not valid");
657 return false;
658 }
659 } else {
660 error!("invalid entry state - entry does not have attribute class and is not valid");
661 return false;
662 }
663 }
664 Modify::Set(_, _) | Modify::Removed(_, _) | Modify::Present(_, _) => {}
666 Modify::Purged(_) | Modify::Assert(_, _) => {}
668 }
669 }
670
671 debug!(?requested_pres, "Requested present set");
672 debug!(?requested_rem, "Requested remove set");
673 debug!(?requested_pres_classes, "Requested present class set");
674 debug!(?requested_rem_classes, "Requested remove class set");
675 debug!(entry_id = %entry.get_display_id());
676
677 if requested_pres.is_empty() && requested_rem.is_empty() {
680 security_error!("No modifications were requested");
681 return false;
682 };
683
684 let sync_agmts = self.get_sync_agreements();
685
686 match apply_modify_access(ident, related_acp, sync_agmts, entry) {
687 ModifyResult::Deny => false,
688 ModifyResult::Grant => true,
689 ModifyResult::Allow {
690 pres,
691 rem,
692 pres_cls,
693 rem_cls,
694 } => {
695 let mut decision = true;
696
697 if !requested_pres.is_subset(&pres) {
698 security_error!("requested_pres is not a subset of allowed");
699 security_error!(
700 "requested_pres: {:?} !⊆ allowed: {:?}",
701 requested_pres,
702 pres
703 );
704 decision = false
705 };
706
707 if !requested_rem.is_subset(&rem) {
708 security_error!("requested_rem is not a subset of allowed");
709 security_error!("requested_rem: {:?} !⊆ allowed: {:?}", requested_rem, rem);
710 decision = false;
711 };
712
713 if !requested_pres_classes.is_subset(&pres_cls) {
714 security_error!("requested_pres_classes is not a subset of allowed");
715 security_error!(
716 "requested_classes: {:?} !⊆ allowed: {:?}",
717 requested_pres_classes,
718 pres_cls
719 );
720 decision = false;
721 };
722
723 if !requested_rem_classes.is_subset(&rem_cls) {
724 security_error!("requested_rem_classes is not a subset of allowed");
725 security_error!(
726 "requested_classes: {:?} !⊆ allowed: {:?}",
727 requested_rem_classes,
728 rem_cls
729 );
730 decision = false;
731 }
732
733 if decision {
734 debug!("passed pres, rem, classes check.");
735 }
736
737 decision
739 }
740 }
741 }
742
743 #[instrument(level = "debug", name = "access::create_allow_operation", skip_all)]
744 fn create_allow_operation(
745 &self,
746 ce: &CreateEvent,
747 entries: &[Entry<EntryInit, EntryNew>],
748 ) -> Result<bool, OperationError> {
749 let create_state = self.get_create();
751 let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
752
753 let ident_memberof = ce.ident.get_memberof();
754
755 let related_acp: Vec<_> = create_state
757 .iter()
758 .filter_map(|acs| {
759 let (receiver_condition, target_condition) = resolve_access_conditions(
760 &ce.ident,
761 ident_memberof,
762 &acs.acp.receiver,
763 &acs.acp.target,
764 acp_resolve_filter_cache,
765 )?;
766
767 Some(AccessControlCreateResolved {
768 acp: acs,
769 receiver_condition,
770 target_condition,
771 })
772 })
773 .collect();
774
775 let decision = entries.iter().all(|e| {
777 let requested_pres: BTreeSet<_> = e.attr_keys().cloned().collect();
778 let Some(requested_pres_classes) = e
779 .get_ava_as_iutf8(Attribute::Class)
780 .map(|set| set.iter().map(|s| s.as_str()).collect::<BTreeSet<_>>())
781 else {
782 error!("unable to perform access control checks on entry with no classes, denied.");
783 return false;
784 };
785
786 debug!(?requested_pres, "Requested present set");
787 debug!(?requested_pres_classes, "Requested present class set");
788 debug!(entry_id = %e.get_display_id());
789
790 match apply_create_access(&ce.ident, related_acp.as_slice(), e) {
791 CreateResult::Deny => false,
792 CreateResult::Grant => true,
793 CreateResult::Allow { pres, pres_cls } => {
794 let mut decision = true;
795
796 if !requested_pres.is_subset(&pres) {
797 security_error!("requested_pres is not a subset of allowed");
798 security_error!(
799 "requested_pres: {:?} !⊆ allowed: {:?}",
800 requested_pres,
801 pres
802 );
803 decision = false
804 };
805
806 if !requested_pres_classes.is_subset(&pres_cls) {
807 security_error!("requested_pres_classes is not a subset of allowed");
808 security_error!(
809 "requested_classes: {:?} !⊆ allowed: {:?}",
810 requested_pres_classes,
811 pres_cls
812 );
813 decision = false;
814 };
815
816 if decision {
817 debug!("passed pres, classes check.");
818 }
819
820 decision
821 }
822 }
823 });
824
825 if decision {
826 debug!("allowed create of {} entries ✅", entries.len());
827 } else {
828 security_access!("denied ❌ - create may not proceed");
829 }
830
831 Ok(decision)
832 }
833
834 #[instrument(level = "trace", name = "access::delete_related_acp", skip_all)]
835 fn delete_related_acp<'b>(&'b self, ident: &Identity) -> Vec<AccessControlDeleteResolved<'b>> {
836 let delete_state = self.get_delete();
838 let acp_resolve_filter_cache = self.get_acp_resolve_filter_cache();
839
840 let ident_memberof = ident.get_memberof();
841
842 let related_acp: Vec<_> = delete_state
843 .iter()
844 .filter_map(|acs| {
845 let (receiver_condition, target_condition) = resolve_access_conditions(
846 ident,
847 ident_memberof,
848 &acs.acp.receiver,
849 &acs.acp.target,
850 acp_resolve_filter_cache,
851 )?;
852
853 Some(AccessControlDeleteResolved {
854 acp: acs,
855 receiver_condition,
856 target_condition,
857 })
858 })
859 .collect();
860
861 related_acp
862 }
863
864 #[instrument(level = "debug", name = "access::delete_allow_operation", skip_all)]
865 fn delete_allow_operation(
866 &self,
867 de: &DeleteEvent,
868 entries: &[Arc<EntrySealedCommitted>],
869 ) -> Result<bool, OperationError> {
870 let related_acp = self.delete_related_acp(&de.ident);
872
873 let r = entries.iter().all(|e| {
875 match apply_delete_access(&de.ident, related_acp.as_slice(), e) {
876 DeleteResult::Deny => false,
877 DeleteResult::Grant => true,
878 }
879 });
880 if r {
881 debug!("allowed delete of {} entries ✅", entries.len());
882 } else {
883 security_access!("denied ❌ - delete may not proceed");
884 }
885 Ok(r)
886 }
887
888 #[instrument(level = "debug", name = "access::effective_permission_check", skip_all)]
889 fn effective_permission_check(
890 &self,
891 ident: &Identity,
892 attrs: Option<BTreeSet<Attribute>>,
893 entries: &[Arc<EntrySealedCommitted>],
894 ) -> Result<Vec<AccessEffectivePermission>, OperationError> {
895 trace!(ident = %ident, "Effective permission check");
904 let search_related_acp = self.search_related_acp(ident, attrs.as_ref());
909 let modify_related_acp = self.modify_related_acp(ident);
911 let delete_related_acp = self.delete_related_acp(ident);
913
914 let sync_agmts = self.get_sync_agreements();
915
916 let effective_permissions: Vec<_> = entries
917 .iter()
918 .map(|entry| {
919 self.entry_effective_permission_check(
920 ident,
921 entry,
922 &search_related_acp,
923 &modify_related_acp,
924 &delete_related_acp,
925 sync_agmts,
926 )
927 })
928 .collect();
929
930 effective_permissions.iter().for_each(|ep| {
931 trace!(?ep);
932 });
933
934 Ok(effective_permissions)
935 }
936
937 fn entry_effective_permission_check<'b>(
938 &'b self,
939 ident: &Identity,
940 entry: &Arc<EntrySealedCommitted>,
941 search_related_acp: &[AccessControlSearchResolved<'b>],
942 modify_related_acp: &[AccessControlModifyResolved<'b>],
943 delete_related_acp: &[AccessControlDeleteResolved<'b>],
944 sync_agmts: &HashMap<Uuid, BTreeSet<Attribute>>,
945 ) -> AccessEffectivePermission {
946 let search_effective = match apply_search_access(ident, search_related_acp, entry) {
948 SearchResult::Deny => Access::Deny,
949 SearchResult::Grant => Access::Grant,
950 SearchResult::Allow(allowed_attrs) => {
951 Access::Allow(allowed_attrs.into_iter().collect())
953 }
954 };
955
956 let (modify_pres, modify_rem, modify_pres_class, modify_rem_class) =
958 match apply_modify_access(ident, modify_related_acp, sync_agmts, entry) {
959 ModifyResult::Deny => (
960 Access::Deny,
961 Access::Deny,
962 AccessClass::Deny,
963 AccessClass::Deny,
964 ),
965 ModifyResult::Grant => (
966 Access::Grant,
967 Access::Grant,
968 AccessClass::Grant,
969 AccessClass::Grant,
970 ),
971 ModifyResult::Allow {
972 pres,
973 rem,
974 pres_cls,
975 rem_cls,
976 } => (
977 Access::Allow(pres.into_iter().collect()),
978 Access::Allow(rem.into_iter().collect()),
979 AccessClass::Allow(pres_cls.into_iter().map(|s| s.into()).collect()),
980 AccessClass::Allow(rem_cls.into_iter().map(|s| s.into()).collect()),
981 ),
982 };
983
984 let delete_status = apply_delete_access(ident, delete_related_acp, entry);
986
987 let delete = match delete_status {
988 DeleteResult::Deny => false,
989 DeleteResult::Grant => true,
990 };
991
992 AccessEffectivePermission {
993 ident: ident.get_uuid(),
994 target: entry.get_uuid(),
995 delete,
996 search: search_effective,
997 modify_pres,
998 modify_rem,
999 modify_pres_class,
1000 modify_rem_class,
1001 }
1002 }
1003}
1004
1005pub struct AccessControlsWriteTransaction<'a> {
1006 inner: CowCellWriteTxn<'a, AccessControlsInner>,
1007 acp_resolve_filter_cache: Cell<ResolveFilterCacheReadTxn<'a>>,
1008}
1009
1010impl AccessControlsWriteTransaction<'_> {
1011 pub fn update_search(
1015 &mut self,
1016 mut acps: Vec<AccessControlSearch>,
1017 ) -> Result<(), OperationError> {
1018 std::mem::swap(&mut acps, &mut self.inner.deref_mut().acps_search);
1019 Ok(())
1020 }
1021
1022 pub fn update_create(
1023 &mut self,
1024 mut acps: Vec<AccessControlCreate>,
1025 ) -> Result<(), OperationError> {
1026 std::mem::swap(&mut acps, &mut self.inner.deref_mut().acps_create);
1027 Ok(())
1028 }
1029
1030 pub fn update_modify(
1031 &mut self,
1032 mut acps: Vec<AccessControlModify>,
1033 ) -> Result<(), OperationError> {
1034 std::mem::swap(&mut acps, &mut self.inner.deref_mut().acps_modify);
1035 Ok(())
1036 }
1037
1038 pub fn update_delete(
1039 &mut self,
1040 mut acps: Vec<AccessControlDelete>,
1041 ) -> Result<(), OperationError> {
1042 std::mem::swap(&mut acps, &mut self.inner.deref_mut().acps_delete);
1043 Ok(())
1044 }
1045
1046 pub fn update_sync_agreements(
1047 &mut self,
1048 mut sync_agreements: HashMap<Uuid, BTreeSet<Attribute>>,
1049 ) {
1050 std::mem::swap(
1051 &mut sync_agreements,
1052 &mut self.inner.deref_mut().sync_agreements,
1053 );
1054 }
1055
1056 pub fn commit(self) -> Result<(), OperationError> {
1057 self.inner.commit();
1058
1059 Ok(())
1060 }
1061}
1062
1063impl<'a> AccessControlsTransaction<'a> for AccessControlsWriteTransaction<'a> {
1064 fn get_search(&self) -> &Vec<AccessControlSearch> {
1065 &self.inner.acps_search
1066 }
1067
1068 fn get_create(&self) -> &Vec<AccessControlCreate> {
1069 &self.inner.acps_create
1070 }
1071
1072 fn get_modify(&self) -> &Vec<AccessControlModify> {
1073 &self.inner.acps_modify
1074 }
1075
1076 fn get_delete(&self) -> &Vec<AccessControlDelete> {
1077 &self.inner.acps_delete
1078 }
1079
1080 fn get_sync_agreements(&self) -> &HashMap<Uuid, BTreeSet<Attribute>> {
1081 &self.inner.sync_agreements
1082 }
1083
1084 fn get_acp_resolve_filter_cache(&self) -> &mut ResolveFilterCacheReadTxn<'a> {
1085 unsafe {
1086 let mptr = self.acp_resolve_filter_cache.as_ptr();
1087 &mut (*mptr) as &mut ResolveFilterCacheReadTxn<'a>
1088 }
1089 }
1090}
1091
1092pub struct AccessControlsReadTransaction<'a> {
1097 inner: CowCellReadTxn<AccessControlsInner>,
1098 acp_resolve_filter_cache: Cell<ResolveFilterCacheReadTxn<'a>>,
1100}
1101
1102unsafe impl Sync for AccessControlsReadTransaction<'_> {}
1103
1104unsafe impl Send for AccessControlsReadTransaction<'_> {}
1105
1106impl<'a> AccessControlsTransaction<'a> for AccessControlsReadTransaction<'a> {
1107 fn get_search(&self) -> &Vec<AccessControlSearch> {
1108 &self.inner.acps_search
1109 }
1110
1111 fn get_create(&self) -> &Vec<AccessControlCreate> {
1112 &self.inner.acps_create
1113 }
1114
1115 fn get_modify(&self) -> &Vec<AccessControlModify> {
1116 &self.inner.acps_modify
1117 }
1118
1119 fn get_delete(&self) -> &Vec<AccessControlDelete> {
1120 &self.inner.acps_delete
1121 }
1122
1123 fn get_sync_agreements(&self) -> &HashMap<Uuid, BTreeSet<Attribute>> {
1124 &self.inner.sync_agreements
1125 }
1126
1127 fn get_acp_resolve_filter_cache(&self) -> &mut ResolveFilterCacheReadTxn<'a> {
1128 unsafe {
1129 let mptr = self.acp_resolve_filter_cache.as_ptr();
1130 &mut (*mptr) as &mut ResolveFilterCacheReadTxn<'a>
1131 }
1132 }
1133}
1134
1135impl Default for AccessControls {
1140 #![allow(clippy::expect_used)]
1141 fn default() -> Self {
1142 AccessControls {
1143 inner: CowCell::new(AccessControlsInner {
1144 acps_search: Vec::with_capacity(0),
1145 acps_create: Vec::with_capacity(0),
1146 acps_modify: Vec::with_capacity(0),
1147 acps_delete: Vec::with_capacity(0),
1148 sync_agreements: HashMap::default(),
1149 }),
1150 acp_resolve_filter_cache: ARCacheBuilder::new()
1153 .set_size(ACP_RESOLVE_FILTER_CACHE_MAX, ACP_RESOLVE_FILTER_CACHE_LOCAL)
1154 .set_reader_quiesce(true)
1155 .build()
1156 .expect("Failed to construct acp_resolve_filter_cache"),
1157 }
1158 }
1159}
1160
1161impl AccessControls {
1162 pub fn try_quiesce(&self) {
1163 self.acp_resolve_filter_cache.try_quiesce();
1164 }
1165
1166 pub fn read(&self) -> AccessControlsReadTransaction<'_> {
1167 AccessControlsReadTransaction {
1168 inner: self.inner.read(),
1169 acp_resolve_filter_cache: Cell::new(self.acp_resolve_filter_cache.read()),
1171 }
1172 }
1173
1174 pub fn write(&self) -> AccessControlsWriteTransaction<'_> {
1175 AccessControlsWriteTransaction {
1176 inner: self.inner.write(),
1177 acp_resolve_filter_cache: Cell::new(self.acp_resolve_filter_cache.read()),
1180 }
1181 }
1182}
1183
1184#[cfg(test)]
1185mod tests {
1186 use hashbrown::HashMap;
1187 use std::collections::BTreeSet;
1188 use std::sync::Arc;
1189
1190 use uuid::uuid;
1191
1192 use super::{
1193 profiles::{
1194 AccessControlCreate, AccessControlDelete, AccessControlModify, AccessControlProfile,
1195 AccessControlSearch, AccessControlTarget,
1196 },
1197 Access, AccessClass, AccessControls, AccessControlsTransaction, AccessEffectivePermission,
1198 };
1199 use crate::migration_data::BUILTIN_ACCOUNT_ANONYMOUS;
1200 use crate::prelude::*;
1201 use crate::valueset::ValueSetIname;
1202
1203 const UUID_TEST_ACCOUNT_1: Uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
1204 const UUID_TEST_ACCOUNT_2: Uuid = uuid::uuid!("cec0852a-abdf-4ea6-9dae-d3157cb33d3a");
1205 const UUID_TEST_GROUP_1: Uuid = uuid::uuid!("81ec1640-3637-4a2f-8a52-874fa3c3c92f");
1206 const UUID_TEST_GROUP_2: Uuid = uuid::uuid!("acae81d6-5ea7-4bd8-8f7f-fcec4c0dd647");
1207
1208 pub static E_TEST_ACCOUNT_1: LazyLock<Arc<EntrySealedCommitted>> = LazyLock::new(|| {
1209 Arc::new(
1210 entry_init!(
1211 (Attribute::Class, EntryClass::Object.to_value()),
1212 (Attribute::Name, Value::new_iname("test_account_1")),
1213 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
1214 (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP_1))
1215 )
1216 .into_sealed_committed(),
1217 )
1218 });
1219 pub static E_TEST_ACCOUNT_2: LazyLock<Arc<EntrySealedCommitted>> = LazyLock::new(|| {
1220 Arc::new(
1221 entry_init!(
1222 (Attribute::Class, EntryClass::Object.to_value()),
1223 (Attribute::Name, Value::new_iname("test_account_1")),
1224 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_2)),
1225 (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP_2))
1226 )
1227 .into_sealed_committed(),
1228 )
1229 });
1230
1231 macro_rules! acp_from_entry_err {
1232 (
1233 $qs:expr,
1234 $e:expr,
1235 $type:ty
1236 ) => {{
1237 let ev1 = $e.into_sealed_committed();
1238
1239 let r1 = <$type>::try_from($qs, &ev1);
1240 error!(?r1);
1241 assert!(r1.is_err());
1242 }};
1243 }
1244
1245 macro_rules! acp_from_entry_ok {
1246 (
1247 $qs:expr,
1248 $e:expr,
1249 $type:ty
1250 ) => {{
1251 let ev1 = $e.into_sealed_committed();
1252
1253 let r1 = <$type>::try_from($qs, &ev1);
1254 assert!(r1.is_ok());
1255 r1.unwrap()
1256 }};
1257 }
1258
1259 #[qs_test]
1260 async fn test_access_acp_parser(qs: &QueryServer) {
1261 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1270
1271 acp_from_entry_err!(
1272 &mut qs_write,
1273 entry_init!(
1274 (Attribute::Class, EntryClass::Object.to_value()),
1275 (Attribute::Name, Value::new_iname("acp_invalid")),
1276 (
1277 Attribute::Uuid,
1278 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1279 )
1280 ),
1281 AccessControlProfile
1282 );
1283
1284 acp_from_entry_err!(
1285 &mut qs_write,
1286 entry_init!(
1287 (Attribute::Class, EntryClass::Object.to_value()),
1288 (
1289 Attribute::Class,
1290 EntryClass::AccessControlProfile.to_value()
1291 ),
1292 (
1293 Attribute::Class,
1294 EntryClass::AccessControlReceiverGroup.to_value()
1295 ),
1296 (
1297 Attribute::Class,
1298 EntryClass::AccessControlTargetScope.to_value()
1299 ),
1300 (Attribute::Name, Value::new_iname("acp_invalid")),
1301 (
1302 Attribute::Uuid,
1303 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1304 )
1305 ),
1306 AccessControlProfile
1307 );
1308
1309 acp_from_entry_err!(
1310 &mut qs_write,
1311 entry_init!(
1312 (Attribute::Class, EntryClass::Object.to_value()),
1313 (
1314 Attribute::Class,
1315 EntryClass::AccessControlProfile.to_value()
1316 ),
1317 (
1318 Attribute::Class,
1319 EntryClass::AccessControlReceiverGroup.to_value()
1320 ),
1321 (
1322 Attribute::Class,
1323 EntryClass::AccessControlTargetScope.to_value()
1324 ),
1325 (Attribute::Name, Value::new_iname("acp_invalid")),
1326 (
1327 Attribute::Uuid,
1328 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1329 ),
1330 (Attribute::AcpReceiverGroup, Value::Bool(true)),
1331 (Attribute::AcpTargetScope, Value::Bool(true))
1332 ),
1333 AccessControlProfile
1334 );
1335
1336 acp_from_entry_ok!(
1338 &mut qs_write,
1339 entry_init!(
1340 (Attribute::Class, EntryClass::Object.to_value()),
1341 (
1342 Attribute::Class,
1343 EntryClass::AccessControlProfile.to_value()
1344 ),
1345 (
1346 Attribute::Class,
1347 EntryClass::AccessControlReceiverGroup.to_value()
1348 ),
1349 (
1350 Attribute::Class,
1351 EntryClass::AccessControlTargetScope.to_value()
1352 ),
1353 (Attribute::Name, Value::new_iname("acp_valid")),
1354 (
1355 Attribute::Uuid,
1356 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1357 ),
1358 (
1359 Attribute::AcpReceiverGroup,
1360 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1361 ),
1362 (
1363 Attribute::AcpTargetScope,
1364 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1365 )
1366 ),
1367 AccessControlProfile
1368 );
1369 }
1370
1371 #[qs_test]
1372 async fn test_access_acp_delete_parser(qs: &QueryServer) {
1373 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1374
1375 acp_from_entry_err!(
1376 &mut qs_write,
1377 entry_init!(
1378 (Attribute::Class, EntryClass::Object.to_value()),
1379 (
1380 Attribute::Class,
1381 EntryClass::AccessControlProfile.to_value()
1382 ),
1383 (Attribute::Name, Value::new_iname("acp_valid")),
1384 (
1385 Attribute::Uuid,
1386 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1387 ),
1388 (
1389 Attribute::AcpReceiverGroup,
1390 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1391 ),
1392 (
1393 Attribute::AcpTargetScope,
1394 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1395 )
1396 ),
1397 AccessControlDelete
1398 );
1399
1400 acp_from_entry_ok!(
1401 &mut qs_write,
1402 entry_init!(
1403 (Attribute::Class, EntryClass::Object.to_value()),
1404 (
1405 Attribute::Class,
1406 EntryClass::AccessControlProfile.to_value()
1407 ),
1408 (Attribute::Class, EntryClass::AccessControlDelete.to_value()),
1409 (Attribute::Name, Value::new_iname("acp_valid")),
1410 (
1411 Attribute::Uuid,
1412 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1413 ),
1414 (
1415 Attribute::AcpReceiverGroup,
1416 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1417 ),
1418 (
1419 Attribute::AcpTargetScope,
1420 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1421 )
1422 ),
1423 AccessControlDelete
1424 );
1425 }
1426
1427 #[qs_test]
1428 async fn test_access_acp_search_parser(qs: &QueryServer) {
1429 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1431
1432 acp_from_entry_err!(
1434 &mut qs_write,
1435 entry_init!(
1436 (Attribute::Class, EntryClass::Object.to_value()),
1437 (Attribute::Class, EntryClass::AccessControlSearch.to_value()),
1438 (Attribute::Name, Value::new_iname("acp_valid")),
1439 (
1440 Attribute::Uuid,
1441 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1442 ),
1443 (
1444 Attribute::AcpReceiverGroup,
1445 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1446 ),
1447 (
1448 Attribute::AcpTargetScope,
1449 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1450 ),
1451 (Attribute::AcpSearchAttr, Value::from(Attribute::Name)),
1452 (Attribute::AcpSearchAttr, Value::new_iutf8("class"))
1453 ),
1454 AccessControlSearch
1455 );
1456
1457 acp_from_entry_err!(
1459 &mut qs_write,
1460 entry_init!(
1461 (Attribute::Class, EntryClass::Object.to_value()),
1462 (
1463 Attribute::Class,
1464 EntryClass::AccessControlProfile.to_value()
1465 ),
1466 (Attribute::Name, Value::new_iname("acp_valid")),
1467 (
1468 Attribute::Uuid,
1469 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1470 ),
1471 (
1472 Attribute::AcpReceiverGroup,
1473 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1474 ),
1475 (
1476 Attribute::AcpTargetScope,
1477 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1478 ),
1479 (Attribute::AcpSearchAttr, Value::from(Attribute::Name)),
1480 (Attribute::AcpSearchAttr, Value::new_iutf8("class"))
1481 ),
1482 AccessControlSearch
1483 );
1484
1485 acp_from_entry_err!(
1487 &mut qs_write,
1488 entry_init!(
1489 (Attribute::Class, EntryClass::Object.to_value()),
1490 (
1491 Attribute::Class,
1492 EntryClass::AccessControlProfile.to_value()
1493 ),
1494 (Attribute::Class, EntryClass::AccessControlSearch.to_value()),
1495 (Attribute::Name, Value::new_iname("acp_valid")),
1496 (
1497 Attribute::Uuid,
1498 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1499 ),
1500 (
1501 Attribute::AcpReceiverGroup,
1502 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1503 ),
1504 (
1505 Attribute::AcpTargetScope,
1506 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1507 )
1508 ),
1509 AccessControlSearch
1510 );
1511
1512 acp_from_entry_ok!(
1514 &mut qs_write,
1515 entry_init!(
1516 (Attribute::Class, EntryClass::Object.to_value()),
1517 (
1518 Attribute::Class,
1519 EntryClass::AccessControlProfile.to_value()
1520 ),
1521 (Attribute::Class, EntryClass::AccessControlSearch.to_value()),
1522 (Attribute::Name, Value::new_iname("acp_valid")),
1523 (
1524 Attribute::Uuid,
1525 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1526 ),
1527 (
1528 Attribute::AcpReceiverGroup,
1529 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1530 ),
1531 (
1532 Attribute::AcpTargetScope,
1533 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1534 ),
1535 (Attribute::AcpSearchAttr, Value::from(Attribute::Name)),
1536 (Attribute::AcpSearchAttr, Value::new_iutf8("class"))
1537 ),
1538 AccessControlSearch
1539 );
1540 }
1541
1542 #[qs_test]
1543 async fn test_access_acp_modify_parser(qs: &QueryServer) {
1544 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1546
1547 acp_from_entry_err!(
1548 &mut qs_write,
1549 entry_init!(
1550 (Attribute::Class, EntryClass::Object.to_value()),
1551 (
1552 Attribute::Class,
1553 EntryClass::AccessControlProfile.to_value()
1554 ),
1555 (Attribute::Name, Value::new_iname("acp_invalid")),
1556 (
1557 Attribute::Uuid,
1558 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1559 ),
1560 (
1561 Attribute::AcpReceiverGroup,
1562 Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1563 ),
1564 (
1565 Attribute::AcpTargetScope,
1566 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1567 )
1568 ),
1569 AccessControlModify
1570 );
1571
1572 acp_from_entry_ok!(
1573 &mut qs_write,
1574 entry_init!(
1575 (Attribute::Class, EntryClass::Object.to_value()),
1576 (
1577 Attribute::Class,
1578 EntryClass::AccessControlProfile.to_value()
1579 ),
1580 (Attribute::Class, EntryClass::AccessControlModify.to_value()),
1581 (Attribute::Name, Value::new_iname("acp_valid")),
1582 (
1583 Attribute::Uuid,
1584 Value::Uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1585 ),
1586 (
1587 Attribute::AcpReceiverGroup,
1588 Value::Refer(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1589 ),
1590 (
1591 Attribute::AcpTargetScope,
1592 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1593 )
1594 ),
1595 AccessControlModify
1596 );
1597
1598 acp_from_entry_ok!(
1599 &mut qs_write,
1600 entry_init!(
1601 (Attribute::Class, EntryClass::Object.to_value()),
1602 (
1603 Attribute::Class,
1604 EntryClass::AccessControlProfile.to_value()
1605 ),
1606 (Attribute::Class, EntryClass::AccessControlModify.to_value()),
1607 (Attribute::Name, Value::new_iname("acp_valid")),
1608 (
1609 Attribute::Uuid,
1610 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1611 ),
1612 (
1613 Attribute::AcpReceiverGroup,
1614 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1615 ),
1616 (
1617 Attribute::AcpTargetScope,
1618 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1619 ),
1620 (
1621 Attribute::AcpModifyRemovedAttr,
1622 Value::from(Attribute::Name)
1623 ),
1624 (
1625 Attribute::AcpModifyPresentAttr,
1626 Value::from(Attribute::Name)
1627 ),
1628 (Attribute::AcpModifyClass, EntryClass::Object.to_value())
1629 ),
1630 AccessControlModify
1631 );
1632 }
1633
1634 #[qs_test]
1635 async fn test_access_acp_create_parser(qs: &QueryServer) {
1636 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1638
1639 acp_from_entry_err!(
1640 &mut qs_write,
1641 entry_init!(
1642 (Attribute::Class, EntryClass::Object.to_value()),
1643 (
1644 Attribute::Class,
1645 EntryClass::AccessControlProfile.to_value()
1646 ),
1647 (Attribute::Name, Value::new_iname("acp_invalid")),
1648 (
1649 Attribute::Uuid,
1650 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1651 ),
1652 (
1653 Attribute::AcpReceiverGroup,
1654 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1655 ),
1656 (
1657 Attribute::AcpTargetScope,
1658 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1659 ),
1660 (Attribute::AcpCreateAttr, Value::from(Attribute::Name)),
1661 (Attribute::AcpCreateClass, EntryClass::Object.to_value())
1662 ),
1663 AccessControlCreate
1664 );
1665
1666 acp_from_entry_ok!(
1667 &mut qs_write,
1668 entry_init!(
1669 (Attribute::Class, EntryClass::Object.to_value()),
1670 (
1671 Attribute::Class,
1672 EntryClass::AccessControlProfile.to_value()
1673 ),
1674 (Attribute::Class, EntryClass::AccessControlCreate.to_value()),
1675 (Attribute::Name, Value::new_iname("acp_valid")),
1676 (
1677 Attribute::Uuid,
1678 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1679 ),
1680 (
1681 Attribute::AcpReceiverGroup,
1682 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1683 ),
1684 (
1685 Attribute::AcpTargetScope,
1686 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1687 )
1688 ),
1689 AccessControlCreate
1690 );
1691
1692 acp_from_entry_ok!(
1693 &mut qs_write,
1694 entry_init!(
1695 (Attribute::Class, EntryClass::Object.to_value()),
1696 (
1697 Attribute::Class,
1698 EntryClass::AccessControlProfile.to_value()
1699 ),
1700 (Attribute::Class, EntryClass::AccessControlCreate.to_value()),
1701 (Attribute::Name, Value::new_iname("acp_valid")),
1702 (
1703 Attribute::Uuid,
1704 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1705 ),
1706 (
1707 Attribute::AcpReceiverGroup,
1708 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1709 ),
1710 (
1711 Attribute::AcpTargetScope,
1712 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1713 ),
1714 (Attribute::AcpCreateAttr, Value::from(Attribute::Name)),
1715 (Attribute::AcpCreateClass, EntryClass::Object.to_value())
1716 ),
1717 AccessControlCreate
1718 );
1719 }
1720
1721 #[qs_test]
1722 async fn test_access_acp_compound_parser(qs: &QueryServer) {
1723 let mut qs_write = qs.write(duration_from_epoch_now()).await.unwrap();
1728
1729 let e = entry_init!(
1730 (Attribute::Class, EntryClass::Object.to_value()),
1731 (
1732 Attribute::Class,
1733 EntryClass::AccessControlProfile.to_value()
1734 ),
1735 (Attribute::Class, EntryClass::AccessControlCreate.to_value()),
1736 (Attribute::Class, EntryClass::AccessControlDelete.to_value()),
1737 (Attribute::Class, EntryClass::AccessControlModify.to_value()),
1738 (Attribute::Class, EntryClass::AccessControlSearch.to_value()),
1739 (Attribute::Name, Value::new_iname("acp_valid")),
1740 (
1741 Attribute::Uuid,
1742 Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1743 ),
1744 (
1745 Attribute::AcpReceiverGroup,
1746 Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
1747 ),
1748 (
1749 Attribute::AcpTargetScope,
1750 Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter")
1751 ),
1752 (Attribute::AcpSearchAttr, Value::from(Attribute::Name)),
1753 (Attribute::AcpCreateClass, EntryClass::Class.to_value()),
1754 (Attribute::AcpCreateAttr, Value::from(Attribute::Name)),
1755 (
1756 Attribute::AcpModifyRemovedAttr,
1757 Value::from(Attribute::Name)
1758 ),
1759 (
1760 Attribute::AcpModifyPresentAttr,
1761 Value::from(Attribute::Name)
1762 ),
1763 (Attribute::AcpModifyClass, EntryClass::Object.to_value())
1764 );
1765
1766 acp_from_entry_ok!(&mut qs_write, e.clone(), AccessControlCreate);
1767 acp_from_entry_ok!(&mut qs_write, e.clone(), AccessControlDelete);
1768 acp_from_entry_ok!(&mut qs_write, e.clone(), AccessControlModify);
1769 acp_from_entry_ok!(&mut qs_write, e, AccessControlSearch);
1770 }
1771
1772 macro_rules! test_acp_search {
1773 (
1774 $se:expr,
1775 $controls:expr,
1776 $entries:expr,
1777 $expect:expr
1778 ) => {{
1779 let ac = AccessControls::default();
1780 let mut acw = ac.write();
1781 acw.update_search($controls).expect("Failed to update");
1782 let acw = acw;
1783
1784 let res = acw
1785 .search_filter_entries(&mut $se, $entries)
1786 .expect("op failed");
1787 debug!("result --> {:?}", res);
1788 debug!("expect --> {:?}", $expect);
1789 assert_eq!(res, $expect);
1791 }};
1792 }
1793
1794 macro_rules! test_acp_search_reduce {
1795 (
1796 $se:expr,
1797 $controls:expr,
1798 $entries:expr,
1799 $expect:expr
1800 ) => {{
1801 let ac = AccessControls::default();
1802 let mut acw = ac.write();
1803 acw.update_search($controls).expect("Failed to update");
1804 let acw = acw;
1805
1806 let res = acw
1808 .search_filter_entries(&mut $se, $entries)
1809 .expect("operation failed");
1810 let reduced = acw
1812 .search_filter_entry_attributes(&mut $se, res)
1813 .expect("operation failed");
1814
1815 let expect_set: Vec<Entry<EntryReduced, EntryCommitted>> =
1817 $expect.into_iter().map(|e| e.into_reduced()).collect();
1818
1819 debug!("expect --> {:?}", expect_set);
1820 debug!("result --> {:?}", reduced);
1821 assert_eq!(reduced, expect_set);
1823 }};
1824 }
1825
1826 #[test]
1827 fn test_access_internal_search() {
1828 let se = SearchEvent::new_internal_invalid(filter!(f_pres(Attribute::Class)));
1830
1831 let expect = vec![E_TEST_ACCOUNT_1.clone()];
1832 let entries = vec![E_TEST_ACCOUNT_1.clone()];
1833
1834 test_acp_search!(
1836 &se,
1837 vec![AccessControlSearch::from_raw(
1838 "test_acp",
1839 Uuid::new_v4(),
1840 UUID_TEST_GROUP_1,
1841 filter_valid!(f_pres(Attribute::NonExist)), Attribute::Name.as_ref(), )],
1844 entries,
1845 expect
1846 );
1847 }
1848
1849 #[test]
1850 fn test_access_enforce_search() {
1851 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
1853 let ev2 = E_TESTPERSON_2.clone().into_sealed_committed();
1854
1855 let r_set = vec![Arc::new(ev1.clone()), Arc::new(ev2)];
1856
1857 let se_a = SearchEvent::new_impersonate_entry(
1858 E_TEST_ACCOUNT_1.clone(),
1859 filter_all!(f_pres(Attribute::Name)),
1860 );
1861 let ex_a = vec![Arc::new(ev1)];
1862
1863 let se_b = SearchEvent::new_impersonate_entry(
1864 E_TEST_ACCOUNT_2.clone(),
1865 filter_all!(f_pres(Attribute::Name)),
1866 );
1867 let ex_b = vec![];
1868
1869 let acp = AccessControlSearch::from_raw(
1870 "test_acp",
1871 Uuid::new_v4(),
1872 UUID_TEST_GROUP_1,
1874 filter_valid!(f_eq(
1876 Attribute::Name,
1877 PartialValue::new_iname("testperson1")
1878 )),
1879 Attribute::Name.as_ref(),
1882 );
1883
1884 test_acp_search!(&se_a, vec![acp.clone()], r_set.clone(), ex_a);
1886
1887 test_acp_search!(&se_b, vec![acp], r_set, ex_b);
1889 }
1890
1891 #[test]
1892 fn test_access_enforce_scope_search() {
1893 sketching::test_init();
1894 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
1896
1897 let ex_some = vec![Arc::new(ev1.clone())];
1898
1899 let r_set = vec![Arc::new(ev1)];
1900
1901 let se_ro = SearchEvent::new_impersonate_identity(
1902 Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()),
1903 filter_all!(f_pres(Attribute::Name)),
1904 );
1905
1906 let se_rw = SearchEvent::new_impersonate_identity(
1907 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
1908 filter_all!(f_pres(Attribute::Name)),
1909 );
1910
1911 let acp = AccessControlSearch::from_raw(
1912 "test_acp",
1913 Uuid::new_v4(),
1914 UUID_TEST_GROUP_1,
1916 filter_valid!(f_eq(
1918 Attribute::Name,
1919 PartialValue::new_iname("testperson1")
1920 )),
1921 Attribute::Name.as_ref(),
1924 );
1925
1926 test_acp_search!(&se_ro, vec![acp.clone()], r_set.clone(), ex_some);
1928
1929 test_acp_search!(&se_rw, vec![acp], r_set, ex_some);
1930 }
1931
1932 #[test]
1933 fn test_access_enforce_scope_search_attrs() {
1934 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
1938 let r_set = vec![Arc::new(ev1)];
1939
1940 let exv1 = E_TESTPERSON_1_REDUCED.clone().into_sealed_committed();
1941
1942 let ex_anon_some = vec![exv1];
1943
1944 let se_anon_ro = SearchEvent::new_impersonate_identity(
1945 Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()),
1946 filter_all!(f_pres(Attribute::Name)),
1947 );
1948
1949 let acp = AccessControlSearch::from_raw(
1950 "test_acp",
1951 Uuid::new_v4(),
1952 UUID_TEST_GROUP_1,
1954 filter_valid!(f_eq(
1956 Attribute::Name,
1957 PartialValue::new_iname("testperson1")
1958 )),
1959 Attribute::Name.as_ref(),
1962 );
1963
1964 test_acp_search_reduce!(&se_anon_ro, vec![acp], r_set, ex_anon_some);
1966 }
1967
1968 pub static E_TESTPERSON_1_REDUCED: LazyLock<EntryInitNew> =
1969 LazyLock::new(|| entry_init_fn([(Attribute::Name, Value::new_iname("testperson1"))]));
1970
1971 #[test]
1972 fn test_access_enforce_search_attrs() {
1973 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
1977 let r_set = vec![Arc::new(ev1)];
1978
1979 let exv1 = E_TESTPERSON_1_REDUCED.clone().into_sealed_committed();
1980 let ex_anon = vec![exv1];
1981
1982 let se_anon = SearchEvent::new_impersonate_entry(
1983 E_TEST_ACCOUNT_1.clone(),
1984 filter_all!(f_eq(
1985 Attribute::Name,
1986 PartialValue::new_iname("testperson1")
1987 )),
1988 );
1989
1990 let acp = AccessControlSearch::from_raw(
1991 "test_acp",
1992 Uuid::new_v4(),
1993 UUID_TEST_GROUP_1,
1995 filter_valid!(f_eq(
1997 Attribute::Name,
1998 PartialValue::new_iname("testperson1")
1999 )),
2000 Attribute::Name.as_ref(),
2003 );
2004
2005 test_acp_search_reduce!(&se_anon, vec![acp], r_set, ex_anon);
2007 }
2008
2009 #[test]
2010 fn test_access_enforce_search_attrs_req() {
2011 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2015
2016 let r_set = vec![Arc::new(ev1)];
2017
2018 let exv1 = E_TESTPERSON_1_REDUCED.clone().into_sealed_committed();
2019 let ex_anon = vec![exv1];
2020
2021 let mut se_anon = SearchEvent::new_impersonate_entry(
2022 E_TEST_ACCOUNT_1.clone(),
2023 filter_all!(f_eq(
2024 Attribute::Name,
2025 PartialValue::new_iname("testperson1")
2026 )),
2027 );
2028 se_anon.attrs = Some(btreeset![Attribute::Name]);
2030
2031 let acp = AccessControlSearch::from_raw(
2032 "test_acp",
2033 Uuid::new_v4(),
2034 UUID_TEST_GROUP_1,
2036 filter_valid!(f_eq(
2038 Attribute::Name,
2039 PartialValue::new_iname("testperson1")
2040 )),
2041 "name uuid",
2044 );
2045
2046 test_acp_search_reduce!(&se_anon, vec![acp], r_set, ex_anon);
2048 }
2049
2050 macro_rules! test_acp_modify {
2051 (
2052 $me:expr,
2053 $controls:expr,
2054 $entries:expr,
2055 $expect:expr
2056 ) => {{
2057 let ac = AccessControls::default();
2058 let mut acw = ac.write();
2059 acw.update_modify($controls).expect("Failed to update");
2060 let acw = acw;
2061
2062 let res = acw
2063 .modify_allow_operation(&mut $me, $entries)
2064 .expect("op failed");
2065
2066 debug!("result --> {:?}", res);
2067 debug!("expect --> {:?}", $expect);
2068 assert_eq!($expect, res);
2070 }};
2071 (
2072 $me:expr,
2073 $controls:expr,
2074 $sync_uuid:expr,
2075 $sync_yield_attr:expr,
2076 $entries:expr,
2077 $expect:expr
2078 ) => {{
2079 let ac = AccessControls::default();
2080 let mut acw = ac.write();
2081 acw.update_modify($controls).expect("Failed to update");
2082 let mut sync_agmt = HashMap::new();
2083 let mut set = BTreeSet::new();
2084 set.insert($sync_yield_attr);
2085 sync_agmt.insert($sync_uuid, set);
2086 acw.update_sync_agreements(sync_agmt);
2087 let acw = acw;
2088
2089 let res = acw
2090 .modify_allow_operation(&mut $me, $entries)
2091 .expect("op failed");
2092
2093 debug!("result --> {:?}", res);
2094 debug!("expect --> {:?}", $expect);
2095 assert_eq!($expect, res);
2097 }};
2098 }
2099
2100 #[test]
2101 fn test_access_enforce_modify() {
2102 sketching::test_init();
2103
2104 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2105 let r_set = vec![Arc::new(ev1)];
2106
2107 let me_pres = ModifyEvent::new_impersonate_entry(
2109 E_TEST_ACCOUNT_1.clone(),
2110 filter_all!(f_eq(
2111 Attribute::Name,
2112 PartialValue::new_iname("testperson1")
2113 )),
2114 modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
2115 );
2116 let me_rem = ModifyEvent::new_impersonate_entry(
2118 E_TEST_ACCOUNT_1.clone(),
2119 filter_all!(f_eq(
2120 Attribute::Name,
2121 PartialValue::new_iname("testperson1")
2122 )),
2123 modlist!([m_remove(Attribute::Name, &PartialValue::new_iname("value"))]),
2124 );
2125 let me_purge = ModifyEvent::new_impersonate_entry(
2127 E_TEST_ACCOUNT_1.clone(),
2128 filter_all!(f_eq(
2129 Attribute::Name,
2130 PartialValue::new_iname("testperson1")
2131 )),
2132 modlist!([m_purge(Attribute::Name)]),
2133 );
2134
2135 let me_set = ModifyEvent::new_impersonate_entry(
2137 E_TEST_ACCOUNT_1.clone(),
2138 filter_all!(f_eq(
2139 Attribute::Name,
2140 PartialValue::new_iname("testperson1")
2141 )),
2142 modlist!([Modify::Set(Attribute::Name, ValueSetIname::new("value"))]),
2143 );
2144
2145 let me_pres_class = ModifyEvent::new_impersonate_entry(
2147 E_TEST_ACCOUNT_1.clone(),
2148 filter_all!(f_eq(
2149 Attribute::Name,
2150 PartialValue::new_iname("testperson1")
2151 )),
2152 modlist!([m_pres(Attribute::Class, &EntryClass::Account.to_value())]),
2153 );
2154 let me_rem_class = ModifyEvent::new_impersonate_entry(
2156 E_TEST_ACCOUNT_1.clone(),
2157 filter_all!(f_eq(
2158 Attribute::Name,
2159 PartialValue::new_iname("testperson1")
2160 )),
2161 modlist!([m_remove(
2162 Attribute::Class,
2163 &EntryClass::Account.to_partialvalue()
2164 )]),
2165 );
2166 let me_purge_class = ModifyEvent::new_impersonate_entry(
2168 E_TEST_ACCOUNT_1.clone(),
2169 filter_all!(f_eq(
2170 Attribute::Name,
2171 PartialValue::new_iname("testperson1")
2172 )),
2173 modlist!([m_purge(Attribute::Class)]),
2174 );
2175
2176 let me_set_class = ModifyEvent::new_impersonate_entry(
2178 E_TEST_ACCOUNT_1.clone(),
2179 filter_all!(f_eq(
2180 Attribute::Name,
2181 PartialValue::new_iname("testperson1")
2182 )),
2183 modlist!([Modify::Set(
2184 Attribute::Class,
2185 ValueSetIutf8::from_iter([EntryClass::Account.into(), EntryClass::Object.into(),])
2186 .unwrap()
2187 )]),
2188 );
2189
2190 let acp_allow = AccessControlModify::from_raw(
2192 "test_modify_allow",
2193 Uuid::new_v4(),
2194 UUID_TEST_GROUP_1,
2196 filter_valid!(f_eq(
2198 Attribute::Name,
2199 PartialValue::new_iname("testperson1")
2200 )),
2201 "name class",
2203 "name class",
2205 EntryClass::Account.into(),
2207 "account person",
2209 );
2210 let acp_deny = AccessControlModify::from_raw(
2212 "test_modify_deny",
2213 Uuid::new_v4(),
2214 UUID_TEST_GROUP_1,
2216 filter_valid!(f_eq(
2218 Attribute::Name,
2219 PartialValue::new_iname("testperson1")
2220 )),
2221 "member class",
2223 "member class",
2225 EntryClass::Group.into(),
2226 EntryClass::Group.into(),
2227 );
2228 let acp_no_class = AccessControlModify::from_raw(
2230 "test_modify_no_class",
2231 Uuid::new_v4(),
2232 UUID_TEST_GROUP_1,
2234 filter_valid!(f_eq(
2236 Attribute::Name,
2237 PartialValue::new_iname("testperson1")
2238 )),
2239 "name class",
2241 "name class",
2243 EntryClass::Group.into(),
2245 EntryClass::Group.into(),
2246 );
2247
2248 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r_set, true);
2250 test_acp_modify!(&me_rem, vec![acp_allow.clone()], &r_set, true);
2252 test_acp_modify!(&me_purge, vec![acp_allow.clone()], &r_set, true);
2254 test_acp_modify!(&me_set, vec![acp_allow.clone()], &r_set, true);
2256
2257 test_acp_modify!(&me_pres, vec![acp_deny.clone()], &r_set, false);
2259 test_acp_modify!(&me_rem, vec![acp_deny.clone()], &r_set, false);
2261 test_acp_modify!(&me_purge, vec![acp_deny.clone()], &r_set, false);
2263 test_acp_modify!(&me_set, vec![acp_deny.clone()], &r_set, false);
2265
2266 test_acp_modify!(&me_pres_class, vec![acp_allow.clone()], &r_set, true);
2268 test_acp_modify!(&me_rem_class, vec![acp_allow.clone()], &r_set, true);
2270 test_acp_modify!(&me_purge_class, vec![acp_allow.clone()], &r_set, false);
2272 test_acp_modify!(&me_set_class, vec![acp_allow], &r_set, true);
2274
2275 test_acp_modify!(&me_pres_class, vec![acp_no_class.clone()], &r_set, false);
2277 test_acp_modify!(&me_pres_class, vec![acp_deny.clone()], &r_set, false);
2279 test_acp_modify!(&me_rem_class, vec![acp_no_class.clone()], &r_set, false);
2281 test_acp_modify!(&me_rem_class, vec![acp_deny.clone()], &r_set, false);
2283
2284 test_acp_modify!(&me_set_class, vec![acp_no_class], &r_set, false);
2286 test_acp_modify!(&me_set_class, vec![acp_deny], &r_set, false);
2288 }
2289
2290 #[test]
2291 fn test_access_enforce_scope_modify() {
2292 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2293 let r_set = vec![Arc::new(ev1)];
2294
2295 let me_pres_ro = ModifyEvent::new_impersonate_identity(
2297 Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()),
2298 filter_all!(f_eq(
2299 Attribute::Name,
2300 PartialValue::new_iname("testperson1")
2301 )),
2302 modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
2303 );
2304
2305 let me_pres_rw = ModifyEvent::new_impersonate_identity(
2307 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
2308 filter_all!(f_eq(
2309 Attribute::Name,
2310 PartialValue::new_iname("testperson1")
2311 )),
2312 modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
2313 );
2314
2315 let acp_allow = AccessControlModify::from_raw(
2316 "test_modify_allow",
2317 Uuid::new_v4(),
2318 UUID_TEST_GROUP_1,
2320 filter_valid!(f_eq(
2322 Attribute::Name,
2323 PartialValue::new_iname("testperson1")
2324 )),
2325 "name class",
2327 "name class",
2329 EntryClass::Account.into(),
2331 EntryClass::Account.into(),
2332 );
2333
2334 test_acp_modify!(&me_pres_ro, vec![acp_allow.clone()], &r_set, false);
2335
2336 test_acp_modify!(&me_pres_rw, vec![acp_allow], &r_set, true);
2337 }
2338
2339 macro_rules! test_acp_create {
2340 (
2341 $ce:expr,
2342 $controls:expr,
2343 $entries:expr,
2344 $expect:expr
2345 ) => {{
2346 let ac = AccessControls::default();
2347 let mut acw = ac.write();
2348 acw.update_create($controls).expect("Failed to update");
2349 let acw = acw;
2350
2351 let res = acw
2352 .create_allow_operation(&mut $ce, $entries)
2353 .expect("op failed");
2354
2355 debug!("result --> {:?}", res);
2356 debug!("expect --> {:?}", $expect);
2357 assert_eq!(res, $expect);
2359 }};
2360 }
2361
2362 #[test]
2363 fn test_access_enforce_create() {
2364 let ev1 = entry_init!(
2365 (Attribute::Class, EntryClass::Account.to_value()),
2366 (Attribute::Name, Value::new_iname("testperson1")),
2367 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2368 );
2369 let r1_set = vec![ev1];
2370
2371 let ev2 = entry_init!(
2372 (Attribute::Class, EntryClass::Account.to_value()),
2373 (Attribute::TestNotAllowed, Value::new_iutf8("notallowed")),
2374 (Attribute::Name, Value::new_iname("testperson1")),
2375 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2376 );
2377
2378 let r2_set = vec![ev2];
2379
2380 let ev3 = entry_init!(
2381 (Attribute::Class, EntryClass::Account.to_value()),
2382 (Attribute::Class, Value::new_iutf8("notallowed")),
2383 (Attribute::Name, Value::new_iname("testperson1")),
2384 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2385 );
2386 let r3_set = vec![ev3];
2387
2388 let ev4 = entry_init!(
2389 (Attribute::Class, EntryClass::Account.to_value()),
2390 (Attribute::Class, EntryClass::Group.to_value()),
2391 (Attribute::Name, Value::new_iname("testperson1")),
2392 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2393 );
2394 let r4_set = vec![ev4];
2395
2396 let ce_admin = CreateEvent::new_impersonate_identity(
2403 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
2404 vec![],
2405 );
2406
2407 let acp = AccessControlCreate::from_raw(
2408 "test_create",
2409 Uuid::new_v4(),
2410 UUID_TEST_GROUP_1,
2412 filter_valid!(f_eq(
2415 Attribute::Name,
2416 PartialValue::new_iname("testperson1")
2417 )),
2418 EntryClass::Account.into(),
2420 "class name uuid",
2422 );
2423
2424 let acp2 = AccessControlCreate::from_raw(
2425 "test_create_2",
2426 Uuid::new_v4(),
2427 UUID_TEST_GROUP_1,
2429 filter_valid!(f_eq(
2431 Attribute::Name,
2432 PartialValue::new_iname("testperson1")
2433 )),
2434 EntryClass::Group.into(),
2436 "class name uuid",
2438 );
2439
2440 test_acp_create!(&ce_admin, vec![acp.clone()], &r1_set, true);
2442 test_acp_create!(&ce_admin, vec![acp.clone()], &r2_set, false);
2444 test_acp_create!(&ce_admin, vec![acp.clone()], &r3_set, false);
2446 test_acp_create!(&ce_admin, vec![acp, acp2], &r4_set, false);
2448 }
2449
2450 #[test]
2451 fn test_access_enforce_scope_create() {
2452 let ev1 = entry_init!(
2453 (Attribute::Class, EntryClass::Account.to_value()),
2454 (Attribute::Name, Value::new_iname("testperson1")),
2455 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2456 );
2457 let r1_set = vec![ev1];
2458
2459 let admin = E_TEST_ACCOUNT_1.clone();
2460
2461 let ce_admin_ro = CreateEvent::new_impersonate_identity(
2462 Identity::from_impersonate_entry_readonly(admin.clone()),
2463 vec![],
2464 );
2465
2466 let ce_admin_rw = CreateEvent::new_impersonate_identity(
2467 Identity::from_impersonate_entry_readwrite(admin),
2468 vec![],
2469 );
2470
2471 let acp = AccessControlCreate::from_raw(
2472 "test_create",
2473 Uuid::new_v4(),
2474 UUID_TEST_GROUP_1,
2476 filter_valid!(f_eq(
2479 Attribute::Name,
2480 PartialValue::new_iname("testperson1")
2481 )),
2482 EntryClass::Account.into(),
2484 "class name uuid",
2486 );
2487
2488 test_acp_create!(&ce_admin_ro, vec![acp.clone()], &r1_set, false);
2489
2490 test_acp_create!(&ce_admin_rw, vec![acp], &r1_set, true);
2491 }
2492
2493 macro_rules! test_acp_delete {
2494 (
2495 $de:expr,
2496 $controls:expr,
2497 $entries:expr,
2498 $expect:expr
2499 ) => {{
2500 let ac = AccessControls::default();
2501 let mut acw = ac.write();
2502 acw.update_delete($controls).expect("Failed to update");
2503 let acw = acw;
2504
2505 let res = acw
2506 .delete_allow_operation($de, $entries)
2507 .expect("op failed");
2508
2509 debug!("result --> {:?}", res);
2510 debug!("expect --> {:?}", $expect);
2511 assert_eq!(res, $expect);
2513 }};
2514 }
2515
2516 #[test]
2517 fn test_access_enforce_delete() {
2518 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2519 let r_set = vec![Arc::new(ev1)];
2520
2521 let de_admin = DeleteEvent::new_impersonate_entry(
2522 E_TEST_ACCOUNT_1.clone(),
2523 filter_all!(f_eq(
2524 Attribute::Name,
2525 PartialValue::new_iname("testperson1")
2526 )),
2527 );
2528
2529 let de_anon = DeleteEvent::new_impersonate_entry(
2530 E_TEST_ACCOUNT_2.clone(),
2531 filter_all!(f_eq(
2532 Attribute::Name,
2533 PartialValue::new_iname("testperson1")
2534 )),
2535 );
2536
2537 let acp = AccessControlDelete::from_raw(
2538 "test_delete",
2539 Uuid::new_v4(),
2540 UUID_TEST_GROUP_1,
2542 filter_valid!(f_eq(
2544 Attribute::Name,
2545 PartialValue::new_iname("testperson1")
2546 )),
2547 );
2548
2549 test_acp_delete!(&de_admin, vec![acp.clone()], &r_set, true);
2551 test_acp_delete!(&de_anon, vec![acp], &r_set, false);
2553 }
2554
2555 #[test]
2556 fn test_access_enforce_scope_delete() {
2557 sketching::test_init();
2558 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2559 let r_set = vec![Arc::new(ev1)];
2560
2561 let admin = E_TEST_ACCOUNT_1.clone();
2562
2563 let de_admin_ro = DeleteEvent::new_impersonate_identity(
2564 Identity::from_impersonate_entry_readonly(admin.clone()),
2565 filter_all!(f_eq(
2566 Attribute::Name,
2567 PartialValue::new_iname("testperson1")
2568 )),
2569 );
2570
2571 let de_admin_rw = DeleteEvent::new_impersonate_identity(
2572 Identity::from_impersonate_entry_readwrite(admin),
2573 filter_all!(f_eq(
2574 Attribute::Name,
2575 PartialValue::new_iname("testperson1")
2576 )),
2577 );
2578
2579 let acp = AccessControlDelete::from_raw(
2580 "test_delete",
2581 Uuid::new_v4(),
2582 UUID_TEST_GROUP_1,
2584 filter_valid!(f_eq(
2586 Attribute::Name,
2587 PartialValue::new_iname("testperson1")
2588 )),
2589 );
2590
2591 test_acp_delete!(&de_admin_ro, vec![acp.clone()], &r_set, false);
2592
2593 test_acp_delete!(&de_admin_rw, vec![acp], &r_set, true);
2594 }
2595
2596 macro_rules! test_acp_effective_permissions {
2597 (
2598 $ident:expr,
2599 $attrs:expr,
2600 $search_controls:expr,
2601 $modify_controls:expr,
2602 $entries:expr,
2603 $expect:expr
2604 ) => {{
2605 let ac = AccessControls::default();
2606 let mut acw = ac.write();
2607 acw.update_search($search_controls)
2608 .expect("Failed to update");
2609 acw.update_modify($modify_controls)
2610 .expect("Failed to update");
2611 let acw = acw;
2612
2613 let res = acw
2614 .effective_permission_check($ident, $attrs, $entries)
2615 .expect("Failed to apply effective_permission_check");
2616
2617 debug!("result --> {:?}", res);
2618 debug!("expect --> {:?}", $expect);
2619 assert_eq!(res, $expect);
2621 }};
2622 }
2623
2624 #[test]
2625 fn test_access_effective_permission_check_1() {
2626 sketching::test_init();
2627
2628 let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone());
2629
2630 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2631
2632 let r_set = vec![Arc::new(ev1)];
2633
2634 test_acp_effective_permissions!(
2635 &admin,
2636 None,
2637 vec![AccessControlSearch::from_raw(
2638 "test_acp",
2639 Uuid::new_v4(),
2640 UUID_TEST_GROUP_1,
2642 filter_valid!(f_eq(
2644 Attribute::Name,
2645 PartialValue::new_iname("testperson1")
2646 )),
2647 Attribute::Name.as_ref(),
2649 )],
2650 vec![],
2651 &r_set,
2652 vec![AccessEffectivePermission {
2653 ident: UUID_TEST_ACCOUNT_1,
2654 delete: false,
2655 target: uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
2656 search: Access::Allow(btreeset![Attribute::Name]),
2657 modify_pres: Access::Allow(BTreeSet::new()),
2658 modify_rem: Access::Allow(BTreeSet::new()),
2659 modify_pres_class: AccessClass::Allow(BTreeSet::new()),
2660 modify_rem_class: AccessClass::Allow(BTreeSet::new()),
2661 }]
2662 )
2663 }
2664
2665 #[test]
2666 fn test_access_effective_permission_check_2() {
2667 sketching::test_init();
2668
2669 let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone());
2670
2671 let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
2672
2673 let r_set = vec![Arc::new(ev1)];
2674
2675 test_acp_effective_permissions!(
2676 &admin,
2677 None,
2678 vec![],
2679 vec![AccessControlModify::from_raw(
2680 "test_acp",
2681 Uuid::new_v4(),
2682 UUID_TEST_GROUP_1,
2684 filter_valid!(f_eq(
2686 Attribute::Name,
2687 PartialValue::new_iname("testperson1")
2688 )),
2689 Attribute::Name.as_ref(),
2691 Attribute::Name.as_ref(),
2692 EntryClass::Object.into(),
2693 EntryClass::Object.into(),
2694 )],
2695 &r_set,
2696 vec![AccessEffectivePermission {
2697 ident: UUID_TEST_ACCOUNT_1,
2698 delete: false,
2699 target: uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
2700 search: Access::Allow(BTreeSet::new()),
2701 modify_pres: Access::Allow(btreeset![Attribute::Name]),
2702 modify_rem: Access::Allow(btreeset![Attribute::Name]),
2703 modify_pres_class: AccessClass::Allow(btreeset![EntryClass::Object.into()]),
2704 modify_rem_class: AccessClass::Allow(btreeset![EntryClass::Object.into()]),
2705 }]
2706 )
2707 }
2708
2709 #[test]
2710 fn test_access_sync_authority_create() {
2711 sketching::test_init();
2712
2713 let ce_admin = CreateEvent::new_impersonate_identity(
2714 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
2715 vec![],
2716 );
2717
2718 let ev1 = entry_init!(
2720 (Attribute::Class, EntryClass::Account.to_value()),
2721 (Attribute::Name, Value::new_iname("testperson1")),
2722 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2723 );
2724 let r1_set = vec![ev1];
2725
2726 let ev2 = entry_init!(
2727 (Attribute::Class, EntryClass::Account.to_value()),
2728 (Attribute::Class, EntryClass::SyncObject.to_value()),
2729 (Attribute::Name, Value::new_iname("testperson1")),
2730 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2731 );
2732 let r2_set = vec![ev2];
2733
2734 let acp = AccessControlCreate::from_raw(
2735 "test_create",
2736 Uuid::new_v4(),
2737 UUID_TEST_GROUP_1,
2739 filter_valid!(f_eq(
2742 Attribute::Name,
2743 PartialValue::new_iname("testperson1")
2744 )),
2745 "account sync_object",
2747 "class name uuid",
2749 );
2750
2751 test_acp_create!(&ce_admin, vec![acp.clone()], &r1_set, true);
2753 test_acp_create!(&ce_admin, vec![acp], &r2_set, false);
2755 }
2756
2757 #[test]
2758 fn test_access_sync_authority_delete() {
2759 sketching::test_init();
2760
2761 let ev1 = entry_init!(
2762 (Attribute::Class, EntryClass::Account.to_value()),
2763 (Attribute::Name, Value::new_iname("testperson1")),
2764 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2765 )
2766 .into_sealed_committed();
2767 let r1_set = vec![Arc::new(ev1)];
2768
2769 let ev2 = entry_init!(
2770 (Attribute::Class, EntryClass::Account.to_value()),
2771 (Attribute::Class, EntryClass::SyncObject.to_value()),
2772 (Attribute::Name, Value::new_iname("testperson1")),
2773 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2774 )
2775 .into_sealed_committed();
2776 let r2_set = vec![Arc::new(ev2)];
2777
2778 let de_admin = DeleteEvent::new_impersonate_entry(
2779 E_TEST_ACCOUNT_1.clone(),
2780 filter_all!(f_eq(
2781 Attribute::Name,
2782 PartialValue::new_iname("testperson1")
2783 )),
2784 );
2785
2786 let acp = AccessControlDelete::from_raw(
2787 "test_delete",
2788 Uuid::new_v4(),
2789 UUID_TEST_GROUP_1,
2791 filter_valid!(f_eq(
2793 Attribute::Name,
2794 PartialValue::new_iname("testperson1")
2795 )),
2796 );
2797
2798 test_acp_delete!(&de_admin, vec![acp.clone()], &r1_set, true);
2800 test_acp_delete!(&de_admin, vec![acp], &r2_set, false);
2802 }
2803
2804 #[test]
2805 fn test_access_sync_authority_modify() {
2806 sketching::test_init();
2807
2808 let ev1 = entry_init!(
2809 (Attribute::Class, EntryClass::Account.to_value()),
2810 (Attribute::Name, Value::new_iname("testperson1")),
2811 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2812 )
2813 .into_sealed_committed();
2814 let r1_set = vec![Arc::new(ev1)];
2815
2816 let sync_uuid = Uuid::new_v4();
2817 let ev2 = entry_init!(
2818 (Attribute::Class, EntryClass::Account.to_value()),
2819 (Attribute::Class, EntryClass::SyncObject.to_value()),
2820 (Attribute::SyncParentUuid, Value::Refer(sync_uuid)),
2821 (Attribute::Name, Value::new_iname("testperson1")),
2822 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
2823 )
2824 .into_sealed_committed();
2825 let r2_set = vec![Arc::new(ev2)];
2826
2827 let acp_allow = AccessControlModify::from_raw(
2829 "test_modify_allow",
2830 Uuid::new_v4(),
2831 UUID_TEST_GROUP_1,
2833 filter_valid!(f_eq(
2835 Attribute::Name,
2836 PartialValue::new_iname("testperson1")
2837 )),
2838 &format!("{} {}", Attribute::UserAuthTokenSession, Attribute::Name),
2840 &format!("{} {}", Attribute::UserAuthTokenSession, Attribute::Name),
2842 EntryClass::Account.into(),
2844 EntryClass::Account.into(),
2845 );
2846
2847 let me_pres = ModifyEvent::new_impersonate_entry(
2851 E_TEST_ACCOUNT_1.clone(),
2852 filter_all!(f_eq(
2853 Attribute::Name,
2854 PartialValue::new_iname("testperson1")
2855 )),
2856 modlist!([m_pres(
2857 Attribute::UserAuthTokenSession,
2858 &Value::new_iname("value")
2859 )]),
2860 );
2861 let me_rem = ModifyEvent::new_impersonate_entry(
2863 E_TEST_ACCOUNT_1.clone(),
2864 filter_all!(f_eq(
2865 Attribute::Name,
2866 PartialValue::new_iname("testperson1")
2867 )),
2868 modlist!([m_remove(
2869 Attribute::UserAuthTokenSession,
2870 &PartialValue::new_iname("value")
2871 )]),
2872 );
2873 let me_purge = ModifyEvent::new_impersonate_entry(
2875 E_TEST_ACCOUNT_1.clone(),
2876 filter_all!(f_eq(
2877 Attribute::Name,
2878 PartialValue::new_iname("testperson1")
2879 )),
2880 modlist!([m_purge(Attribute::UserAuthTokenSession)]),
2881 );
2882
2883 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r1_set, true);
2885 test_acp_modify!(&me_rem, vec![acp_allow.clone()], &r1_set, true);
2887 test_acp_modify!(&me_purge, vec![acp_allow.clone()], &r1_set, true);
2889
2890 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r2_set, true);
2892 test_acp_modify!(&me_rem, vec![acp_allow.clone()], &r2_set, true);
2894 test_acp_modify!(&me_purge, vec![acp_allow.clone()], &r2_set, true);
2896
2897 let me_pres = ModifyEvent::new_impersonate_entry(
2899 E_TEST_ACCOUNT_1.clone(),
2900 filter_all!(f_eq(
2901 Attribute::Name,
2902 PartialValue::new_iname("testperson1")
2903 )),
2904 modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
2905 );
2906 let me_rem = ModifyEvent::new_impersonate_entry(
2908 E_TEST_ACCOUNT_1.clone(),
2909 filter_all!(f_eq(
2910 Attribute::Name,
2911 PartialValue::new_iname("testperson1")
2912 )),
2913 modlist!([m_remove(Attribute::Name, &PartialValue::new_iname("value"))]),
2914 );
2915 let me_purge = ModifyEvent::new_impersonate_entry(
2917 E_TEST_ACCOUNT_1.clone(),
2918 filter_all!(f_eq(
2919 Attribute::Name,
2920 PartialValue::new_iname("testperson1")
2921 )),
2922 modlist!([m_purge(Attribute::Name)]),
2923 );
2924
2925 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r2_set, false);
2927 test_acp_modify!(&me_rem, vec![acp_allow.clone()], &r2_set, false);
2929 test_acp_modify!(&me_purge, vec![acp_allow.clone()], &r2_set, false);
2931
2932 test_acp_modify!(
2937 &me_pres,
2938 vec![acp_allow.clone()],
2939 sync_uuid,
2940 Attribute::Name,
2941 &r2_set,
2942 true
2943 );
2944 test_acp_modify!(
2946 &me_rem,
2947 vec![acp_allow.clone()],
2948 sync_uuid,
2949 Attribute::Name,
2950 &r2_set,
2951 true
2952 );
2953 test_acp_modify!(
2955 &me_purge,
2956 vec![acp_allow],
2957 sync_uuid,
2958 Attribute::Name,
2959 &r2_set,
2960 true
2961 );
2962 }
2963
2964 #[test]
2965 fn test_access_oauth2_dyn_search() {
2966 sketching::test_init();
2967 let rs_uuid = Uuid::new_v4();
2970 let ev1 = entry_init!(
2971 (Attribute::Class, EntryClass::Object.to_value()),
2972 (
2973 Attribute::Class,
2974 EntryClass::OAuth2ResourceServer.to_value()
2975 ),
2976 (
2977 Attribute::Class,
2978 EntryClass::OAuth2ResourceServerBasic.to_value()
2979 ),
2980 (Attribute::Uuid, Value::Uuid(rs_uuid)),
2981 (Attribute::Name, Value::new_iname("test_resource_server")),
2982 (
2983 Attribute::DisplayName,
2984 Value::new_utf8s("test_resource_server")
2985 ),
2986 (
2987 Attribute::OAuth2RsOriginLanding,
2988 Value::new_url_s("https://demo.example.com").unwrap()
2989 ),
2990 (
2991 Attribute::OAuth2RsOrigin,
2992 Value::new_url_s("app://hidden").unwrap()
2993 ),
2994 (
2995 Attribute::OAuth2RsScopeMap,
2996 Value::new_oauthscopemap(UUID_TEST_GROUP_1, btreeset!["groups".to_string()])
2997 .expect("invalid oauthscope")
2998 ),
2999 (
3000 Attribute::OAuth2RsSupScopeMap,
3001 Value::new_oauthscopemap(UUID_TEST_GROUP_1, btreeset!["supplement".to_string()])
3002 .expect("invalid oauthscope")
3003 ),
3004 (
3005 Attribute::OAuth2AllowInsecureClientDisablePkce,
3006 Value::new_bool(true)
3007 ),
3008 (
3009 Attribute::OAuth2JwtLegacyCryptoEnable,
3010 Value::new_bool(false)
3011 ),
3012 (Attribute::OAuth2PreferShortUsername, Value::new_bool(false))
3013 )
3014 .into_sealed_committed();
3015
3016 let ev1_reduced = entry_init!(
3017 (Attribute::Class, EntryClass::Object.to_value()),
3018 (
3019 Attribute::Class,
3020 EntryClass::OAuth2ResourceServer.to_value()
3021 ),
3022 (
3023 Attribute::Class,
3024 EntryClass::OAuth2ResourceServerBasic.to_value()
3025 ),
3026 (Attribute::Uuid, Value::Uuid(rs_uuid)),
3027 (Attribute::Name, Value::new_iname("test_resource_server")),
3028 (
3029 Attribute::DisplayName,
3030 Value::new_utf8s("test_resource_server")
3031 ),
3032 (
3033 Attribute::OAuth2RsOriginLanding,
3034 Value::new_url_s("https://demo.example.com").unwrap()
3035 )
3036 )
3037 .into_sealed_committed();
3038
3039 let ev2 = entry_init!(
3040 (Attribute::Class, EntryClass::Object.to_value()),
3041 (
3042 Attribute::Class,
3043 EntryClass::OAuth2ResourceServer.to_value()
3044 ),
3045 (
3046 Attribute::Class,
3047 EntryClass::OAuth2ResourceServerBasic.to_value()
3048 ),
3049 (Attribute::Uuid, Value::Uuid(Uuid::new_v4())),
3050 (Attribute::Name, Value::new_iname("second_resource_server")),
3051 (
3052 Attribute::DisplayName,
3053 Value::new_utf8s("second_resource_server")
3054 ),
3055 (
3056 Attribute::OAuth2RsOriginLanding,
3057 Value::new_url_s("https://noaccess.example.com").unwrap()
3058 ),
3059 (
3060 Attribute::OAuth2RsOrigin,
3061 Value::new_url_s("app://hidden").unwrap()
3062 ),
3063 (
3064 Attribute::OAuth2RsScopeMap,
3065 Value::new_oauthscopemap(UUID_SYSTEM_ADMINS, btreeset!["groups".to_string()])
3066 .expect("invalid oauthscope")
3067 ),
3068 (
3069 Attribute::OAuth2RsSupScopeMap,
3070 Value::new_oauthscopemap(
3071 UUID_TEST_GROUP_1,
3073 btreeset!["supplement".to_string()]
3074 )
3075 .expect("invalid oauthscope")
3076 ),
3077 (
3078 Attribute::OAuth2AllowInsecureClientDisablePkce,
3079 Value::new_bool(true)
3080 ),
3081 (
3082 Attribute::OAuth2JwtLegacyCryptoEnable,
3083 Value::new_bool(false)
3084 ),
3085 (Attribute::OAuth2PreferShortUsername, Value::new_bool(false))
3086 )
3087 .into_sealed_committed();
3088
3089 let r_set = vec![Arc::new(ev1.clone()), Arc::new(ev2)];
3090
3091 let se_a = SearchEvent::new_impersonate_entry(
3093 E_TEST_ACCOUNT_1.clone(),
3094 filter_all!(f_pres(Attribute::Name)),
3095 );
3096 let ex_a = vec![Arc::new(ev1)];
3097 let ex_a_reduced = vec![ev1_reduced];
3098
3099 test_acp_search!(&se_a, vec![], r_set.clone(), ex_a);
3100 test_acp_search_reduce!(&se_a, vec![], r_set.clone(), ex_a_reduced);
3101
3102 let anon: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS.clone().into();
3104 let mut anon = anon.into_invalid_new();
3105 anon.set_ava_set(&Attribute::MemberOf, ValueSetRefer::new(UUID_TEST_GROUP_1));
3106
3107 let anon = Arc::new(anon.into_sealed_committed());
3108
3109 let se_anon =
3110 SearchEvent::new_impersonate_entry(anon, filter_all!(f_pres(Attribute::Name)));
3111 let ex_anon = vec![];
3112 test_acp_search!(&se_anon, vec![], r_set.clone(), ex_anon);
3113
3114 let se_b = SearchEvent::new_impersonate_entry(
3116 E_TEST_ACCOUNT_2.clone(),
3117 filter_all!(f_pres(Attribute::Name)),
3118 );
3119 let ex_b = vec![];
3120
3121 test_acp_search!(&se_b, vec![], r_set, ex_b);
3122 }
3123
3124 #[test]
3125 fn test_access_sync_account_dyn_search() {
3126 sketching::test_init();
3127 let sync_uuid = Uuid::new_v4();
3132 let portal_url = Url::parse("https://localhost/portal").unwrap();
3133
3134 let ev1 = entry_init!(
3135 (Attribute::Class, EntryClass::Object.to_value()),
3136 (Attribute::Class, EntryClass::SyncAccount.to_value()),
3137 (Attribute::Uuid, Value::Uuid(sync_uuid)),
3138 (Attribute::Name, Value::new_iname("test_sync_account")),
3139 (
3140 Attribute::SyncCredentialPortal,
3141 Value::Url(portal_url.clone())
3142 )
3143 )
3144 .into_sealed_committed();
3145
3146 let ev1_reduced = entry_init!(
3147 (Attribute::Class, EntryClass::Object.to_value()),
3148 (Attribute::Class, EntryClass::SyncAccount.to_value()),
3149 (Attribute::Uuid, Value::Uuid(sync_uuid)),
3150 (
3151 Attribute::SyncCredentialPortal,
3152 Value::Url(portal_url.clone())
3153 )
3154 )
3155 .into_sealed_committed();
3156
3157 let ev2 = entry_init!(
3158 (Attribute::Class, EntryClass::Object.to_value()),
3159 (Attribute::Class, EntryClass::SyncAccount.to_value()),
3160 (Attribute::Uuid, Value::Uuid(Uuid::new_v4())),
3161 (Attribute::Name, Value::new_iname("test_sync_account")),
3162 (
3163 Attribute::SyncCredentialPortal,
3164 Value::Url(portal_url.clone())
3165 )
3166 )
3167 .into_sealed_committed();
3168
3169 let sync_test_account: Arc<EntrySealedCommitted> = Arc::new(
3170 entry_init!(
3171 (Attribute::Class, EntryClass::Object.to_value()),
3172 (Attribute::Class, EntryClass::Account.to_value()),
3173 (Attribute::Class, EntryClass::SyncObject.to_value()),
3174 (Attribute::Name, Value::new_iname("test_account_1")),
3175 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3176 (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP_1)),
3177 (Attribute::SyncParentUuid, Value::Refer(sync_uuid))
3178 )
3179 .into_sealed_committed(),
3180 );
3181
3182 let r_set = vec![Arc::new(ev1.clone()), Arc::new(ev2)];
3184
3185 let se_a = SearchEvent::new_impersonate_entry(
3186 sync_test_account,
3187 filter_all!(f_pres(Attribute::SyncCredentialPortal)),
3188 );
3189 let ex_a = vec![Arc::new(ev1)];
3190 let ex_a_reduced = vec![ev1_reduced];
3191
3192 test_acp_search!(&se_a, vec![], r_set.clone(), ex_a);
3193 test_acp_search_reduce!(&se_a, vec![], r_set.clone(), ex_a_reduced);
3194
3195 let se_b = SearchEvent::new_impersonate_entry(
3197 E_TEST_ACCOUNT_2.clone(),
3198 filter_all!(f_pres(Attribute::SyncCredentialPortal)),
3199 );
3200 let ex_b = vec![];
3201
3202 test_acp_search!(&se_b, vec![], r_set, ex_b);
3203 }
3204
3205 #[test]
3206 fn test_access_entry_managed_by_search() {
3207 sketching::test_init();
3208
3209 let test_entry = Arc::new(
3210 entry_init!(
3211 (Attribute::Class, EntryClass::Object.to_value()),
3212 (Attribute::Name, Value::new_iname("testperson1")),
3213 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3214 (Attribute::EntryManagedBy, Value::Refer(UUID_TEST_GROUP_1))
3215 )
3216 .into_sealed_committed(),
3217 );
3218
3219 let data_set = vec![test_entry.clone()];
3220
3221 let se_a = SearchEvent::new_impersonate_entry(
3222 E_TEST_ACCOUNT_1.clone(),
3223 filter_all!(f_pres(Attribute::Name)),
3224 );
3225 let expect_a = vec![test_entry];
3226
3227 let se_b = SearchEvent::new_impersonate_entry(
3228 E_TEST_ACCOUNT_2.clone(),
3229 filter_all!(f_pres(Attribute::Name)),
3230 );
3231 let expect_b = vec![];
3232
3233 let acp = AccessControlSearch::from_managed_by(
3234 "test_acp",
3235 Uuid::new_v4(),
3236 AccessControlTarget::Scope(filter_valid!(f_eq(
3238 Attribute::Name,
3239 PartialValue::new_iname("testperson1")
3240 ))),
3241 Attribute::Name.as_ref(),
3244 );
3245
3246 test_acp_search!(&se_a, vec![acp.clone()], data_set.clone(), expect_a);
3248
3249 test_acp_search!(&se_b, vec![acp], data_set, expect_b);
3251 }
3252
3253 #[test]
3254 fn test_access_entry_managed_by_create() {
3255 sketching::test_init();
3256
3257 let test_entry = entry_init!(
3258 (Attribute::Class, EntryClass::Object.to_value()),
3259 (Attribute::Name, Value::new_iname("testperson1")),
3260 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3261 (Attribute::EntryManagedBy, Value::Refer(UUID_TEST_GROUP_1))
3262 );
3263
3264 let data_set = vec![test_entry];
3265
3266 let ce = CreateEvent::new_impersonate_identity(
3267 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
3268 vec![],
3269 );
3270
3271 let acp = AccessControlCreate::from_managed_by(
3272 "test_create",
3273 Uuid::new_v4(),
3274 AccessControlTarget::Scope(filter_valid!(f_eq(
3275 Attribute::Name,
3276 PartialValue::new_iname("testperson1")
3277 ))),
3278 EntryClass::Account.into(),
3280 "class name uuid",
3282 );
3283
3284 test_acp_create!(&ce, vec![acp.clone()], &data_set, false);
3287 }
3288
3289 #[test]
3290 fn test_access_entry_managed_by_modify() {
3291 let test_entry = Arc::new(
3292 entry_init!(
3293 (Attribute::Class, EntryClass::Object.to_value()),
3294 (Attribute::Name, Value::new_iname("testperson1")),
3295 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3296 (Attribute::EntryManagedBy, Value::Refer(UUID_TEST_GROUP_1))
3297 )
3298 .into_sealed_committed(),
3299 );
3300
3301 let data_set = vec![test_entry];
3302
3303 let me_pres = ModifyEvent::new_impersonate_entry(
3305 E_TEST_ACCOUNT_1.clone(),
3306 filter_all!(f_eq(
3307 Attribute::Name,
3308 PartialValue::new_iname("testperson1")
3309 )),
3310 modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
3311 );
3312 let me_rem = ModifyEvent::new_impersonate_entry(
3314 E_TEST_ACCOUNT_1.clone(),
3315 filter_all!(f_eq(
3316 Attribute::Name,
3317 PartialValue::new_iname("testperson1")
3318 )),
3319 modlist!([m_remove(Attribute::Name, &PartialValue::new_iname("value"))]),
3320 );
3321 let me_purge = ModifyEvent::new_impersonate_entry(
3323 E_TEST_ACCOUNT_1.clone(),
3324 filter_all!(f_eq(
3325 Attribute::Name,
3326 PartialValue::new_iname("testperson1")
3327 )),
3328 modlist!([m_purge(Attribute::Name)]),
3329 );
3330
3331 let acp_allow = AccessControlModify::from_managed_by(
3332 "test_modify_allow",
3333 Uuid::new_v4(),
3334 AccessControlTarget::Scope(filter_valid!(f_eq(
3336 Attribute::Name,
3337 PartialValue::new_iname("testperson1")
3338 ))),
3339 "name class",
3341 "name class",
3343 EntryClass::Account.into(),
3345 EntryClass::Account.into(),
3346 );
3347
3348 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &data_set, true);
3350 test_acp_modify!(&me_rem, vec![acp_allow.clone()], &data_set, true);
3352 test_acp_modify!(&me_purge, vec![acp_allow.clone()], &data_set, true);
3354 }
3355
3356 #[test]
3357 fn test_access_entry_managed_by_delete() {
3358 let test_entry = Arc::new(
3359 entry_init!(
3360 (Attribute::Class, EntryClass::Object.to_value()),
3361 (Attribute::Name, Value::new_iname("testperson1")),
3362 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3363 (Attribute::EntryManagedBy, Value::Refer(UUID_TEST_GROUP_1))
3364 )
3365 .into_sealed_committed(),
3366 );
3367
3368 let data_set = vec![test_entry];
3369
3370 let de_a = DeleteEvent::new_impersonate_entry(
3371 E_TEST_ACCOUNT_1.clone(),
3372 filter_all!(f_eq(
3373 Attribute::Name,
3374 PartialValue::new_iname("testperson1")
3375 )),
3376 );
3377
3378 let de_b = DeleteEvent::new_impersonate_entry(
3379 E_TEST_ACCOUNT_2.clone(),
3380 filter_all!(f_eq(
3381 Attribute::Name,
3382 PartialValue::new_iname("testperson1")
3383 )),
3384 );
3385
3386 let acp = AccessControlDelete::from_managed_by(
3387 "test_delete",
3388 Uuid::new_v4(),
3389 AccessControlTarget::Scope(filter_valid!(f_eq(
3391 Attribute::Name,
3392 PartialValue::new_iname("testperson1")
3393 ))),
3394 );
3395
3396 test_acp_delete!(&de_a, vec![acp.clone()], &data_set, true);
3398 test_acp_delete!(&de_b, vec![acp], &data_set, false);
3400 }
3401
3402 #[test]
3403 fn test_access_delete_protect_system_ranges() {
3404 let ev1: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS.clone().into();
3405 let ev1 = ev1.into_sealed_committed();
3406 let r_set = vec![Arc::new(ev1)];
3407
3408 let de_account = DeleteEvent::new_impersonate_entry(
3409 E_TEST_ACCOUNT_1.clone(),
3410 filter_all!(f_eq(
3411 Attribute::Name,
3412 PartialValue::new_iname("testperson1")
3413 )),
3414 );
3415
3416 let acp = AccessControlDelete::from_raw(
3417 "test_delete",
3418 Uuid::new_v4(),
3419 UUID_TEST_GROUP_1,
3420 filter_valid!(f_eq(Attribute::Name, PartialValue::new_iname("anonymous"))),
3422 );
3423
3424 test_acp_delete!(&de_account, vec![acp], &r_set, false);
3426 }
3427
3428 #[test]
3429 fn test_access_sync_memberof_implies_directmemberof() {
3430 sketching::test_init();
3431
3432 let ev1 = entry_init!(
3433 (Attribute::Class, EntryClass::Object.to_value()),
3434 (Attribute::Name, Value::new_iname("test_account_1")),
3435 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1)),
3436 (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP_1)),
3437 (Attribute::DirectMemberOf, Value::Refer(UUID_TEST_GROUP_1))
3438 )
3439 .into_sealed_committed();
3440 let r_set = vec![Arc::new(ev1)];
3441
3442 let exv1 = entry_init!(
3443 (Attribute::Name, Value::new_iname("test_account_1")),
3444 (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP_1)),
3445 (Attribute::DirectMemberOf, Value::Refer(UUID_TEST_GROUP_1))
3446 )
3447 .into_sealed_committed();
3448
3449 let ex_anon_some = vec![exv1];
3450
3451 let se_anon_ro = SearchEvent::new_impersonate_identity(
3452 Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()),
3453 filter_all!(f_pres(Attribute::Name)),
3454 );
3455
3456 let acp = AccessControlSearch::from_raw(
3457 "test_acp",
3458 Uuid::new_v4(),
3459 UUID_TEST_GROUP_1,
3461 filter_valid!(f_eq(
3463 Attribute::Uuid,
3464 PartialValue::Uuid(UUID_TEST_ACCOUNT_1)
3465 )),
3466 format!("{} {}", Attribute::Name, Attribute::MemberOf).as_str(),
3469 );
3470
3471 test_acp_search_reduce!(&se_anon_ro, vec![acp], r_set, ex_anon_some);
3473 }
3474
3475 #[test]
3476 fn test_access_protected_deny_create() {
3477 sketching::test_init();
3478
3479 let ev1 = entry_init!(
3480 (Attribute::Class, EntryClass::Account.to_value()),
3481 (Attribute::Name, Value::new_iname("testperson1")),
3482 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3483 );
3484 let r1_set = vec![ev1];
3485
3486 let ev2 = entry_init!(
3487 (Attribute::Class, EntryClass::Account.to_value()),
3488 (Attribute::Class, EntryClass::System.to_value()),
3489 (Attribute::Name, Value::new_iname("testperson1")),
3490 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3491 );
3492
3493 let r2_set = vec![ev2];
3494
3495 let ce_admin = CreateEvent::new_impersonate_identity(
3496 Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()),
3497 vec![],
3498 );
3499
3500 let acp = AccessControlCreate::from_raw(
3501 "test_create",
3502 Uuid::new_v4(),
3503 UUID_TEST_GROUP_1,
3505 filter_valid!(f_eq(
3508 Attribute::Name,
3509 PartialValue::new_iname("testperson1")
3510 )),
3511 EntryClass::Account.into(),
3513 "class name uuid",
3515 );
3516
3517 test_acp_create!(&ce_admin, vec![acp.clone()], &r1_set, true);
3519 test_acp_create!(&ce_admin, vec![acp.clone()], &r2_set, false);
3521 }
3522
3523 #[test]
3524 fn test_access_protected_deny_delete() {
3525 sketching::test_init();
3526
3527 let ev1 = entry_init!(
3528 (Attribute::Class, EntryClass::Account.to_value()),
3529 (Attribute::Name, Value::new_iname("testperson1")),
3530 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3531 )
3532 .into_sealed_committed();
3533 let r1_set = vec![Arc::new(ev1)];
3534
3535 let ev2 = entry_init!(
3536 (Attribute::Class, EntryClass::Account.to_value()),
3537 (Attribute::Class, EntryClass::System.to_value()),
3538 (Attribute::Name, Value::new_iname("testperson1")),
3539 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3540 )
3541 .into_sealed_committed();
3542
3543 let r2_set = vec![Arc::new(ev2)];
3544
3545 let de = DeleteEvent::new_impersonate_entry(
3546 E_TEST_ACCOUNT_1.clone(),
3547 filter_all!(f_eq(
3548 Attribute::Name,
3549 PartialValue::new_iname("testperson1")
3550 )),
3551 );
3552
3553 let acp = AccessControlDelete::from_raw(
3554 "test_delete",
3555 Uuid::new_v4(),
3556 UUID_TEST_GROUP_1,
3558 filter_valid!(f_eq(
3560 Attribute::Name,
3561 PartialValue::new_iname("testperson1")
3562 )),
3563 );
3564
3565 test_acp_delete!(&de, vec![acp.clone()], &r1_set, true);
3567 test_acp_delete!(&de, vec![acp.clone()], &r2_set, false);
3569 }
3570
3571 #[test]
3572 fn test_access_protected_deny_modify() {
3573 sketching::test_init();
3574
3575 let ev1 = entry_init!(
3576 (Attribute::Class, EntryClass::Account.to_value()),
3577 (Attribute::Name, Value::new_iname("testperson1")),
3578 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3579 )
3580 .into_sealed_committed();
3581 let r1_set = vec![Arc::new(ev1)];
3582
3583 let ev2 = entry_init!(
3584 (Attribute::Class, EntryClass::Account.to_value()),
3585 (Attribute::Class, EntryClass::System.to_value()),
3586 (Attribute::Name, Value::new_iname("testperson1")),
3587 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3588 )
3589 .into_sealed_committed();
3590
3591 let r2_set = vec![Arc::new(ev2)];
3592
3593 let acp_allow = AccessControlModify::from_raw(
3595 "test_modify_allow",
3596 Uuid::new_v4(),
3597 UUID_TEST_GROUP_1,
3599 filter_valid!(f_eq(
3601 Attribute::Name,
3602 PartialValue::new_iname("testperson1")
3603 )),
3604 "displayname class",
3606 "displayname class",
3608 "system recycled",
3610 "system recycled",
3611 );
3612
3613 let me_pres = ModifyEvent::new_impersonate_entry(
3614 E_TEST_ACCOUNT_1.clone(),
3615 filter_all!(f_eq(
3616 Attribute::Name,
3617 PartialValue::new_iname("testperson1")
3618 )),
3619 modlist!([m_pres(Attribute::DisplayName, &Value::new_utf8s("value"))]),
3620 );
3621
3622 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r1_set, true);
3624
3625 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r2_set, false);
3627
3628 let me_rem_sys = ModifyEvent::new_impersonate_entry(
3630 E_TEST_ACCOUNT_1.clone(),
3631 filter_all!(f_eq(
3632 Attribute::Class,
3633 PartialValue::new_iname("testperson1")
3634 )),
3635 modlist!([m_remove(
3636 Attribute::Class,
3637 &EntryClass::System.to_partialvalue()
3638 )]),
3639 );
3640
3641 test_acp_modify!(&me_rem_sys, vec![acp_allow.clone()], &r2_set, false);
3642
3643 let me_pres = ModifyEvent::new_impersonate_entry(
3645 E_TEST_ACCOUNT_1.clone(),
3646 filter_all!(f_eq(
3647 Attribute::Name,
3648 PartialValue::new_iname("testperson1")
3649 )),
3650 modlist!([m_pres(Attribute::Class, &EntryClass::Recycled.to_value())]),
3651 );
3652
3653 test_acp_modify!(&me_pres, vec![acp_allow.clone()], &r1_set, false);
3654 }
3655
3656 #[test]
3657 fn test_access_modify_set_bypass() {
3658 sketching::test_init();
3659
3660 let ev1 = entry_init!(
3661 (Attribute::Class, EntryClass::Account.to_value()),
3662 (Attribute::Name, Value::new_iname("testperson1")),
3663 (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT_1))
3664 )
3665 .into_sealed_committed();
3666 let r1_set = vec![Arc::new(ev1)];
3667
3668 let me_pres = ModifyEvent::new_impersonate_entry(
3669 E_TEST_ACCOUNT_1.clone(),
3670 filter_all!(f_eq(
3671 Attribute::Name,
3672 PartialValue::new_iname("testperson1")
3673 )),
3674 modlist!([Modify::Set(
3675 Attribute::DisplayName,
3676 ValueSetUtf8::new("value".to_string())
3677 )]),
3678 );
3679
3680 test_acp_modify!(&me_pres, vec![], &r1_set, false);
3682
3683 let me_empty = ModifyEvent::new_impersonate_entry(
3684 E_TEST_ACCOUNT_1.clone(),
3685 filter_all!(f_eq(
3686 Attribute::Name,
3687 PartialValue::new_iname("testperson1")
3688 )),
3689 modlist!([]),
3690 );
3691
3692 test_acp_modify!(&me_empty, vec![], &r1_set, false);
3694 }
3695}