kanidmd_lib/server/access/
create.rs1use super::profiles::{
2 AccessControlCreateResolved, AccessControlReceiverCondition, AccessControlTargetCondition,
3};
4use super::protected::PROTECTED_ENTRY_CLASSES;
5use crate::prelude::*;
6use std::collections::BTreeSet;
7
8pub(super) enum CreateResult {
9 Deny,
10 Grant,
11}
12
13enum IResult {
14 Deny,
15 Grant,
16 Ignore,
17}
18
19pub(super) fn apply_create_access<'a>(
20 ident: &Identity,
21 related_acp: &'a [AccessControlCreateResolved],
22 entry: &'a Entry<EntryInit, EntryNew>,
23) -> CreateResult {
24 let mut denied = false;
25 let mut grant = false;
26
27 match protected_filter_entry(ident, entry) {
29 IResult::Deny => denied = true,
30 IResult::Grant | IResult::Ignore => {}
31 }
32
33 match create_filter_entry(ident, related_acp, entry) {
34 IResult::Deny => denied = true,
35 IResult::Grant => grant = true,
36 IResult::Ignore => {}
37 }
38
39 if denied {
40 CreateResult::Deny
42 } else if grant {
43 CreateResult::Grant
45 } else {
46 CreateResult::Deny
48 }
49}
50
51fn create_filter_entry<'a>(
52 ident: &Identity,
53 related_acp: &'a [AccessControlCreateResolved],
54 entry: &'a Entry<EntryInit, EntryNew>,
55) -> IResult {
56 match &ident.origin {
57 IdentType::Internal => {
58 trace!("Internal operation, bypassing access check");
59 return IResult::Grant;
61 }
62 IdentType::Synch(_) => {
63 security_critical!("Blocking sync check");
64 return IResult::Deny;
65 }
66 IdentType::User(_) => {}
67 };
68 debug!(event = %ident, "Access check for create event");
69
70 match ident.access_scope() {
71 AccessScope::ReadOnly | AccessScope::Synchronise => {
72 security_access!("denied ❌ - identity access scope is not permitted to create");
73 return IResult::Deny;
74 }
75 AccessScope::ReadWrite => {
76 }
78 };
79
80 let create_attrs: BTreeSet<&str> = entry.get_ava_names().collect();
82 let create_classes: BTreeSet<&str> = match entry.get_ava_iter_iutf8(Attribute::Class) {
97 Some(s) => s.collect(),
98 None => {
99 admin_error!("Class set failed to build - corrupted entry?");
100 return IResult::Deny;
101 }
102 };
103
104 let allow = related_acp.iter().any(|accr| {
112 match &accr.receiver_condition {
114 AccessControlReceiverCondition::GroupChecked => {
115 }
118 AccessControlReceiverCondition::EntryManager => {
119 return false;
121 }
122 };
123
124 match &accr.target_condition {
125 AccessControlTargetCondition::Scope(f_res) => {
126 if !entry.entry_match_no_index(f_res) {
127 trace!(?entry, acs = %accr.acp.acp.name, "entry DOES NOT match acs");
128 return false;
130 }
131 }
132 };
133
134 let entry_name = entry.get_display_id();
137 let allowed_attrs: BTreeSet<&str> = accr.acp.attrs.iter().map(|s| s.as_str()).collect();
141 let allowed_classes: BTreeSet<&str> = accr.acp.classes.iter().map(|s| s.as_str()).collect();
142
143 if !create_attrs.is_subset(&allowed_attrs) {
144 debug!(%entry_name, acs = ?accr.acp.acp.name, "entry create denied");
145 debug!("create_attrs is not a subset of allowed");
146 debug!("create: {:?} !⊆ allowed: {:?}", create_attrs, allowed_attrs);
147 false
148 } else if !create_classes.is_subset(&allowed_classes) {
149 debug!(%entry_name, acs = ?accr.acp.acp.name, "entry create denied");
150 debug!("create_classes is not a subset of allowed");
151 debug!(
152 "create: {:?} !⊆ allowed: {:?}",
153 create_classes, allowed_classes
154 );
155 false
156 } else {
157 info!(%entry_name, acs = ?accr.acp.acp.name, "entry create allowed");
159 debug!("create: {:?} ⊆ allowed: {:?}", create_attrs, allowed_attrs);
160 debug!(
161 "create: {:?} ⊆ allowed: {:?}",
162 create_classes, allowed_classes
163 );
164 true
165 }
166 });
167
168 if allow {
169 IResult::Grant
170 } else {
171 IResult::Ignore
172 }
173}
174
175fn protected_filter_entry(ident: &Identity, entry: &Entry<EntryInit, EntryNew>) -> IResult {
176 match &ident.origin {
177 IdentType::Internal => {
178 trace!("Internal operation, protected rules do not apply.");
179 IResult::Ignore
180 }
181 IdentType::Synch(_) => {
182 security_access!("sync agreements may not directly create entities");
183 IResult::Deny
184 }
185 IdentType::User(_) => {
186 if let Some(classes) = entry.get_ava_as_iutf8(Attribute::Class) {
188 if classes.is_disjoint(&PROTECTED_ENTRY_CLASSES) {
189 IResult::Ignore
191 } else {
192 security_access!("attempt to create with protected class type");
194 IResult::Deny
195 }
196 } else {
197 IResult::Ignore
200 }
201 }
202 }
203}