1use std::collections::VecDeque;
8use std::collections::{BTreeMap, BTreeSet};
9use std::sync::Arc;
10
11use tracing::trace;
12
13use crate::event::{CreateEvent, ModifyEvent};
14use crate::plugins::Plugin;
15use crate::prelude::*;
16use crate::schema::SchemaTransaction;
17
18pub struct AttrUnique;
19
20fn get_cand_attr_set<'a, VALID: 'a, STATE: 'a, T>(
21 cand: T,
23 uniqueattrs: &[Attribute],
24) -> Result<BTreeMap<(Attribute, PartialValue), Vec<Uuid>>, OperationError>
25where
26 T: IntoIterator<Item = &'a Entry<VALID, STATE>>,
27{
28 let mut cand_attr: BTreeMap<(Attribute, PartialValue), Vec<Uuid>> = BTreeMap::new();
29
30 cand.into_iter()
31 .filter_map(|e| e.mask_recycled_ts())
33 .try_for_each(|e| {
34 let uuid = e
35 .get_ava_single_uuid(Attribute::Uuid)
36 .ok_or_else(|| {
37 error!("An entry is missing its uuid. This should be impossible!");
38 OperationError::InvalidEntryState
39 })?;
40
41 for attr in uniqueattrs.iter() {
43 if let Some(vs) = e.get_ava_set(attr) {
44 for pv in vs.to_partialvalue_iter() {
45 let key = (attr.clone(), pv);
46 cand_attr.entry(key)
47 .and_modify(|v| {
49 warn!(
50 "ava already exists -> {:?} on entry {:?} has conflicts within change set",
51 attr,
52 e.get_display_id()
53 );
54 v.push(uuid)
55 })
56 .or_insert_with(|| vec![uuid]);
58 }
59 }
60 }
61
62 Ok(())
63 })
64 .map(|()| cand_attr)
65}
66
67fn enforce_unique<VALID, STATE>(
68 qs: &mut QueryServerWriteTransaction,
69 cand: &[Entry<VALID, STATE>],
70) -> Result<(), OperationError> {
71 let uniqueattrs = {
72 let schema = qs.get_schema();
73 schema.get_attributes_unique()
74 };
75
76 let cand_attr_set = get_cand_attr_set(cand, uniqueattrs).map_err(|e| {
79 error!(err = ?e, "failed to get cand attr set");
80 e
81 })?;
82
83 if cand_attr_set.is_empty() {
85 return Ok(());
86 }
87
88 let mut cand_attr = Vec::with_capacity(cand_attr_set.len());
90 let mut err = false;
91 for (key, mut uuid_set) in cand_attr_set.into_iter() {
92 if let Some(uuid) = uuid_set.pop() {
93 if uuid_set.is_empty() {
94 cand_attr.push((key, uuid));
96 } else {
97 err = true;
100 }
101 } else {
102 warn!("datastructure corruption occurred while processing candidate attribute set");
104 debug_assert!(false);
105 return Err(OperationError::Plugin(PluginError::AttrUnique(
106 "corruption detected".to_string(),
107 )));
108 }
109 }
110
111 if err {
112 return Err(OperationError::Plugin(PluginError::AttrUnique(
113 "duplicate value detected".to_string(),
114 )));
115 }
116
117 let mut cand_filters = Vec::with_capacity(0);
119 for ((attr, v), uuid) in cand_attr.iter() {
120 cand_filters.push(f_and(vec![
123 FC::Eq(attr.clone(), v.clone()),
124 f_andnot(FC::Eq(Attribute::Uuid, PartialValue::Uuid(*uuid))),
125 ]));
126 }
127
128 let filt_in = filter!(f_or(cand_filters.clone()));
130
131 trace!(?filt_in);
132
133 let conflict_cand = qs.internal_exists(filt_in).map_err(|e| {
135 admin_error!("internal exists error {:?}", e);
136 e
137 })?;
138
139 if conflict_cand {
143 if cand_filters.len() < 2 {
154 error!(
155 ?cand_filters,
156 "The following filter conditions failed to assert uniqueness"
157 );
158 } else {
159 let mid = cand_filters.len() / 2;
163 let (left, right) = cand_filters.split_at(mid);
164
165 let mut queue = VecDeque::new();
166 queue.push_back(left);
167 queue.push_back(right);
168
169 while let Some(cand_query) = queue.pop_front() {
172 let filt_in = filter!(f_or(cand_query.to_vec()));
173 let conflict_cand = qs.internal_search(filt_in).map_err(|e| {
174 admin_error!("internal exists error {:?}", e);
175 e
176 })?;
177
178 if let Some(conflict_cand_zero) = conflict_cand.first() {
180 if cand_query.len() >= 2 {
181 let mid = cand_query.len() / 2;
183 let (left, right) = cand_query.split_at(mid);
184 queue.push_back(left);
185 queue.push_back(right);
186 } else {
188 error!(cand_filters = ?cand_query, conflicting_with = %conflict_cand_zero.get_display_id(), "The following filter conditions failed to assert uniqueness");
190 }
191 }
192 }
193 }
195
196 Err(OperationError::Plugin(PluginError::AttrUnique(
197 "duplicate value detected".to_string(),
198 )))
199 } else {
200 Ok(())
202 }
203}
204
205impl Plugin for AttrUnique {
206 fn id() -> &'static str {
207 "plugin_attrunique"
208 }
209
210 #[instrument(level = "debug", name = "attrunique_pre_create_transform", skip_all)]
211 fn pre_create_transform(
212 qs: &mut QueryServerWriteTransaction,
213 cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
214 _ce: &CreateEvent,
215 ) -> Result<(), OperationError> {
216 enforce_unique(qs, cand)
217 }
218
219 #[instrument(level = "debug", name = "attrunique_pre_modify", skip_all)]
220 fn pre_modify(
221 qs: &mut QueryServerWriteTransaction,
222 _pre_cand: &[Arc<EntrySealedCommitted>],
223 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
224 _me: &ModifyEvent,
225 ) -> Result<(), OperationError> {
226 enforce_unique(qs, cand)
227 }
228
229 #[instrument(level = "debug", name = "attrunique_pre_batch_modify", skip_all)]
230 fn pre_batch_modify(
231 qs: &mut QueryServerWriteTransaction,
232 _pre_cand: &[Arc<EntrySealedCommitted>],
233 cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
234 _me: &BatchModifyEvent,
235 ) -> Result<(), OperationError> {
236 enforce_unique(qs, cand)
237 }
238
239 #[instrument(level = "debug", name = "attrunique_pre_repl_refresh", skip_all)]
240 fn pre_repl_refresh(
241 qs: &mut QueryServerWriteTransaction,
242 cand: &[EntryRefreshNew],
243 ) -> Result<(), OperationError> {
244 enforce_unique(qs, cand)
245 }
246
247 #[instrument(level = "debug", name = "attrunique_post_repl_incremental", skip_all)]
248 fn post_repl_incremental_conflict(
249 qs: &mut QueryServerWriteTransaction,
250 cand: &[(EntrySealedCommitted, Arc<EntrySealedCommitted>)],
251 conflict_uuids: &mut BTreeSet<Uuid>,
252 ) -> Result<(), OperationError> {
253 let uniqueattrs = {
285 let schema = qs.get_schema();
286 schema.get_attributes_unique()
287 };
288
289 let cand_attr_set =
292 get_cand_attr_set(cand.iter().map(|(e, _)| e), uniqueattrs).map_err(|e| {
293 error!(err = ?e, "failed to get cand attr set");
294 e
295 })?;
296
297 if cand_attr_set.is_empty() {
299 return Ok(());
300 }
301
302 let cand_filters: Vec<_> = cand_attr_set
305 .iter()
306 .flat_map(|((attr, v), uuids)| {
307 uuids.iter().map(|uuid| {
308 f_and(vec![
311 FC::Eq(attr.clone(), v.clone()),
312 f_andnot(FC::Eq(Attribute::Uuid, PartialValue::Uuid(*uuid))),
313 ])
314 })
315 })
316 .collect();
317
318 let filt_in = filter!(f_or(cand_filters));
319
320 trace!(?filt_in);
321
322 let conflict_cand = qs.internal_exists(filt_in).map_err(|e| {
324 admin_error!("internal exists error {:?}", e);
325 e
326 })?;
327
328 if conflict_cand {
329 let mut conflict_uuid_map: BTreeMap<Uuid, BTreeSet<Uuid>> = BTreeMap::new();
340
341 let mut cand_attr_map: BTreeMap<Uuid, BTreeSet<_>> = BTreeMap::new();
346
347 cand_attr_set.into_iter().for_each(|(key, uuids)| {
348 uuids.into_iter().for_each(|uuid| {
349 cand_attr_map
350 .entry(uuid)
351 .and_modify(|set| {
352 set.insert(key.clone());
353 })
354 .or_insert_with(|| {
355 let mut set = BTreeSet::new();
356 set.insert(key.clone());
357 set
358 });
359 })
360 });
361
362 for (uuid, ava_set) in cand_attr_map.into_iter() {
363 let cand_filters: Vec<_> = ava_set
364 .iter()
365 .map(|(attr, pv)| {
366 f_and(vec![
367 FC::Eq(attr.clone(), pv.clone()),
368 f_andnot(FC::Eq(Attribute::Uuid, PartialValue::Uuid(uuid))),
369 ])
370 })
371 .collect();
372
373 let filt_in = filter!(f_or(cand_filters.clone()));
374
375 let filt_conflicts = qs.internal_search(filt_in).map_err(|e| {
376 admin_error!("internal search error {:?}", e);
377 e
378 })?;
379
380 if !filt_conflicts.is_empty() {
386 let mut conflict_uuid_set = BTreeSet::new();
387
388 for e in filt_conflicts {
389 conflict_uuid_set.insert(e.get_uuid());
391 conflict_uuid_map
393 .entry(e.get_uuid())
394 .and_modify(|set| {
395 set.insert(uuid);
396 })
397 .or_insert_with(|| {
398 let mut set = BTreeSet::new();
399 set.insert(uuid);
400 set
401 });
402 }
403
404 conflict_uuid_map
405 .entry(uuid)
406 .and_modify(|set| set.append(&mut conflict_uuid_set))
407 .or_insert_with(|| conflict_uuid_set);
408 }
409 }
410
411 trace!(?conflict_uuid_map);
412
413 if conflict_uuid_map.is_empty() {
414 error!("Impossible state. Attribute unique conflicts were detected in fast path, but were not found in slow path.");
415 return Err(OperationError::InvalidState);
416 }
417
418 let filt = filter!(FC::Or(
421 conflict_uuid_map
422 .keys()
423 .map(|u| f_eq(Attribute::Uuid, PartialValue::Uuid(*u)))
424 .collect()
425 ));
426
427 let mut work_set = qs.internal_search_writeable(&filt)?;
428
429 for (_, entry) in work_set.iter_mut() {
430 let Some(uuid) = entry.get_uuid() else {
431 error!("Impossible state. Entry that was declared in conflict map does not have a uuid.");
432 return Err(OperationError::InvalidState);
433 };
434
435 conflict_uuids.insert(uuid);
437
438 if let Some(conflict_uuid_set) = conflict_uuid_map.get(&uuid) {
439 entry.to_conflict(conflict_uuid_set.iter().copied())
440 } else {
441 error!("Impossible state. Entry that was declared in conflict map was not present in work set.");
442 return Err(OperationError::InvalidState);
443 }
444 }
445
446 qs.internal_apply_writable(work_set).map_err(|e| {
447 admin_error!("Failed to commit memberof group set {:?}", e);
448 e
449 })?;
450
451 Ok(())
453 } else {
454 Ok(())
456 }
457 }
458
459 #[instrument(level = "debug", name = "attrunique::verify", skip_all)]
460 fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
461 let filt_in = filter!(f_pres(Attribute::Class));
463
464 let all_cand = match qs
465 .internal_search(filt_in)
466 .map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
467 {
468 Ok(all_cand) => all_cand,
469 Err(e) => return vec![e],
470 };
471
472 let all_cand: Vec<_> = all_cand.into_iter().map(|e| e.as_ref().clone()).collect();
473
474 let uniqueattrs = {
475 let schema = qs.get_schema();
476 schema.get_attributes_unique()
477 };
478
479 let mut res: Vec<Result<(), ConsistencyError>> = Vec::with_capacity(0);
480
481 if get_cand_attr_set(&all_cand, uniqueattrs).is_err() {
482 res.push(Err(ConsistencyError::DuplicateUniqueAttribute))
483 }
484
485 trace!(?res);
486
487 res
488 }
489}
490
491#[cfg(test)]
492mod tests {
493 use crate::prelude::*;
494
495 #[test]
497 fn test_pre_create_name_unique() {
498 let e: Entry<EntryInit, EntryNew> = entry_init!(
499 (Attribute::Class, EntryClass::Person.to_value()),
500 (Attribute::Class, EntryClass::Account.to_value()),
501 (Attribute::Name, Value::new_iname("testperson")),
502 (Attribute::Description, Value::new_utf8s("testperson")),
503 (Attribute::DisplayName, Value::new_utf8s("testperson"))
504 );
505
506 let create = vec![e.clone()];
507 let preload = vec![e];
508
509 run_create_test!(
510 Err(OperationError::Plugin(PluginError::AttrUnique(
511 "duplicate value detected".to_string()
512 ))),
513 preload,
514 create,
515 None,
516 |_| {}
517 );
518 }
519
520 #[test]
522 fn test_pre_create_name_unique_2() {
523 let e: Entry<EntryInit, EntryNew> = entry_init!(
524 (Attribute::Class, EntryClass::Person.to_value()),
525 (Attribute::Class, EntryClass::Account.to_value()),
526 (Attribute::Name, Value::new_iname("testperson")),
527 (Attribute::Description, Value::new_utf8s("testperson")),
528 (Attribute::DisplayName, Value::new_utf8s("testperson"))
529 );
530
531 let create = vec![e.clone(), e];
532 let preload = Vec::with_capacity(0);
533
534 run_create_test!(
535 Err(OperationError::Plugin(PluginError::AttrUnique(
536 "ava already exists".to_string()
537 ))),
538 preload,
539 create,
540 None,
541 |_| {}
542 );
543 }
544
545 #[test]
550 fn test_pre_modify_name_unique() {
551 let ea: Entry<EntryInit, EntryNew> = entry_init!(
552 (Attribute::Class, EntryClass::Group.to_value()),
553 (Attribute::Name, Value::new_iname("testgroup_a")),
554 (Attribute::Description, Value::new_utf8s("testgroup"))
555 );
556 let eb: Entry<EntryInit, EntryNew> = entry_init!(
557 (Attribute::Class, EntryClass::Group.to_value()),
558 (Attribute::Name, Value::new_iname("testgroup_b")),
559 (Attribute::Description, Value::new_utf8s("testgroup"))
560 );
561
562 let preload = vec![ea, eb];
563
564 run_modify_test!(
565 Err(OperationError::Plugin(PluginError::AttrUnique(
566 "duplicate value detected".to_string()
567 ))),
568 preload,
569 filter!(f_or!([f_eq(
570 Attribute::Name,
571 PartialValue::new_iname("testgroup_b")
572 ),])),
573 ModifyList::new_list(vec![
574 Modify::Purged(Attribute::Name),
575 Modify::Present(Attribute::Name, Value::new_iname("testgroup_a"))
576 ]),
577 None,
578 |_| {},
579 |_| {}
580 );
581 }
582
583 #[test]
585 fn test_pre_modify_name_unique_2() {
586 let ea: Entry<EntryInit, EntryNew> = entry_init!(
587 (Attribute::Class, EntryClass::Group.to_value()),
588 (Attribute::Name, Value::new_iname("testgroup_a")),
589 (Attribute::Description, Value::new_utf8s("testgroup"))
590 );
591 let eb: Entry<EntryInit, EntryNew> = entry_init!(
592 (Attribute::Class, EntryClass::Group.to_value()),
593 (Attribute::Name, Value::new_iname("testgroup_b")),
594 (Attribute::Description, Value::new_utf8s("testgroup"))
595 );
596
597 let preload = vec![ea, eb];
598
599 run_modify_test!(
600 Err(OperationError::Plugin(PluginError::AttrUnique(
601 "ava already exists".to_string()
602 ))),
603 preload,
604 filter!(f_or!([
605 f_eq(Attribute::Name, PartialValue::new_iname("testgroup_a")),
606 f_eq(Attribute::Name, PartialValue::new_iname("testgroup_b")),
607 ])),
608 ModifyList::new_list(vec![
609 Modify::Purged(Attribute::Name),
610 Modify::Present(Attribute::Name, Value::new_iname("testgroup"))
611 ]),
612 None,
613 |_| {},
614 |_| {}
615 );
616 }
617
618 #[test]
619 fn test_verify_name_unique() {
620 }
622}