kanidmd_lib/server/access/
delete.rs
1use super::profiles::{
2 AccessControlDeleteResolved, AccessControlReceiverCondition, AccessControlTargetCondition,
3};
4use super::protected::PROTECTED_ENTRY_CLASSES;
5use crate::prelude::*;
6use std::sync::Arc;
7
8pub(super) enum DeleteResult {
9 Deny,
10 Grant,
11}
12
13enum IResult {
14 Deny,
15 Grant,
16 Ignore,
17}
18
19pub(super) fn apply_delete_access<'a>(
20 ident: &Identity,
21 related_acp: &'a [AccessControlDeleteResolved],
22 entry: &'a Arc<EntrySealedCommitted>,
23) -> DeleteResult {
24 let mut denied = false;
25 let mut grant = false;
26
27 match protected_filter_entry(ident, entry) {
28 IResult::Deny => denied = true,
29 IResult::Grant | IResult::Ignore => {}
30 }
31
32 match delete_filter_entry(ident, related_acp, entry) {
33 IResult::Deny => denied = true,
34 IResult::Grant => grant = true,
35 IResult::Ignore => {}
36 }
37
38 if denied {
39 DeleteResult::Deny
41 } else if grant {
42 DeleteResult::Grant
44 } else {
45 DeleteResult::Deny
47 }
48}
49
50fn delete_filter_entry<'a>(
51 ident: &Identity,
52 related_acp: &'a [AccessControlDeleteResolved],
53 entry: &'a Arc<EntrySealedCommitted>,
54) -> IResult {
55 match &ident.origin {
56 IdentType::Internal => {
57 trace!("Internal operation, bypassing access check");
58 return IResult::Grant;
60 }
61 IdentType::Synch(_) => {
62 security_critical!("Blocking sync check");
63 return IResult::Deny;
64 }
65 IdentType::User(_) => {}
66 };
67 debug!(event = %ident, "Access check for delete event");
68
69 match ident.access_scope() {
70 AccessScope::ReadOnly | AccessScope::Synchronise => {
71 security_access!("denied ❌ - identity access scope is not permitted to delete");
72 return IResult::Deny;
73 }
74 AccessScope::ReadWrite => {
75 }
77 };
78
79 let ident_memberof = ident.get_memberof();
80 let ident_uuid = ident.get_uuid();
81
82 let allow = related_acp.iter().any(|acd| {
83 match &acd.receiver_condition {
85 AccessControlReceiverCondition::GroupChecked => {
86 }
89 AccessControlReceiverCondition::EntryManager => {
90 if let Some(entry_manager_uuids) = entry.get_ava_refer(Attribute::EntryManagedBy) {
97 let group_check = ident_memberof
98 .map(|imo| imo.intersection(entry_manager_uuids).next().is_some())
100 .unwrap_or_default();
101
102 let user_check = ident_uuid
103 .map(|u| entry_manager_uuids.contains(&u))
104 .unwrap_or_default();
105
106 if !(group_check || user_check) {
107 return false;
109 }
110 } else {
111 return false;
113 }
114 }
115 };
116
117 match &acd.target_condition {
118 AccessControlTargetCondition::Scope(f_res) => {
119 if !entry.entry_match_no_index(f_res) {
120 trace!(
121 "entry {:?} DOES NOT match acs {}",
122 entry.get_uuid(),
123 acd.acp.acp.name
124 );
125 return false;
127 }
128 }
129 };
130
131 let entry_name = entry.get_display_id();
132 security_access!(
133 %entry_name,
134 acs = %acd.acp.acp.name,
135 "entry matches acs"
136 );
137
138 true
139 }); if allow {
142 IResult::Grant
143 } else {
144 IResult::Ignore
145 }
146}
147
148fn protected_filter_entry(ident: &Identity, entry: &Arc<EntrySealedCommitted>) -> IResult {
149 match &ident.origin {
150 IdentType::Internal => {
151 trace!("Internal operation, protected rules do not apply.");
152 IResult::Ignore
153 }
154 IdentType::Synch(_) => {
155 security_access!("sync agreements may not directly delete entities");
156 IResult::Deny
157 }
158 IdentType::User(_) => {
159 if entry.get_uuid() <= UUID_ANONYMOUS {
161 security_access!("attempt to delete system builtin entry");
162 return IResult::Deny;
163 }
164
165 if let Some(classes) = entry.get_ava_as_iutf8(Attribute::Class) {
167 if classes.is_disjoint(&PROTECTED_ENTRY_CLASSES) {
168 IResult::Ignore
170 } else {
171 security_access!("attempt to create with protected class type");
173 IResult::Deny
174 }
175 } else {
176 IResult::Ignore
179 }
180 }
181 }
182}