1use crate::prelude::*;
2use std::collections::BTreeSet;
3
4use crate::filter::{Filter, FilterValid, FilterValidResolved};
5
6use kanidm_proto::internal::Filter as ProtoFilter;
7
8#[derive(Debug, Clone)]
13pub struct AccessControlSearchResolved<'a> {
14 pub acp: &'a AccessControlSearch,
15 pub receiver_condition: AccessControlReceiverCondition,
16 pub target_condition: AccessControlTargetCondition,
17}
18
19#[derive(Debug, Clone)]
20pub struct AccessControlSearch {
21 pub acp: AccessControlProfile,
22 pub attrs: BTreeSet<Attribute>,
23}
24
25impl AccessControlSearch {
26 pub fn try_from(
27 qs: &mut QueryServerWriteTransaction,
28 value: &Entry<EntrySealed, EntryCommitted>,
29 ) -> Result<Self, OperationError> {
30 if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlSearch.into()) {
31 admin_error!("class {} not present.", EntryClass::AccessControlSearch);
32 return Err(OperationError::InvalidAcpState(format!(
33 "Missing {}",
34 EntryClass::AccessControlSearch
35 )));
36 }
37
38 let mut attrs: BTreeSet<_> = value
39 .get_ava_iter_iutf8(Attribute::AcpSearchAttr)
40 .ok_or_else(|| {
41 admin_error!("Missing {}", Attribute::AcpSearchAttr);
42 OperationError::InvalidAcpState(format!("Missing {}", Attribute::AcpSearchAttr))
43 })?
44 .map(Attribute::from)
45 .collect();
46
47 if attrs.contains(&Attribute::MemberOf) {
49 attrs.insert(Attribute::DirectMemberOf);
50 }
51
52 let acp = AccessControlProfile::try_from(qs, value)?;
53
54 Ok(AccessControlSearch { acp, attrs })
55 }
56
57 #[cfg(test)]
60 pub(super) fn from_raw(
61 name: &str,
62 uuid: Uuid,
63 receiver: Uuid,
64 targetscope: Filter<FilterValid>,
65 attrs: &str,
66 ) -> Self {
67 let mut attrs: BTreeSet<_> = attrs.split_whitespace().map(Attribute::from).collect();
68
69 if attrs.contains(&Attribute::MemberOf) {
71 attrs.insert(Attribute::DirectMemberOf);
72 }
73
74 AccessControlSearch {
75 acp: AccessControlProfile {
76 name: name.to_string(),
77 uuid,
78 receiver: AccessControlReceiver::Group(btreeset!(receiver)),
79 target: AccessControlTarget::Scope(targetscope),
80 },
81 attrs,
82 }
83 }
84
85 #[cfg(test)]
88 pub(super) fn from_managed_by(
89 name: &str,
90 uuid: Uuid,
91 target: AccessControlTarget,
92 attrs: &str,
93 ) -> Self {
94 AccessControlSearch {
95 acp: AccessControlProfile {
96 name: name.to_string(),
97 uuid,
98 receiver: AccessControlReceiver::EntryManager,
99 target,
100 },
101 attrs: attrs.split_whitespace().map(Attribute::from).collect(),
102 }
103 }
104}
105
106#[derive(Debug, Clone)]
107pub struct AccessControlDeleteResolved<'a> {
108 pub acp: &'a AccessControlDelete,
109 pub receiver_condition: AccessControlReceiverCondition,
110 pub target_condition: AccessControlTargetCondition,
111}
112
113#[derive(Debug, Clone)]
114pub struct AccessControlDelete {
115 pub acp: AccessControlProfile,
116}
117
118impl AccessControlDelete {
119 pub fn try_from(
120 qs: &mut QueryServerWriteTransaction,
121 value: &Entry<EntrySealed, EntryCommitted>,
122 ) -> Result<Self, OperationError> {
123 if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlDelete.into()) {
124 admin_error!("class access_control_delete not present.");
125 return Err(OperationError::InvalidAcpState(
126 "Missing access_control_delete".to_string(),
127 ));
128 }
129
130 Ok(AccessControlDelete {
131 acp: AccessControlProfile::try_from(qs, value)?,
132 })
133 }
134
135 #[cfg(test)]
138 pub(super) fn from_raw(
139 name: &str,
140 uuid: Uuid,
141 receiver: Uuid,
142 targetscope: Filter<FilterValid>,
143 ) -> Self {
144 AccessControlDelete {
145 acp: AccessControlProfile {
146 name: name.to_string(),
147 uuid,
148 receiver: AccessControlReceiver::Group(btreeset!(receiver)),
149 target: AccessControlTarget::Scope(targetscope),
150 },
151 }
152 }
153
154 #[cfg(test)]
157 pub(super) fn from_managed_by(name: &str, uuid: Uuid, target: AccessControlTarget) -> Self {
158 AccessControlDelete {
159 acp: AccessControlProfile {
160 name: name.to_string(),
161 uuid,
162 receiver: AccessControlReceiver::EntryManager,
163 target,
164 },
165 }
166 }
167}
168
169#[derive(Debug, Clone)]
170pub struct AccessControlCreateResolved<'a> {
171 pub acp: &'a AccessControlCreate,
172 pub receiver_condition: AccessControlReceiverCondition,
173 pub target_condition: AccessControlTargetCondition,
174}
175
176#[derive(Debug, Clone)]
177pub struct AccessControlCreate {
178 pub acp: AccessControlProfile,
179 pub classes: Vec<AttrString>,
180 pub attrs: Vec<Attribute>,
181}
182
183impl AccessControlCreate {
184 pub fn try_from(
185 qs: &mut QueryServerWriteTransaction,
186 value: &Entry<EntrySealed, EntryCommitted>,
187 ) -> Result<Self, OperationError> {
188 if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlCreate.into()) {
189 admin_error!("class {} not present.", EntryClass::AccessControlCreate);
190 return Err(OperationError::InvalidAcpState(format!(
191 "Missing {}",
192 EntryClass::AccessControlCreate
193 )));
194 }
195
196 let attrs = value
197 .get_ava_iter_iutf8(Attribute::AcpCreateAttr)
198 .map(|i| i.map(Attribute::from).collect())
199 .unwrap_or_default();
200
201 let classes = value
202 .get_ava_iter_iutf8(Attribute::AcpCreateClass)
203 .map(|i| i.map(AttrString::from).collect())
204 .unwrap_or_default();
205
206 Ok(AccessControlCreate {
207 acp: AccessControlProfile::try_from(qs, value)?,
208 classes,
209 attrs,
210 })
211 }
212
213 #[cfg(test)]
216 pub(super) fn from_raw(
217 name: &str,
218 uuid: Uuid,
219 receiver: Uuid,
220 targetscope: Filter<FilterValid>,
221 classes: &str,
222 attrs: &str,
223 ) -> Self {
224 AccessControlCreate {
225 acp: AccessControlProfile {
226 name: name.to_string(),
227 uuid,
228 receiver: AccessControlReceiver::Group(btreeset!(receiver)),
229 target: AccessControlTarget::Scope(targetscope),
230 },
231 classes: classes.split_whitespace().map(AttrString::from).collect(),
232 attrs: attrs.split_whitespace().map(Attribute::from).collect(),
233 }
234 }
235
236 #[cfg(test)]
239 pub(super) fn from_managed_by(
240 name: &str,
241 uuid: Uuid,
242 target: AccessControlTarget,
243 classes: &str,
244 attrs: &str,
245 ) -> Self {
246 AccessControlCreate {
247 acp: AccessControlProfile {
248 name: name.to_string(),
249 uuid,
250 receiver: AccessControlReceiver::EntryManager,
251 target,
252 },
253 classes: classes.split_whitespace().map(AttrString::from).collect(),
254 attrs: attrs.split_whitespace().map(Attribute::from).collect(),
255 }
256 }
257}
258
259#[derive(Debug, Clone)]
260pub struct AccessControlModifyResolved<'a> {
261 pub acp: &'a AccessControlModify,
262 pub receiver_condition: AccessControlReceiverCondition,
263 pub target_condition: AccessControlTargetCondition,
264}
265
266#[derive(Debug, Clone)]
267pub struct AccessControlModify {
268 pub acp: AccessControlProfile,
269 pub presattrs: Vec<Attribute>,
270 pub remattrs: Vec<Attribute>,
271 pub pres_classes: Vec<AttrString>,
272 pub rem_classes: Vec<AttrString>,
273}
274
275impl AccessControlModify {
276 pub fn try_from(
277 qs: &mut QueryServerWriteTransaction,
278 value: &Entry<EntrySealed, EntryCommitted>,
279 ) -> Result<Self, OperationError> {
280 if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlModify.into()) {
281 admin_error!("class access_control_modify not present.");
282 return Err(OperationError::InvalidAcpState(
283 "Missing access_control_modify".to_string(),
284 ));
285 }
286
287 let presattrs = value
288 .get_ava_iter_iutf8(Attribute::AcpModifyPresentAttr)
289 .map(|i| i.map(Attribute::from).collect())
290 .unwrap_or_default();
291
292 let remattrs = value
293 .get_ava_iter_iutf8(Attribute::AcpModifyRemovedAttr)
294 .map(|i| i.map(Attribute::from).collect())
295 .unwrap_or_default();
296
297 let classes: Vec<AttrString> = value
298 .get_ava_iter_iutf8(Attribute::AcpModifyClass)
299 .map(|i| i.map(AttrString::from).collect())
300 .unwrap_or_default();
301
302 let pres_classes = value
303 .get_ava_iter_iutf8(Attribute::AcpModifyPresentClass)
304 .map(|i| i.map(AttrString::from).collect())
305 .unwrap_or_else(|| classes.clone());
306
307 let rem_classes = value
308 .get_ava_iter_iutf8(Attribute::AcpModifyRemoveClass)
309 .map(|i| i.map(AttrString::from).collect())
310 .unwrap_or_else(|| classes);
311
312 Ok(AccessControlModify {
313 acp: AccessControlProfile::try_from(qs, value)?,
314 pres_classes,
315 rem_classes,
316 presattrs,
317 remattrs,
318 })
319 }
320
321 #[cfg(test)]
324 pub(super) fn from_raw(
325 name: &str,
326 uuid: Uuid,
327 receiver: Uuid,
328 targetscope: Filter<FilterValid>,
329 presattrs: &str,
330 remattrs: &str,
331 pres_classes: &str,
332 rem_classes: &str,
333 ) -> Self {
334 AccessControlModify {
335 acp: AccessControlProfile {
336 name: name.to_string(),
337 uuid,
338 receiver: AccessControlReceiver::Group(btreeset!(receiver)),
339 target: AccessControlTarget::Scope(targetscope),
340 },
341 pres_classes: pres_classes
342 .split_whitespace()
343 .map(AttrString::from)
344 .collect(),
345 rem_classes: rem_classes
346 .split_whitespace()
347 .map(AttrString::from)
348 .collect(),
349 presattrs: presattrs.split_whitespace().map(Attribute::from).collect(),
350 remattrs: remattrs.split_whitespace().map(Attribute::from).collect(),
351 }
352 }
353
354 #[cfg(test)]
357 pub(super) fn from_managed_by(
358 name: &str,
359 uuid: Uuid,
360 target: AccessControlTarget,
361 presattrs: &str,
362 remattrs: &str,
363 pres_classes: &str,
364 rem_classes: &str,
365 ) -> Self {
366 AccessControlModify {
367 acp: AccessControlProfile {
368 name: name.to_string(),
369 uuid,
370 receiver: AccessControlReceiver::EntryManager,
371 target,
372 },
373 pres_classes: pres_classes
374 .split_whitespace()
375 .map(AttrString::from)
376 .collect(),
377 rem_classes: rem_classes
378 .split_whitespace()
379 .map(AttrString::from)
380 .collect(),
381 presattrs: presattrs.split_whitespace().map(Attribute::from).collect(),
382 remattrs: remattrs.split_whitespace().map(Attribute::from).collect(),
383 }
384 }
385}
386
387#[derive(Debug, Clone)]
388pub enum AccessControlReceiver {
389 None,
390 Group(BTreeSet<Uuid>),
391 EntryManager,
392}
393
394#[derive(Debug, Clone)]
395pub enum AccessControlReceiverCondition {
396 GroupChecked,
398 EntryManager,
399}
400
401#[derive(Debug, Clone)]
410pub enum AccessControlTarget {
411 None,
412 Scope(Filter<FilterValid>),
413}
414
415#[derive(Debug, Clone)]
416pub enum AccessControlTargetCondition {
417 Scope(Filter<FilterValidResolved>),
419}
420
421#[derive(Debug, Clone)]
430pub struct AccessControlProfile {
431 pub name: String,
432 #[allow(dead_code)]
435 uuid: Uuid,
436 pub receiver: AccessControlReceiver,
437 pub target: AccessControlTarget,
438}
439
440impl AccessControlProfile {
441 pub(super) fn try_from(
442 qs: &mut QueryServerWriteTransaction,
443 value: &Entry<EntrySealed, EntryCommitted>,
444 ) -> Result<Self, OperationError> {
445 if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into()) {
447 error!("class access_control_profile not present.");
448 return Err(OperationError::InvalidAcpState(
449 "Missing access_control_profile".to_string(),
450 ));
451 }
452
453 let name = value
455 .get_ava_single_iname(Attribute::Name)
456 .ok_or_else(|| {
457 error!("Missing {}", Attribute::Name);
458 OperationError::InvalidAcpState(format!("Missing {}", Attribute::Name))
459 })?
460 .to_string();
461 let uuid = value.get_uuid();
463
464 let receiver = if value.attribute_equality(
465 Attribute::Class,
466 &EntryClass::AccessControlReceiverGroup.into(),
467 ) {
468 value
469 .get_ava_refer(Attribute::AcpReceiverGroup)
470 .cloned()
471 .map(AccessControlReceiver::Group)
472 .ok_or_else(|| {
473 admin_error!("Missing {}", Attribute::AcpReceiverGroup);
474 OperationError::InvalidAcpState(format!(
475 "Missing {}",
476 Attribute::AcpReceiverGroup
477 ))
478 })?
479 } else if value.attribute_equality(
480 Attribute::Class,
481 &EntryClass::AccessControlReceiverEntryManager.into(),
482 ) {
483 AccessControlReceiver::EntryManager
484 } else {
485 warn!(
486 ?name,
487 "access control has no defined receivers - this will do nothing!"
488 );
489 AccessControlReceiver::None
490 };
491
492 let target = if value.attribute_equality(
493 Attribute::Class,
494 &EntryClass::AccessControlTargetScope.into(),
495 ) {
496 let targetscope_f: ProtoFilter = value
498 .get_ava_single_protofilter(Attribute::AcpTargetScope)
499 .cloned()
500 .ok_or_else(|| {
501 admin_error!("Missing {}", Attribute::AcpTargetScope);
502 OperationError::InvalidAcpState(format!(
503 "Missing {}",
504 Attribute::AcpTargetScope
505 ))
506 })?;
507
508 let ident = Identity::from_internal();
509
510 let targetscope_i = Filter::from_rw(&ident, &targetscope_f, qs).map_err(|e| {
511 admin_error!("{} validation failed {:?}", Attribute::AcpTargetScope, e);
512 e
513 })?;
514
515 targetscope_i
516 .validate(qs.get_schema())
517 .map_err(|e| {
518 admin_error!("{} Schema Violation {:?}", Attribute::AcpTargetScope, e);
519 OperationError::SchemaViolation(e)
520 })
521 .map(AccessControlTarget::Scope)?
522 } else {
523 warn!(
524 ?name,
525 "access control has no defined targets - this will do nothing!"
526 );
527 AccessControlTarget::None
528 };
529
530 Ok(AccessControlProfile {
531 name,
532 uuid,
533 receiver,
534 target,
535 })
536 }
537}