kanidmd_lib/plugins/
base.rs

1use std::collections::BTreeSet;
2use std::iter::once;
3use std::sync::Arc;
4
5use hashbrown::HashSet;
6
7use crate::event::{CreateEvent, ModifyEvent};
8use crate::modify::Modify;
9use crate::plugins::Plugin;
10use crate::prelude::*;
11
12// This module has some special properties around it's operation, namely that it
13// has to make a certain number of assertions *early* in the entry lifecycle around
14// names and uuids since these have such significance to every other part of the
15// servers operation. As a result, this is the ONLY PLUGIN that does validation in the
16// pre_create_transform step, where every other SHOULD use the post_* hooks for all
17// validation operations.
18//
19// Additionally, this plugin WILL block and deny certain modifications to uuids and
20// more to prevent intentional DB damage.
21
22pub struct Base {}
23
24impl Plugin for Base {
25    fn id() -> &'static str {
26        "plugin_base"
27    }
28
29    #[instrument(level = "debug", name = "base_pre_create_transform", skip_all)]
30    #[allow(clippy::cognitive_complexity)]
31    fn pre_create_transform(
32        qs: &mut QueryServerWriteTransaction,
33        cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
34        ce: &CreateEvent,
35    ) -> Result<(), OperationError> {
36        // debug!("Entering base pre_create_transform");
37        // For each candidate
38        for entry in cand.iter_mut() {
39            // First, ensure we have the 'object', class in the class set.
40            entry.add_ava(Attribute::Class, EntryClass::Object.to_value());
41
42            // if they don't have uuid, create it.
43            match entry.get_ava_set(Attribute::Uuid).map(|s| s.len()) {
44                None => {
45                    // Generate
46                    let ava_uuid = Value::Uuid(Uuid::new_v4());
47                    trace!("Setting temporary UUID {:?} to entry", ava_uuid);
48                    entry.set_ava(&Attribute::Uuid, once(ava_uuid));
49                }
50                Some(1) => {
51                    // Do nothing
52                }
53                Some(x) => {
54                    // If we get some it MUST be 2 +
55                    admin_error!(
56                        "Entry defines {} attr, but has multiple ({}) values.",
57                        Attribute::Uuid,
58                        x
59                    );
60                    return Err(OperationError::Plugin(PluginError::Base(
61                        "Uuid has multiple values".to_string(),
62                    )));
63                }
64            };
65        }
66
67        // Now, every cand has a UUID - create a cand uuid set from it.
68        let mut cand_uuid: BTreeSet<Uuid> = BTreeSet::new();
69
70        let mut system_range_invalid = false;
71
72        // As we insert into the set, if a duplicate is found, return an error
73        // that a duplicate exists.
74        //
75        // Remember, we have to use the ava here, not the get_uuid types because
76        // we may not have filled in the uuid field yet.
77        for entry in cand.iter_mut() {
78            let uuid_ref: Uuid = entry
79                .get_ava_single_uuid(Attribute::Uuid)
80                .ok_or_else(|| OperationError::InvalidAttribute(Attribute::Uuid.to_string()))?;
81
82            // Check that the system-protected range is not in the cand_uuid, unless we are
83            // an internal operation.
84            if uuid_ref < DYNAMIC_RANGE_MINIMUM_UUID {
85                if ce.ident.is_internal() {
86                    // it's a builtin entry, lets add the class.
87                    entry.add_ava(Attribute::Class, EntryClass::Builtin.to_value());
88                } else {
89                    // Don't do that!
90                    error!(
91                        "uuid from protected system UUID range found in create set! {:?}",
92                        uuid_ref
93                    );
94                    system_range_invalid = true;
95                }
96            };
97
98            if !cand_uuid.insert(uuid_ref) {
99                trace!("uuid duplicate found in create set! {:?}", uuid_ref);
100                return Err(OperationError::Plugin(PluginError::Base(
101                    "Uuid duplicate detected in request".to_string(),
102                )));
103            }
104        }
105
106        if system_range_invalid {
107            return Err(OperationError::Plugin(PluginError::Base(
108                "Uuid must not be in protected range".to_string(),
109            )));
110        }
111
112        if cand_uuid.contains(&UUID_DOES_NOT_EXIST) {
113            error!(
114                "uuid \"does not exist\" found in create set! THIS IS A BUG. PLEASE REPORT IT IMMEDIATELY."
115            );
116            return Err(OperationError::Plugin(PluginError::Base(
117                "Attempt to create UUID_DOES_NOT_EXIST".to_string(),
118            )));
119        }
120
121        // Now from each element, generate a filter to search for all of them
122        //
123        // IMPORTANT: We don't exclude recycled or tombstones here!
124        let filt_in = filter_all!(FC::Or(
125            cand_uuid
126                .into_iter()
127                .map(|u| FC::Eq(Attribute::Uuid, PartialValue::Uuid(u)))
128                .collect(),
129        ));
130
131        // If any results exist, fail as a duplicate UUID is present.
132        // TODO #69: Can we report which UUID exists? Probably yes, we do
133        // internal search and report the UUID *OR* we alter internal_exists
134        // to return UUID sets. This can be done as an extension to #69 where the
135        // internal exists is actually a wrapper around a search for uuid internally
136        //
137        // But does it add value? How many people will try to custom define/add uuid?
138        let r = qs.internal_exists(filt_in);
139
140        match r {
141            Ok(b) => {
142                if b {
143                    admin_error!("A UUID already exists, rejecting.");
144                    return Err(OperationError::Plugin(PluginError::Base(
145                        "Uuid duplicate found in database".to_string(),
146                    )));
147                }
148            }
149            Err(e) => {
150                admin_error!("Error occurred checking UUID existence. {:?}", e);
151                return Err(e);
152            }
153        }
154
155        Ok(())
156    }
157
158    #[instrument(level = "debug", name = "base_pre_modify", skip_all)]
159    fn pre_modify(
160        _qs: &mut QueryServerWriteTransaction,
161        _pre_cand: &[Arc<EntrySealedCommitted>],
162        _cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
163        me: &ModifyEvent,
164    ) -> Result<(), OperationError> {
165        me.modlist.iter().try_for_each(|modify| {
166            let attr = match &modify {
167                Modify::Present(a, _)
168                | Modify::Removed(a, _)
169                | Modify::Purged(a)
170                | Modify::Set(a, _) => Some(a),
171                Modify::Assert(_, _) => None,
172            };
173            if attr == Some(&Attribute::Uuid) {
174                debug!(?modify, "Modify in violation");
175                request_error!("Modifications to UUID's are NOT ALLOWED");
176                Err(OperationError::SystemProtectedAttribute)
177            } else {
178                Ok(())
179            }
180        })
181    }
182
183    #[instrument(level = "debug", name = "base_pre_modify", skip_all)]
184    fn pre_batch_modify(
185        _qs: &mut QueryServerWriteTransaction,
186        _pre_cand: &[Arc<EntrySealedCommitted>],
187        _cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
188        me: &BatchModifyEvent,
189    ) -> Result<(), OperationError> {
190        me.modset
191            .values()
192            .flat_map(|ml| ml.iter())
193            .try_for_each(|modify| {
194                let attr = match &modify {
195                    Modify::Present(a, _)
196                    | Modify::Removed(a, _)
197                    | Modify::Set(a, _)
198                    | Modify::Purged(a) => Some(a),
199                    Modify::Assert(_, _) => None,
200                };
201                if attr == Some(&Attribute::Uuid) {
202                    debug!(?modify, "Modify in violation");
203                    request_error!("Modifications to UUID's are NOT ALLOWED");
204                    Err(OperationError::SystemProtectedAttribute)
205                } else {
206                    Ok(())
207                }
208            })
209    }
210
211    #[instrument(level = "debug", name = "base::verify", skip_all)]
212    fn verify(qs: &mut QueryServerReadTransaction) -> Vec<Result<(), ConsistencyError>> {
213        // Search for class = *
214        let entries = match qs.internal_search(filter!(f_pres(Attribute::Class))) {
215            Ok(v) => v,
216            Err(e) => {
217                admin_error!("Internal Search Failure: {:?}", e);
218                return vec![Err(ConsistencyError::QueryServerSearchFailure)];
219            }
220        };
221
222        let mut uuid_seen: HashSet<Uuid> = HashSet::with_capacity(entries.len());
223
224        entries
225            .iter()
226            // do an exists checks on the uuid
227            .map(|e| {
228                // To get the entry deserialised, a UUID MUST EXIST, else an expect
229                // will be thrown in the deserialise (possibly it will be better
230                // handled later). But it means this check only needs to validate
231                // uniqueness!
232                let uuid = e.get_uuid();
233
234                if uuid_seen.insert(uuid) {
235                    // Insert returns true if the item was unique.
236                    Ok(())
237                } else {
238                    Err(ConsistencyError::UuidNotUnique(uuid.to_string()))
239                }
240            })
241            .filter(|v| v.is_err())
242            .collect()
243    }
244}
245
246#[cfg(test)]
247mod tests {
248    use crate::prelude::*;
249    use std::sync::Arc;
250
251    const UUID_TEST_ACCOUNT: Uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
252    const UUID_TEST_GROUP: Uuid = uuid::uuid!("81ec1640-3637-4a2f-8a52-874fa3c3c92f");
253    const UUID_TEST_ACP: Uuid = uuid::uuid!("acae81d6-5ea7-4bd8-8f7f-fcec4c0dd647");
254
255    lazy_static! {
256        pub static ref TEST_ACCOUNT: EntryInitNew = entry_init!(
257            (Attribute::Class, EntryClass::Account.to_value()),
258            (Attribute::Class, EntryClass::ServiceAccount.to_value()),
259            (Attribute::Class, EntryClass::MemberOf.to_value()),
260            (Attribute::Name, Value::new_iname("test_account_1")),
261            (Attribute::DisplayName, Value::new_utf8s("test_account_1")),
262            (Attribute::Uuid, Value::Uuid(UUID_TEST_ACCOUNT)),
263            (Attribute::MemberOf, Value::Refer(UUID_TEST_GROUP))
264        );
265        pub static ref TEST_GROUP: EntryInitNew = entry_init!(
266            (Attribute::Class, EntryClass::Group.to_value()),
267            (Attribute::Name, Value::new_iname("test_group_a")),
268            (Attribute::Uuid, Value::Uuid(UUID_TEST_GROUP)),
269            (Attribute::Member, Value::Refer(UUID_TEST_ACCOUNT))
270        );
271        pub static ref ALLOW_ALL: EntryInitNew = entry_init!(
272            (Attribute::Class, EntryClass::Object.to_value()),
273            (
274                Attribute::Class,
275                EntryClass::AccessControlProfile.to_value()
276            ),
277            (
278                Attribute::Class,
279                EntryClass::AccessControlTargetScope.to_value()
280            ),
281            (
282                Attribute::Class,
283                EntryClass::AccessControlReceiverGroup.to_value()
284            ),
285            (Attribute::Class, EntryClass::AccessControlModify.to_value()),
286            (Attribute::Class, EntryClass::AccessControlCreate.to_value()),
287            (Attribute::Class, EntryClass::AccessControlDelete.to_value()),
288            (Attribute::Class, EntryClass::AccessControlSearch.to_value()),
289            (
290                Attribute::Name,
291                Value::new_iname("idm_admins_acp_allow_all_test")
292            ),
293            (Attribute::Uuid, Value::Uuid(UUID_TEST_ACP)),
294            (Attribute::AcpReceiverGroup, Value::Refer(UUID_TEST_GROUP)),
295            (
296                Attribute::AcpTargetScope,
297                Value::new_json_filter_s("{\"pres\":\"class\"}").expect("filter")
298            ),
299            (Attribute::AcpSearchAttr, Value::from(Attribute::Name)),
300            (Attribute::AcpSearchAttr, Value::from(Attribute::Class)),
301            (Attribute::AcpSearchAttr, Value::from(Attribute::Uuid)),
302            (Attribute::AcpModifyClass, EntryClass::System.to_value()),
303            (
304                Attribute::AcpModifyRemovedAttr,
305                Value::from(Attribute::Class)
306            ),
307            (
308                Attribute::AcpModifyRemovedAttr,
309                Value::from(Attribute::DisplayName)
310            ),
311            (Attribute::AcpModifyRemovedAttr, Value::from(Attribute::May)),
312            (
313                Attribute::AcpModifyRemovedAttr,
314                Value::from(Attribute::Must)
315            ),
316            (
317                Attribute::AcpModifyPresentAttr,
318                Value::from(Attribute::Class)
319            ),
320            (
321                Attribute::AcpModifyPresentAttr,
322                Value::from(Attribute::DisplayName)
323            ),
324            (Attribute::AcpModifyPresentAttr, Value::from(Attribute::May)),
325            (
326                Attribute::AcpModifyPresentAttr,
327                Value::from(Attribute::Must)
328            ),
329            (Attribute::AcpCreateClass, EntryClass::Object.to_value()),
330            (Attribute::AcpCreateClass, EntryClass::Person.to_value()),
331            (Attribute::AcpCreateClass, EntryClass::System.to_value()),
332            (Attribute::AcpCreateAttr, Value::from(Attribute::Name)),
333            (Attribute::AcpCreateAttr, Value::from(Attribute::Class)),
334            (
335                Attribute::AcpCreateAttr,
336                Value::from(Attribute::Description)
337            ),
338            (
339                Attribute::AcpCreateAttr,
340                Value::from(Attribute::DisplayName)
341            ),
342            (Attribute::AcpCreateAttr, Value::from(Attribute::Uuid))
343        );
344        pub static ref PRELOAD: Vec<EntryInitNew> =
345            vec![TEST_ACCOUNT.clone(), TEST_GROUP.clone(), ALLOW_ALL.clone()];
346        pub static ref E_TEST_ACCOUNT: Arc<EntrySealedCommitted> =
347            Arc::new(TEST_ACCOUNT.clone().into_sealed_committed());
348    }
349
350    // check create where no uuid
351    #[test]
352    fn test_pre_create_no_uuid() {
353        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
354
355        let e = entry_init!(
356            (Attribute::Class, EntryClass::Person.to_value()),
357            (Attribute::Class, EntryClass::Account.to_value()),
358            (Attribute::Name, Value::new_iname("testperson")),
359            (
360                Attribute::DisplayName,
361                Value::Utf8("Test Person".to_string())
362            )
363        );
364
365        let create = vec![e];
366
367        run_create_test!(
368            Ok(()),
369            preload,
370            create,
371            None,
372            |qs: &mut QueryServerWriteTransaction| {
373                let cands = qs
374                    .internal_search(filter!(f_eq(
375                        Attribute::Name,
376                        PartialValue::new_iname("testperson")
377                    )))
378                    .expect("Internal search failure");
379                let ue = cands.first().expect("No cand");
380                assert!(ue.attribute_pres(Attribute::Uuid));
381            }
382        );
383    }
384
385    // check unparsable uuid
386    #[test]
387    fn test_pre_create_uuid_invalid() {
388        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
389
390        let e = entry_init!(
391            (Attribute::Class, EntryClass::Person.to_value()),
392            (Attribute::Class, EntryClass::Account.to_value()),
393            (Attribute::Name, Value::new_iname("testperson")),
394            (
395                Attribute::DisplayName,
396                Value::Utf8("Test Person".to_string())
397            ),
398            (Attribute::Uuid, Value::Utf8("xxxxxx".to_string()))
399        );
400
401        let create = vec![e];
402
403        run_create_test!(
404            Err(OperationError::InvalidAttribute(
405                Attribute::Uuid.to_string()
406            )),
407            preload,
408            create,
409            None,
410            |_| {}
411        );
412    }
413
414    // check entry where uuid is empty list
415    #[test]
416    fn test_pre_create_uuid_empty() {
417        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
418
419        let mut e = entry_init!(
420            (Attribute::Class, EntryClass::Person.to_value()),
421            (Attribute::Class, EntryClass::Account.to_value()),
422            (Attribute::Name, Value::new_iname("testperson")),
423            (
424                Attribute::DisplayName,
425                Value::Utf8("Test Person".to_string())
426            ),
427            (
428                Attribute::Uuid,
429                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
430            )
431        );
432
433        let vs = e.get_ava_mut(Attribute::Uuid).unwrap();
434        vs.clear();
435
436        let create = vec![e.clone()];
437
438        run_create_test!(
439            Err(OperationError::Plugin(PluginError::Base(
440                "Uuid format invalid".to_string()
441            ))),
442            preload,
443            create,
444            None,
445            |_| {}
446        );
447    }
448
449    // check create where provided uuid is valid. It should be unchanged.
450    #[test]
451    fn test_pre_create_uuid_valid() {
452        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
453
454        let e = entry_init!(
455            (Attribute::Class, EntryClass::Person.to_value()),
456            (Attribute::Class, EntryClass::Account.to_value()),
457            (Attribute::Name, Value::new_iname("testperson")),
458            (
459                Attribute::DisplayName,
460                Value::Utf8("Test Person".to_string())
461            ),
462            (
463                Attribute::Uuid,
464                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
465            )
466        );
467
468        let create = vec![e];
469
470        run_create_test!(
471            Ok(()),
472            preload,
473            create,
474            None,
475            |qs: &mut QueryServerWriteTransaction| {
476                let cands = qs
477                    .internal_search(filter!(f_eq(
478                        Attribute::Name,
479                        PartialValue::new_iname("testperson")
480                    )))
481                    .expect("Internal search failure");
482                let ue = cands.first().expect("No cand");
483                assert!(ue.attribute_equality(
484                    Attribute::Uuid,
485                    &PartialValue::Uuid(uuid!("79724141-3603-4060-b6bb-35c72772611d"))
486                ));
487            }
488        );
489    }
490
491    #[test]
492    fn test_pre_create_uuid_valid_multi() {
493        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
494
495        let e = entry_init!(
496            (Attribute::Class, EntryClass::Person.to_value()),
497            (Attribute::Class, EntryClass::Account.to_value()),
498            (Attribute::Name, Value::new_iname("testperson")),
499            (
500                Attribute::DisplayName,
501                Value::Utf8("Test Person".to_string())
502            ),
503            (
504                Attribute::Uuid,
505                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611e"))
506            ),
507            (
508                Attribute::Uuid,
509                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
510            )
511        );
512
513        let create = vec![e];
514
515        run_create_test!(
516            Err(OperationError::Plugin(PluginError::Base(
517                "Uuid has multiple values".to_string()
518            ))),
519            preload,
520            create,
521            None,
522            |_| {}
523        );
524    }
525
526    // check create where uuid already exists.
527    // -- check create where uuid is a well-known
528    // This second case is technically handled as well-known
529    // types are created "at startup" so it's not possible
530    // to create one.
531    //
532    // To solidify this, we could make a range of min-max well knowns
533    // to ensure we always have a name space to draw from?
534    #[test]
535    fn test_pre_create_uuid_exist() {
536        let e = entry_init!(
537            (Attribute::Class, EntryClass::Person.to_value()),
538            (Attribute::Class, EntryClass::Account.to_value()),
539            (Attribute::Name, Value::new_iname("testperson")),
540            (
541                Attribute::DisplayName,
542                Value::Utf8("Test Person".to_string())
543            ),
544            (
545                Attribute::Uuid,
546                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
547            )
548        );
549
550        let create = vec![e.clone()];
551        let preload = vec![e];
552
553        run_create_test!(
554            Err(OperationError::Plugin(PluginError::Base(
555                "Uuid duplicate found in database".to_string()
556            ))),
557            preload,
558            create,
559            None,
560            |_| {}
561        );
562    }
563
564    #[test]
565    fn test_pre_create_double_uuid() {
566        // Test adding two entries with the same uuid
567        let preload: Vec<Entry<EntryInit, EntryNew>> = Vec::with_capacity(0);
568
569        let ea = entry_init!(
570            (Attribute::Class, EntryClass::Person.to_value()),
571            (Attribute::Class, EntryClass::Account.to_value()),
572            (Attribute::Name, Value::new_iname("testperson")),
573            (
574                Attribute::DisplayName,
575                Value::Utf8("Test Person".to_string())
576            ),
577            (
578                Attribute::Uuid,
579                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
580            )
581        );
582
583        let eb = ea.clone();
584
585        let create = vec![ea, eb];
586
587        run_create_test!(
588            Err(OperationError::Plugin(PluginError::Base(
589                "Uuid duplicate detected in request".to_string()
590            ))),
591            preload,
592            create,
593            None,
594            |_| {}
595        );
596    }
597
598    // All of these *SHOULD* be blocked?
599    #[test]
600    fn test_modify_uuid_present() {
601        // Add another uuid to a type
602        let ea = entry_init!(
603            (Attribute::Class, EntryClass::Group.to_value()),
604            (Attribute::Name, Value::new_iname("testgroup_a")),
605            (
606                Attribute::Uuid,
607                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
608            )
609        );
610
611        let preload = vec![ea];
612
613        run_modify_test!(
614            Err(OperationError::SystemProtectedAttribute),
615            preload,
616            filter!(f_eq(
617                Attribute::Name,
618                PartialValue::new_iname("testgroup_a")
619            )),
620            ModifyList::new_list(vec![Modify::Present(
621                Attribute::Uuid,
622                Value::from("f15a7219-1d15-44e3-a7b4-bec899c07788")
623            )]),
624            None,
625            |_| {},
626            |_| {}
627        );
628    }
629
630    #[test]
631    fn test_modify_uuid_removed() {
632        // Test attempting to remove a uuid
633        let ea = entry_init!(
634            (Attribute::Class, EntryClass::Group.to_value()),
635            (Attribute::Name, Value::new_iname("testgroup_a")),
636            (
637                Attribute::Uuid,
638                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
639            )
640        );
641
642        let preload = vec![ea];
643
644        run_modify_test!(
645            Err(OperationError::SystemProtectedAttribute),
646            preload,
647            filter!(f_eq(
648                Attribute::Name,
649                PartialValue::new_iname("testgroup_a")
650            )),
651            ModifyList::new_list(vec![Modify::Removed(
652                Attribute::Uuid,
653                PartialValue::Uuid(uuid!("f15a7219-1d15-44e3-a7b4-bec899c07788"))
654            )]),
655            None,
656            |_| {},
657            |_| {}
658        );
659    }
660
661    #[test]
662    fn test_modify_uuid_purged() {
663        // Test attempting to purge uuid
664        let ea = entry_init!(
665            (Attribute::Class, EntryClass::Group.to_value()),
666            (Attribute::Name, Value::new_iname("testgroup_a")),
667            (
668                Attribute::Uuid,
669                Value::Uuid(uuid::uuid!("79724141-3603-4060-b6bb-35c72772611d"))
670            )
671        );
672
673        let preload = vec![ea];
674
675        run_modify_test!(
676            Err(OperationError::SystemProtectedAttribute),
677            preload,
678            filter!(f_eq(
679                Attribute::Name,
680                PartialValue::new_iname("testgroup_a")
681            )),
682            ModifyList::new_list(vec![Modify::Purged(Attribute::Uuid)]),
683            None,
684            |_| {},
685            |_| {}
686        );
687    }
688
689    #[test]
690    fn test_protected_uuid_range() {
691        // Test an external create, it should fail.
692        // Testing internal create is not super needed, due to migrations at start
693        // up testing this every time we run :P
694        let preload = PRELOAD.clone();
695
696        let e = entry_init!(
697            (Attribute::Class, EntryClass::Person.to_value()),
698            (Attribute::Name, Value::new_iname("testperson")),
699            (Attribute::DisplayName, Value::new_iname("testperson")),
700            (
701                Attribute::Uuid,
702                Value::Uuid(uuid::uuid!("00000000-0000-0000-0000-f0f0f0f0f0f0"))
703            )
704        );
705
706        let create = vec![e];
707
708        run_create_test!(
709            Err(OperationError::Plugin(PluginError::Base(
710                "Uuid must not be in protected range".to_string()
711            ))),
712            preload,
713            create,
714            Some(E_TEST_ACCOUNT.clone()),
715            |_| {}
716        );
717    }
718
719    #[test]
720    fn test_protected_uuid_range_2() {
721        // Test an external create, it should fail.
722        // Testing internal create is not super needed, due to migrations at start
723        // up testing this every time we run :P
724        let preload = PRELOAD.clone();
725
726        let e = entry_init!(
727            (Attribute::Class, EntryClass::Person.to_value()),
728            (Attribute::Name, Value::new_iname("testperson")),
729            (Attribute::DisplayName, Value::new_iname("testperson")),
730            (
731                Attribute::Uuid,
732                Value::Uuid(uuid::uuid!("00000000-0000-0000-0000-f0f0f0f0f0f0"))
733            )
734        );
735
736        let create = vec![e];
737
738        run_create_test!(
739            Err(OperationError::Plugin(PluginError::Base(
740                "Uuid must not be in protected range".to_string()
741            ))),
742            preload,
743            create,
744            Some(E_TEST_ACCOUNT.clone()),
745            |_| {}
746        );
747    }
748
749    #[test]
750    fn test_protected_uuid_does_not_exist() {
751        // Test that internal create of "does not exist" will fail.
752        let preload = Vec::with_capacity(0);
753
754        let e = entry_init!(
755            (Attribute::Class, EntryClass::Person.to_value()),
756            (Attribute::Class, EntryClass::System.to_value()),
757            (Attribute::Name, Value::new_iname("testperson")),
758            (Attribute::DisplayName, Value::new_iname("testperson")),
759            (
760                Attribute::Uuid,
761                Value::Uuid(uuid::uuid!("00000000-0000-0000-0000-fffffffffffe"))
762            )
763        );
764
765        let create = vec![e];
766
767        run_create_test!(
768            Err(OperationError::Plugin(PluginError::Base(
769                "UUID_DOES_NOT_EXIST may not exist!".to_string()
770            ))),
771            preload,
772            create,
773            None,
774            |_| {}
775        );
776    }
777}