1use crate::be::IdxKey;
20use crate::prelude::*;
21use crate::valueset::ValueSet;
22use concread::cowcell::*;
23use hashbrown::{HashMap, HashSet};
24use std::collections::BTreeSet;
25use tracing::trace;
26use uuid::Uuid;
27
28pub struct Schema {
45 classes: CowCell<HashMap<AttrString, SchemaClass>>,
46 attributes: CowCell<HashMap<Attribute, SchemaAttribute>>,
47 unique_cache: CowCell<Vec<Attribute>>,
48 ref_cache: CowCell<HashMap<Attribute, SchemaAttribute>>,
49}
50
51pub struct SchemaWriteTransaction<'a> {
55 classes: CowCellWriteTxn<'a, HashMap<AttrString, SchemaClass>>,
56 attributes: CowCellWriteTxn<'a, HashMap<Attribute, SchemaAttribute>>,
57
58 unique_cache: CowCellWriteTxn<'a, Vec<Attribute>>,
59 ref_cache: CowCellWriteTxn<'a, HashMap<Attribute, SchemaAttribute>>,
60}
61
62pub struct SchemaReadTransaction {
64 classes: CowCellReadTxn<HashMap<AttrString, SchemaClass>>,
65 attributes: CowCellReadTxn<HashMap<Attribute, SchemaAttribute>>,
66
67 unique_cache: CowCellReadTxn<Vec<Attribute>>,
68 ref_cache: CowCellReadTxn<HashMap<Attribute, SchemaAttribute>>,
69}
70
71#[derive(Debug, Clone, Copy, Default)]
72pub enum Replicated {
73 #[default]
74 True,
75 False,
76}
77
78impl From<Replicated> for bool {
79 fn from(value: Replicated) -> bool {
80 match value {
81 Replicated::True => true,
82 Replicated::False => false,
83 }
84 }
85}
86
87impl From<bool> for Replicated {
88 fn from(value: bool) -> Self {
89 match value {
90 true => Replicated::True,
91 false => Replicated::False,
92 }
93 }
94}
95
96#[derive(Debug, Clone, Default)]
104pub struct SchemaAttribute {
105 pub name: Attribute,
106 pub uuid: Uuid,
107 pub description: String,
108 pub multivalue: bool,
110 pub unique: bool,
112 pub phantom: bool,
116 pub sync_allowed: bool,
119
120 pub replicated: Replicated,
122 pub indexed: bool,
124 pub syntax: SyntaxType,
126}
127
128impl SchemaAttribute {
129 pub fn try_from(value: &Entry<EntrySealed, EntryCommitted>) -> Result<Self, OperationError> {
130 let uuid = value.get_uuid();
134
135 if !value.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into()) {
137 admin_error!(
138 "class {} not present - {:?}",
139 EntryClass::AttributeType,
140 uuid
141 );
142 return Err(OperationError::InvalidSchemaState(format!(
143 "missing {}",
144 EntryClass::AttributeType
145 )));
146 }
147
148 let name = value
150 .get_ava_single_iutf8(Attribute::AttributeName)
151 .map(|s| s.into())
152 .ok_or_else(|| {
153 admin_error!("missing {} - {:?}", Attribute::AttributeName, uuid);
154 OperationError::InvalidSchemaState("missing attributename".to_string())
155 })?;
156 let description = value
158 .get_ava_single_utf8(Attribute::Description)
159 .map(|s| s.to_string())
160 .ok_or_else(|| {
161 admin_error!("missing {} - {}", Attribute::Description, name);
162 OperationError::InvalidSchemaState("missing description".to_string())
163 })?;
164
165 let multivalue = value
167 .get_ava_single_bool(Attribute::MultiValue)
168 .ok_or_else(|| {
169 admin_error!("missing {} - {}", Attribute::MultiValue, name);
170 OperationError::InvalidSchemaState("missing multivalue".to_string())
171 })?;
172
173 let unique = value
174 .get_ava_single_bool(Attribute::Unique)
175 .ok_or_else(|| {
176 admin_error!("missing {} - {}", Attribute::Unique, name);
177 OperationError::InvalidSchemaState("missing unique".to_string())
178 })?;
179
180 let phantom = value
181 .get_ava_single_bool(Attribute::Phantom)
182 .unwrap_or_default();
183
184 let sync_allowed = value
185 .get_ava_single_bool(Attribute::SyncAllowed)
186 .unwrap_or_default();
187
188 let replicated = value
191 .get_ava_single_bool(Attribute::Replicated)
192 .map(Replicated::from)
193 .unwrap_or_default();
194
195 let indexed = value
196 .get_ava_single_bool(Attribute::Indexed)
197 .unwrap_or_default();
198
199 let syntax = value
201 .get_ava_single_syntax(Attribute::Syntax)
202 .ok_or_else(|| {
203 admin_error!("missing {} - {}", Attribute::Syntax, name);
204 OperationError::InvalidSchemaState(format!("missing {}", Attribute::Syntax))
205 })?;
206
207 trace!(?name, ?indexed);
208
209 Ok(SchemaAttribute {
210 name,
211 uuid,
212 description,
213 multivalue,
214 unique,
215 phantom,
216 sync_allowed,
217 replicated,
218 indexed,
219 syntax,
220 })
221 }
222
223 pub fn validate_partialvalue(
227 &self,
228 a: &Attribute,
229 v: &PartialValue,
230 ) -> Result<(), SchemaError> {
231 let r = match self.syntax {
232 SyntaxType::Boolean => matches!(v, PartialValue::Bool(_)),
233 SyntaxType::SyntaxId => matches!(v, PartialValue::Syntax(_)),
234 SyntaxType::IndexId => matches!(v, PartialValue::Index(_)),
235 SyntaxType::Uuid => matches!(v, PartialValue::Uuid(_)),
236 SyntaxType::ReferenceUuid => matches!(v, PartialValue::Refer(_)),
237 SyntaxType::Utf8StringInsensitive => matches!(v, PartialValue::Iutf8(_)),
238 SyntaxType::Utf8StringIname => matches!(v, PartialValue::Iname(_)),
239 SyntaxType::Utf8String => matches!(v, PartialValue::Utf8(_)),
240 SyntaxType::JsonFilter => matches!(v, PartialValue::JsonFilt(_)),
241 SyntaxType::Credential => matches!(v, PartialValue::Cred(_)),
242 SyntaxType::SecretUtf8String => matches!(v, PartialValue::SecretValue),
243 SyntaxType::SshKey => matches!(v, PartialValue::SshKey(_)),
244 SyntaxType::SecurityPrincipalName => matches!(v, PartialValue::Spn(_, _)),
245 SyntaxType::Uint32 => matches!(v, PartialValue::Uint32(_)),
246 SyntaxType::Cid => matches!(v, PartialValue::Cid(_)),
247 SyntaxType::NsUniqueId => matches!(v, PartialValue::Nsuniqueid(_)),
248 SyntaxType::DateTime => matches!(v, PartialValue::DateTime(_)),
249 SyntaxType::EmailAddress => matches!(v, PartialValue::EmailAddress(_)),
250 SyntaxType::Url => matches!(v, PartialValue::Url(_)),
251 SyntaxType::OauthScope => matches!(v, PartialValue::OauthScope(_)),
252 SyntaxType::OauthScopeMap => matches!(v, PartialValue::Refer(_)),
253 SyntaxType::OauthClaimMap => {
254 matches!(v, PartialValue::Iutf8(_))
255 || matches!(v, PartialValue::Refer(_))
256 || matches!(v, PartialValue::OauthClaimValue(_, _, _))
257 || matches!(v, PartialValue::OauthClaim(_, _))
258 }
259 SyntaxType::PrivateBinary => matches!(v, PartialValue::PrivateBinary),
260 SyntaxType::IntentToken => matches!(v, PartialValue::IntentToken(_)),
261 SyntaxType::Passkey => matches!(v, PartialValue::Passkey(_)),
262 SyntaxType::AttestedPasskey => matches!(v, PartialValue::AttestedPasskey(_)),
263 SyntaxType::Session => matches!(v, PartialValue::Refer(_)),
265 SyntaxType::ApiToken => matches!(v, PartialValue::Refer(_)),
266 SyntaxType::Oauth2Session => matches!(v, PartialValue::Refer(_)),
267 SyntaxType::JwsKeyEs256 => matches!(v, PartialValue::Iutf8(_)),
269 SyntaxType::JwsKeyRs256 => matches!(v, PartialValue::Iutf8(_)),
270 SyntaxType::UiHint => matches!(v, PartialValue::UiHint(_)),
271 SyntaxType::EcKeyPrivate => matches!(v, PartialValue::SecretValue),
272 SyntaxType::TotpSecret => matches!(v, PartialValue::Utf8(_)),
274 SyntaxType::AuditLogString => matches!(v, PartialValue::Utf8(_)),
275 SyntaxType::Image => matches!(v, PartialValue::Utf8(_)),
276 SyntaxType::CredentialType => matches!(v, PartialValue::CredentialType(_)),
277
278 SyntaxType::HexString | SyntaxType::Certificate | SyntaxType::KeyInternal => {
279 matches!(v, PartialValue::HexString(_))
280 }
281
282 SyntaxType::WebauthnAttestationCaList => false,
283 SyntaxType::ApplicationPassword => {
284 matches!(v, PartialValue::Uuid(_)) || matches!(v, PartialValue::Refer(_))
285 }
286 };
287 if r {
288 Ok(())
289 } else {
290 error!(
291 ?a,
292 ?self,
293 ?v,
294 "validate_partialvalue InvalidAttributeSyntax"
295 );
296 Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
297 }
298 }
299
300 pub fn validate_value(&self, a: &Attribute, v: &Value) -> Result<(), SchemaError> {
301 let r = v.validate()
302 && match self.syntax {
303 SyntaxType::Boolean => matches!(v, Value::Bool(_)),
304 SyntaxType::SyntaxId => matches!(v, Value::Syntax(_)),
305 SyntaxType::IndexId => matches!(v, Value::Index(_)),
306 SyntaxType::Uuid => matches!(v, Value::Uuid(_)),
307 SyntaxType::ReferenceUuid => matches!(v, Value::Refer(_)),
308 SyntaxType::Utf8StringInsensitive => matches!(v, Value::Iutf8(_)),
309 SyntaxType::Utf8StringIname => matches!(v, Value::Iname(_)),
310 SyntaxType::Utf8String => matches!(v, Value::Utf8(_)),
311 SyntaxType::JsonFilter => matches!(v, Value::JsonFilt(_)),
312 SyntaxType::Credential => matches!(v, Value::Cred(_, _)),
313 SyntaxType::SecretUtf8String => matches!(v, Value::SecretValue(_)),
314 SyntaxType::SshKey => matches!(v, Value::SshKey(_, _)),
315 SyntaxType::SecurityPrincipalName => matches!(v, Value::Spn(_, _)),
316 SyntaxType::Uint32 => matches!(v, Value::Uint32(_)),
317 SyntaxType::Cid => matches!(v, Value::Cid(_)),
318 SyntaxType::NsUniqueId => matches!(v, Value::Nsuniqueid(_)),
319 SyntaxType::DateTime => matches!(v, Value::DateTime(_)),
320 SyntaxType::EmailAddress => matches!(v, Value::EmailAddress(_, _)),
321 SyntaxType::Url => matches!(v, Value::Url(_)),
322 SyntaxType::OauthScope => matches!(v, Value::OauthScope(_)),
323 SyntaxType::OauthScopeMap => matches!(v, Value::OauthScopeMap(_, _)),
324 SyntaxType::OauthClaimMap => {
325 matches!(v, Value::OauthClaimValue(_, _, _))
326 || matches!(v, Value::OauthClaimMap(_, _))
327 }
328 SyntaxType::PrivateBinary => matches!(v, Value::PrivateBinary(_)),
329 SyntaxType::IntentToken => matches!(v, Value::IntentToken(_, _)),
330 SyntaxType::Passkey => matches!(v, Value::Passkey(_, _, _)),
331 SyntaxType::AttestedPasskey => matches!(v, Value::AttestedPasskey(_, _, _)),
332 SyntaxType::Session => matches!(v, Value::Session(_, _)),
333 SyntaxType::ApiToken => matches!(v, Value::ApiToken(_, _)),
334 SyntaxType::Oauth2Session => matches!(v, Value::Oauth2Session(_, _)),
335 SyntaxType::JwsKeyEs256 => matches!(v, Value::JwsKeyEs256(_)),
336 SyntaxType::JwsKeyRs256 => matches!(v, Value::JwsKeyRs256(_)),
337 SyntaxType::UiHint => matches!(v, Value::UiHint(_)),
338 SyntaxType::TotpSecret => matches!(v, Value::TotpSecret(_, _)),
339 SyntaxType::AuditLogString => matches!(v, Value::Utf8(_)),
340 SyntaxType::EcKeyPrivate => matches!(v, Value::EcKeyPrivate(_)),
341 SyntaxType::Image => matches!(v, Value::Image(_)),
342 SyntaxType::CredentialType => matches!(v, Value::CredentialType(_)),
343 SyntaxType::WebauthnAttestationCaList => {
344 matches!(v, Value::WebauthnAttestationCaList(_))
345 }
346 SyntaxType::KeyInternal => matches!(v, Value::KeyInternal { .. }),
347 SyntaxType::HexString => matches!(v, Value::HexString(_)),
348 SyntaxType::Certificate => matches!(v, Value::Certificate(_)),
349 SyntaxType::ApplicationPassword => matches!(v, Value::ApplicationPassword(..)),
350 };
351 if r {
352 Ok(())
353 } else {
354 error!(
355 ?a,
356 ?self,
357 ?v,
358 "validate_value failure - InvalidAttributeSyntax"
359 );
360 Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
361 }
362 }
363
364 pub fn validate_ava(&self, a: &Attribute, ava: &ValueSet) -> Result<(), SchemaError> {
365 trace!("Checking for valid {:?} -> {:?}", self.name, ava);
366 if !self.multivalue && ava.len() > 1 {
368 admin_error!("Ava len > 1 on single value attribute!");
370 return Err(SchemaError::InvalidAttributeSyntax(a.to_string()));
371 };
372 let valid = self.syntax == ava.syntax();
374 if valid && ava.validate(self) {
375 Ok(())
376 } else {
377 error!(
378 ?a,
379 "validate_ava - InvalidAttributeSyntax for {:?}", self.syntax
380 );
381 Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
382 }
383 }
384}
385
386#[derive(Debug, Clone, Default)]
404pub struct SchemaClass {
405 pub name: AttrString,
406 pub uuid: Uuid,
407 pub description: String,
408 pub sync_allowed: bool,
409 pub systemmay: Vec<Attribute>,
411 pub may: Vec<Attribute>,
412 pub systemmust: Vec<Attribute>,
413 pub must: Vec<Attribute>,
414 pub systemsupplements: Vec<AttrString>,
419 pub supplements: Vec<AttrString>,
420 pub systemexcludes: Vec<AttrString>,
422 pub excludes: Vec<AttrString>,
423}
424
425impl SchemaClass {
426 pub fn try_from(value: &Entry<EntrySealed, EntryCommitted>) -> Result<Self, OperationError> {
427 let uuid = value.get_uuid();
429 if !value.attribute_equality(Attribute::Class, &EntryClass::ClassType.into()) {
431 error!("class classtype not present - {:?}", uuid);
432 return Err(OperationError::InvalidSchemaState(
433 "missing classtype".to_string(),
434 ));
435 }
436
437 let name = value
439 .get_ava_single_iutf8(Attribute::ClassName)
440 .map(AttrString::from)
441 .ok_or_else(|| {
442 error!("missing {} - {:?}", Attribute::ClassName, uuid);
443 OperationError::InvalidSchemaState(format!("missing {}", Attribute::ClassName))
444 })?;
445
446 let description = value
448 .get_ava_single_utf8(Attribute::Description)
449 .map(String::from)
450 .ok_or_else(|| {
451 error!("missing {} - {}", Attribute::Description, name);
452 OperationError::InvalidSchemaState(format!("missing {}", Attribute::Description))
453 })?;
454
455 let sync_allowed = value
456 .get_ava_single_bool(Attribute::SyncAllowed)
457 .unwrap_or(false);
458
459 let systemmay = value
461 .get_ava_iter_iutf8(Attribute::SystemMay)
462 .into_iter()
463 .flat_map(|iter| iter.map(Attribute::from))
464 .collect();
465 let systemmust = value
466 .get_ava_iter_iutf8(Attribute::SystemMust)
467 .into_iter()
468 .flat_map(|iter| iter.map(Attribute::from))
469 .collect();
470 let may = value
471 .get_ava_iter_iutf8(Attribute::May)
472 .into_iter()
473 .flat_map(|iter| iter.map(Attribute::from))
474 .collect();
475 let must = value
476 .get_ava_iter_iutf8(Attribute::Must)
477 .into_iter()
478 .flat_map(|iter| iter.map(Attribute::from))
479 .collect();
480
481 let systemsupplements = value
482 .get_ava_iter_iutf8(Attribute::SystemSupplements)
483 .map(|i| i.map(|v| v.into()).collect())
484 .unwrap_or_default();
485 let supplements = value
486 .get_ava_iter_iutf8(Attribute::Supplements)
487 .map(|i| i.map(|v| v.into()).collect())
488 .unwrap_or_default();
489 let systemexcludes = value
490 .get_ava_iter_iutf8(Attribute::SystemExcludes)
491 .map(|i| i.map(|v| v.into()).collect())
492 .unwrap_or_default();
493 let excludes = value
494 .get_ava_iter_iutf8(Attribute::Excludes)
495 .map(|i| i.map(|v| v.into()).collect())
496 .unwrap_or_default();
497
498 Ok(SchemaClass {
499 name,
500 uuid,
501 description,
502 sync_allowed,
503 systemmay,
504 may,
505 systemmust,
506 must,
507 systemsupplements,
508 supplements,
509 systemexcludes,
510 excludes,
511 })
512 }
513
514 pub fn may_iter(&self) -> impl Iterator<Item = &Attribute> {
517 self.systemmay
518 .iter()
519 .chain(self.may.iter())
520 .chain(self.systemmust.iter())
521 .chain(self.must.iter())
522 }
523}
524
525pub trait SchemaTransaction {
526 fn get_classes(&self) -> &HashMap<AttrString, SchemaClass>;
527 fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute>;
528
529 fn get_attributes_unique(&self) -> &Vec<Attribute>;
530 fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute>;
531
532 fn validate(&self) -> Vec<Result<(), ConsistencyError>> {
533 let mut res = Vec::with_capacity(0);
534
535 let class_snapshot = self.get_classes();
536 let attribute_snapshot = self.get_attributes();
537
538 let mut unique_uuid_set = HashSet::new();
543 class_snapshot
544 .values()
545 .map(|class| &class.uuid)
546 .chain(attribute_snapshot.values().map(|attr| &attr.uuid))
547 .for_each(|uuid| {
548 if !unique_uuid_set.insert(uuid) {
550 res.push(Err(ConsistencyError::SchemaUuidNotUnique(*uuid)))
551 }
552 });
553
554 class_snapshot.values().for_each(|class| {
555 class
557 .systemmay
558 .iter()
559 .chain(class.may.iter())
560 .chain(class.systemmust.iter())
561 .chain(class.must.iter())
562 .for_each(|a| {
563 match attribute_snapshot.get(a) {
564 Some(attr) => {
565 if attr.phantom {
567 res.push(Err(ConsistencyError::SchemaClassPhantomAttribute(
568 class.name.to_string(),
569 a.to_string(),
570 )))
571 }
572 }
573 None => {
574 res.push(Err(ConsistencyError::SchemaClassMissingAttribute(
576 class.name.to_string(),
577 a.to_string(),
578 )))
579 }
580 }
581 })
582 }); res
584 }
585
586 fn is_replicated(&self, attr: &Attribute) -> bool {
587 match self.get_attributes().get(attr) {
588 Some(a_schema) => {
589 a_schema.replicated.into() && !a_schema.phantom
592 }
593 None => {
594 warn!(
595 "Attribute {} was not found in schema during replication request",
596 attr
597 );
598 false
599 }
600 }
601 }
602
603 fn is_multivalue(&self, attr: &Attribute) -> Result<bool, SchemaError> {
604 match self.get_attributes().get(attr) {
605 Some(a_schema) => Ok(a_schema.multivalue),
606 None => {
607 Err(SchemaError::InvalidAttribute(attr.to_string()))
609 }
610 }
611 }
612
613 fn normalise_attr_if_exists(&self, an: &str) -> Option<Attribute> {
614 let attr = Attribute::from(an);
615 if self.get_attributes().contains_key(&attr) {
616 Some(attr)
617 } else {
618 None
619 }
620 }
621
622 fn query_attrs_difference(
623 &self,
624 prev_class: &BTreeSet<&str>,
625 new_class: &BTreeSet<&str>,
626 ) -> Result<(BTreeSet<&str>, BTreeSet<&str>), SchemaError> {
627 let schema_classes = self.get_classes();
628
629 let mut invalid_classes = Vec::with_capacity(0);
630
631 let prev_attrs: BTreeSet<&str> = prev_class
632 .iter()
633 .filter_map(|cls| match schema_classes.get(*cls) {
634 Some(x) => Some(x.may_iter()),
635 None => {
636 admin_debug!("invalid class: {:?}", cls);
637 invalid_classes.push(cls.to_string());
638 None
639 }
640 })
641 .flatten()
643 .map(|s| s.as_str())
644 .collect();
645
646 if !invalid_classes.is_empty() {
647 return Err(SchemaError::InvalidClass(invalid_classes));
648 };
649
650 let new_attrs: BTreeSet<&str> = new_class
651 .iter()
652 .filter_map(|cls| match schema_classes.get(*cls) {
653 Some(x) => Some(x.may_iter()),
654 None => {
655 admin_debug!("invalid class: {:?}", cls);
656 invalid_classes.push(cls.to_string());
657 None
658 }
659 })
660 .flatten()
662 .map(|s| s.as_str())
663 .collect();
664
665 if !invalid_classes.is_empty() {
666 return Err(SchemaError::InvalidClass(invalid_classes));
667 };
668
669 let removed = prev_attrs.difference(&new_attrs).copied().collect();
670 let added = new_attrs.difference(&prev_attrs).copied().collect();
671
672 Ok((added, removed))
673 }
674}
675
676impl SchemaWriteTransaction<'_> {
677 pub fn commit(self) -> Result<(), OperationError> {
683 let SchemaWriteTransaction {
684 classes,
685 attributes,
686 unique_cache,
687 ref_cache,
688 } = self;
689
690 unique_cache.commit();
691 ref_cache.commit();
692 classes.commit();
693 attributes.commit();
694 Ok(())
695 }
696
697 pub fn update_attributes(
698 &mut self,
699 attributetypes: Vec<SchemaAttribute>,
700 ) -> Result<(), OperationError> {
701 self.attributes.clear();
703
704 self.unique_cache.clear();
705 self.ref_cache.clear();
706 attributetypes.into_iter().for_each(|a| {
710 if a.syntax == SyntaxType::ReferenceUuid ||
712 a.syntax == SyntaxType::OauthScopeMap ||
713 a.syntax == SyntaxType::OauthClaimMap ||
714 a.syntax == SyntaxType::Oauth2Session ||
716 a.syntax == SyntaxType::ApplicationPassword
718 {
721 self.ref_cache.insert(a.name.clone(), a.clone());
722 }
723 if a.unique {
724 self.unique_cache.push(a.name.clone());
725 }
726 self.attributes.insert(a.name.clone(), a);
728 });
729
730 Ok(())
731 }
732
733 pub fn update_classes(&mut self, classtypes: Vec<SchemaClass>) -> Result<(), OperationError> {
734 self.classes.clear();
736 classtypes.into_iter().for_each(|a| {
740 self.classes.insert(a.name.clone(), a);
741 });
742 Ok(())
743 }
744
745 pub fn to_entries(&self) -> Vec<Entry<EntryInit, EntryNew>> {
746 let r: Vec<_> = self
747 .attributes
748 .values()
749 .map(Entry::<EntryInit, EntryNew>::from)
750 .chain(
751 self.classes
752 .values()
753 .map(Entry::<EntryInit, EntryNew>::from),
754 )
755 .collect();
756 r
757 }
758
759 pub fn reload_idxmeta(&self) -> Vec<IdxKey> {
760 self.get_attributes()
761 .values()
762 .flat_map(|a| {
763 if a.indexed || a.unique {
765 a.syntax.index_types()
766 } else {
767 &[]
768 }
769 .iter()
770 .map(move |itype: &IndexType| IdxKey {
771 attr: a.name.clone(),
772 itype: *itype,
773 })
774 })
775 .collect()
776 }
777
778 #[instrument(level = "debug", name = "schema::generate_in_memory", skip_all)]
779 pub fn generate_in_memory(&mut self) -> Result<(), OperationError> {
780 self.classes.clear();
782 self.attributes.clear();
783 self.attributes.insert(
786 Attribute::Class,
787 SchemaAttribute {
788 name: Attribute::Class,
789 uuid: UUID_SCHEMA_ATTR_CLASS,
790 description: String::from("The set of classes defining an object"),
791 multivalue: true,
792 unique: false,
793 phantom: false,
794 sync_allowed: false,
795 replicated: Replicated::True,
796 indexed: true,
797 syntax: SyntaxType::Utf8StringInsensitive,
798 },
799 );
800 self.attributes.insert(
801 Attribute::Uuid,
802 SchemaAttribute {
803 name: Attribute::Uuid,
804 uuid: UUID_SCHEMA_ATTR_UUID,
805 description: String::from("The universal unique id of the object"),
806 multivalue: false,
807 unique: false,
810 phantom: false,
811 sync_allowed: false,
812 replicated: Replicated::True,
813 indexed: true,
814 syntax: SyntaxType::Uuid,
815 },
816 );
817 self.attributes.insert(
818 Attribute::SourceUuid,
819 SchemaAttribute {
820 name: Attribute::SourceUuid,
821 uuid: UUID_SCHEMA_ATTR_SOURCE_UUID,
822 description: String::from(
823 "The universal unique id of the source object(s) which conflicted with this entry",
824 ),
825 multivalue: true,
826 unique: false,
829 phantom: false,
830 sync_allowed: false,
831 replicated: Replicated::True,
832 indexed: true,
833 syntax: SyntaxType::Uuid,
834 },
835 );
836 self.attributes.insert(
837 Attribute::CreatedAtCid,
838 SchemaAttribute {
839 name: Attribute::CreatedAtCid,
840 uuid: UUID_SCHEMA_ATTR_CREATED_AT_CID,
841 description: String::from("The cid when this entry was created"),
842 multivalue: false,
843 unique: false,
846 phantom: false,
847 sync_allowed: false,
848 replicated: Replicated::False,
849 indexed: false,
850 syntax: SyntaxType::Cid,
851 },
852 );
853 self.attributes.insert(
854 Attribute::LastModifiedCid,
855 SchemaAttribute {
856 name: Attribute::LastModifiedCid,
857 uuid: UUID_SCHEMA_ATTR_LAST_MOD_CID,
858 description: String::from("The cid of the last change to this object"),
859 multivalue: false,
860 unique: false,
863 phantom: false,
864 sync_allowed: false,
865 replicated: Replicated::False,
866 indexed: false,
867 syntax: SyntaxType::Cid,
868 },
869 );
870 self.attributes.insert(
871 Attribute::Name,
872 SchemaAttribute {
873 name: Attribute::Name,
874 uuid: UUID_SCHEMA_ATTR_NAME,
875 description: String::from("The shortform name of an object"),
876 multivalue: false,
877 unique: true,
878 phantom: false,
879 sync_allowed: true,
880 replicated: Replicated::True,
881 indexed: true,
882 syntax: SyntaxType::Utf8StringIname,
883 },
884 );
885 self.attributes.insert(
886 Attribute::Spn,
887 SchemaAttribute {
888 name: Attribute::Spn,
889 uuid: UUID_SCHEMA_ATTR_SPN,
890 description: String::from(
891 "The Security Principal Name of an object, unique across all domain trusts",
892 ),
893 multivalue: false,
894 unique: true,
895 phantom: false,
896 sync_allowed: false,
897 replicated: Replicated::True,
898 indexed: true,
899 syntax: SyntaxType::SecurityPrincipalName,
900 },
901 );
902 self.attributes.insert(
903 Attribute::AttributeName,
904 SchemaAttribute {
905 name: Attribute::AttributeName,
906 uuid: UUID_SCHEMA_ATTR_ATTRIBUTENAME,
907 description: String::from("The name of a schema attribute"),
908 multivalue: false,
909 unique: true,
910 phantom: false,
911 sync_allowed: false,
912 replicated: Replicated::True,
913 indexed: true,
914 syntax: SyntaxType::Utf8StringInsensitive,
915 },
916 );
917 self.attributes.insert(
918 Attribute::ClassName,
919 SchemaAttribute {
920 name: Attribute::ClassName,
921 uuid: UUID_SCHEMA_ATTR_CLASSNAME,
922 description: String::from("The name of a schema class"),
923 multivalue: false,
924 unique: true,
925 phantom: false,
926 sync_allowed: false,
927 replicated: Replicated::True,
928 indexed: true,
929 syntax: SyntaxType::Utf8StringInsensitive,
930 },
931 );
932 self.attributes.insert(
933 Attribute::Description,
934 SchemaAttribute {
935 name: Attribute::Description,
936 uuid: UUID_SCHEMA_ATTR_DESCRIPTION,
937 description: String::from("A description of an attribute, object or class"),
938 multivalue: false,
939 unique: false,
940 phantom: false,
941 sync_allowed: true,
942 replicated: Replicated::True,
943 indexed: false,
944 syntax: SyntaxType::Utf8String,
945 },
946 );
947 self.attributes.insert(Attribute::MultiValue, SchemaAttribute {
948 name: Attribute::MultiValue,
949 uuid: UUID_SCHEMA_ATTR_MULTIVALUE,
950 description: String::from("If true, this attribute is able to store multiple values rather than just a single value."),
951 multivalue: false,
952 unique: false,
953 phantom: false,
954 sync_allowed: false,
955 replicated: Replicated::True,
956 indexed: false,
957 syntax: SyntaxType::Boolean,
958 });
959 self.attributes.insert(Attribute::Phantom, SchemaAttribute {
960 name: Attribute::Phantom,
961 uuid: UUID_SCHEMA_ATTR_PHANTOM,
962 description: String::from("If true, this attribute must NOT be present in any may/must sets of a class as. This represents generated attributes."),
963 multivalue: false,
964 unique: false,
965 phantom: false,
966 sync_allowed: false,
967 replicated: Replicated::True,
968 indexed: false,
969 syntax: SyntaxType::Boolean,
970 });
971 self.attributes.insert(Attribute::SyncAllowed, SchemaAttribute {
972 name: Attribute::SyncAllowed,
973 uuid: UUID_SCHEMA_ATTR_SYNC_ALLOWED,
974 description: String::from("If true, this attribute or class can by synchronised by an external scim import"),
975 multivalue: false,
976 unique: false,
977 phantom: false,
978 sync_allowed: false,
979 replicated: Replicated::True,
980 indexed: false,
981 syntax: SyntaxType::Boolean,
982 });
983 self.attributes.insert(Attribute::Replicated, SchemaAttribute {
984 name: Attribute::Replicated,
985 uuid: UUID_SCHEMA_ATTR_REPLICATED,
986 description: String::from("If true, this attribute or class can by replicated between nodes in the topology"),
987 multivalue: false,
988 unique: false,
989 phantom: false,
990 sync_allowed: false,
991 replicated: Replicated::True,
992 indexed: false,
993 syntax: SyntaxType::Boolean,
994 });
995 self.attributes.insert(
996 Attribute::Unique,
997 SchemaAttribute {
998 name: Attribute::Unique,
999 uuid: UUID_SCHEMA_ATTR_UNIQUE,
1000 description: String::from(
1001 "If true, this attribute must store a unique value through out the database.",
1002 ),
1003 multivalue: false,
1004 unique: false,
1005 phantom: false,
1006 sync_allowed: false,
1007 replicated: Replicated::True,
1008 indexed: false,
1009 syntax: SyntaxType::Boolean,
1010 },
1011 );
1012 self.attributes.insert(
1013 Attribute::Index,
1014 SchemaAttribute {
1015 name: Attribute::Index,
1016 uuid: UUID_SCHEMA_ATTR_INDEX,
1017 description: String::from(
1018 "Describe the indexes to apply to instances of this attribute.",
1019 ),
1020 multivalue: true,
1021 unique: false,
1022 phantom: false,
1023 sync_allowed: false,
1024 replicated: Replicated::True,
1025 indexed: false,
1026 syntax: SyntaxType::IndexId,
1027 },
1028 );
1029 self.attributes.insert(
1030 Attribute::Indexed,
1031 SchemaAttribute {
1032 name: Attribute::Indexed,
1033 uuid: UUID_SCHEMA_ATTR_INDEXED,
1034 description: String::from(
1035 "A boolean stating if this attribute will be indexed according to its syntax rules."
1036 ),
1037 multivalue: false,
1038 unique: false,
1039 phantom: false,
1040 sync_allowed: false,
1041 replicated: Replicated::True,
1042 indexed: false,
1043 syntax: SyntaxType::Boolean,
1044 },
1045 );
1046 self.attributes.insert(
1047 Attribute::Syntax,
1048 SchemaAttribute {
1049 name: Attribute::Syntax,
1050 uuid: UUID_SCHEMA_ATTR_SYNTAX,
1051 description: String::from(
1052 "Describe the syntax of this attribute. This affects indexing and sorting.",
1053 ),
1054 multivalue: false,
1055 unique: false,
1056 phantom: false,
1057 sync_allowed: false,
1058 replicated: Replicated::True,
1059 indexed: false,
1060 syntax: SyntaxType::SyntaxId,
1061 },
1062 );
1063 self.attributes.insert(
1064 Attribute::SystemMay,
1065 SchemaAttribute {
1066 name: Attribute::SystemMay,
1067 uuid: UUID_SCHEMA_ATTR_SYSTEMMAY,
1068 description: String::from(
1069 "A list of system provided optional attributes this class can store.",
1070 ),
1071 multivalue: true,
1072 unique: false,
1073 phantom: false,
1074 sync_allowed: false,
1075 replicated: Replicated::True,
1076 indexed: false,
1077 syntax: SyntaxType::Utf8StringInsensitive,
1078 },
1079 );
1080 self.attributes.insert(
1081 Attribute::May,
1082 SchemaAttribute {
1083 name: Attribute::May,
1084 uuid: UUID_SCHEMA_ATTR_MAY,
1085 description: String::from(
1086 "A user modifiable list of optional attributes this class can store.",
1087 ),
1088 multivalue: true,
1089 unique: false,
1090 phantom: false,
1091 sync_allowed: false,
1092 replicated: Replicated::True,
1093 indexed: false,
1094 syntax: SyntaxType::Utf8StringInsensitive,
1095 },
1096 );
1097 self.attributes.insert(
1098 Attribute::SystemMust,
1099 SchemaAttribute {
1100 name: Attribute::SystemMust,
1101 uuid: UUID_SCHEMA_ATTR_SYSTEMMUST,
1102 description: String::from(
1103 "A list of system provided required attributes this class must store.",
1104 ),
1105 multivalue: true,
1106 unique: false,
1107 phantom: false,
1108 sync_allowed: false,
1109 replicated: Replicated::True,
1110 indexed: false,
1111 syntax: SyntaxType::Utf8StringInsensitive,
1112 },
1113 );
1114 self.attributes.insert(
1115 Attribute::Must,
1116 SchemaAttribute {
1117 name: Attribute::Must,
1118 uuid: UUID_SCHEMA_ATTR_MUST,
1119 description: String::from(
1120 "A user modifiable list of required attributes this class must store.",
1121 ),
1122 multivalue: true,
1123 unique: false,
1124 phantom: false,
1125 sync_allowed: false,
1126 replicated: Replicated::True,
1127 indexed: false,
1128 syntax: SyntaxType::Utf8StringInsensitive,
1129 },
1130 );
1131 self.attributes.insert(
1132 Attribute::SystemSupplements,
1133 SchemaAttribute {
1134 name: Attribute::SystemSupplements,
1135 uuid: UUID_SCHEMA_ATTR_SYSTEMSUPPLEMENTS,
1136 description: String::from(
1137 "A set of classes that this type supplements, where this class can't exist without their presence.",
1138 ),
1139 multivalue: true,
1140 unique: false,
1141 phantom: false,
1142 sync_allowed: false,
1143 replicated: Replicated::True,
1144 indexed: false,
1145 syntax: SyntaxType::Utf8StringInsensitive,
1146 },
1147 );
1148 self.attributes.insert(
1149 Attribute::Supplements,
1150 SchemaAttribute {
1151 name: Attribute::Supplements,
1152 uuid: UUID_SCHEMA_ATTR_SUPPLEMENTS,
1153 description: String::from(
1154 "A set of user modifiable classes, where this determines that at least one other type must supplement this type",
1155 ),
1156 multivalue: true,
1157 unique: false,
1158 phantom: false,
1159 sync_allowed: false,
1160 replicated: Replicated::True,
1161 indexed: false,
1162 syntax: SyntaxType::Utf8StringInsensitive,
1163 },
1164 );
1165 self.attributes.insert(
1166 Attribute::SystemExcludes,
1167 SchemaAttribute {
1168 name: Attribute::SystemExcludes,
1169 uuid: UUID_SCHEMA_ATTR_SYSTEMEXCLUDES,
1170 description: String::from(
1171 "A set of classes that are denied presence in connection to this class",
1172 ),
1173 multivalue: true,
1174 unique: false,
1175 phantom: false,
1176 sync_allowed: false,
1177 replicated: Replicated::True,
1178 indexed: false,
1179 syntax: SyntaxType::Utf8StringInsensitive,
1180 },
1181 );
1182 self.attributes.insert(
1183 Attribute::Excludes,
1184 SchemaAttribute {
1185 name: Attribute::Excludes,
1186 uuid: UUID_SCHEMA_ATTR_EXCLUDES,
1187 description: String::from(
1188 "A set of user modifiable classes that are denied presence in connection to this class",
1189 ),
1190 multivalue: true,
1191 unique: false,
1192 phantom: false,
1193 sync_allowed: false,
1194 replicated: Replicated::True,
1195 indexed: false,
1196 syntax: SyntaxType::Utf8StringInsensitive,
1197 },
1198 );
1199
1200 self.attributes.insert(
1203 Attribute::AcpEnable,
1204 SchemaAttribute {
1205 name: Attribute::AcpEnable,
1206 uuid: UUID_SCHEMA_ATTR_ACP_ENABLE,
1207 description: String::from("A flag to determine if this ACP is active for application. True is enabled, and enforced. False is checked but not enforced."),
1208 multivalue: false,
1209 unique: false,
1210 phantom: false,
1211 sync_allowed: false,
1212 replicated: Replicated::True,
1213 indexed: true,
1214 syntax: SyntaxType::Boolean,
1215 },
1216 );
1217
1218 self.attributes.insert(
1219 Attribute::AcpReceiver,
1220 SchemaAttribute {
1221 name: Attribute::AcpReceiver,
1222 uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER,
1223 description: String::from(
1224 "Who the ACP applies to, constraining or allowing operations.",
1225 ),
1226 multivalue: false,
1227 unique: false,
1228 phantom: false,
1229 sync_allowed: false,
1230 replicated: Replicated::True,
1231 indexed: true,
1232 syntax: SyntaxType::JsonFilter,
1233 },
1234 );
1235 self.attributes.insert(
1236 Attribute::AcpReceiverGroup,
1237 SchemaAttribute {
1238 name: Attribute::AcpReceiverGroup,
1239 uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER_GROUP,
1240 description: String::from(
1241 "The group that receives this access control to allow access",
1242 ),
1243 multivalue: true,
1244 unique: false,
1245 phantom: false,
1246 sync_allowed: false,
1247 replicated: Replicated::True,
1248 indexed: true,
1249 syntax: SyntaxType::ReferenceUuid,
1250 },
1251 );
1252
1253 self.attributes.insert(
1254 Attribute::AcpTargetScope,
1255 SchemaAttribute {
1256 name: Attribute::AcpTargetScope,
1257 uuid: UUID_SCHEMA_ATTR_ACP_TARGETSCOPE,
1258 description: String::from(
1259 "The effective targets of the ACP, e.g. what will be acted upon.",
1260 ),
1261 multivalue: false,
1262 unique: false,
1263 phantom: false,
1264 sync_allowed: false,
1265 replicated: Replicated::True,
1266 indexed: true,
1267 syntax: SyntaxType::JsonFilter,
1268 },
1269 );
1270 self.attributes.insert(
1271 Attribute::AcpSearchAttr,
1272 SchemaAttribute {
1273 name: Attribute::AcpSearchAttr,
1274 uuid: UUID_SCHEMA_ATTR_ACP_SEARCH_ATTR,
1275 description: String::from(
1276 "The attributes that may be viewed or searched by the receiver on targetscope.",
1277 ),
1278 multivalue: true,
1279 unique: false,
1280 phantom: false,
1281 sync_allowed: false,
1282 replicated: Replicated::True,
1283 indexed: true,
1284 syntax: SyntaxType::Utf8StringInsensitive,
1285 },
1286 );
1287 self.attributes.insert(
1288 Attribute::AcpCreateClass,
1289 SchemaAttribute {
1290 name: Attribute::AcpCreateClass,
1291 uuid: UUID_SCHEMA_ATTR_ACP_CREATE_CLASS,
1292 description: String::from("The set of classes that can be created on a new entry."),
1293 multivalue: true,
1294 unique: false,
1295 phantom: false,
1296 sync_allowed: false,
1297 replicated: Replicated::True,
1298 indexed: true,
1299 syntax: SyntaxType::Utf8StringInsensitive,
1300 },
1301 );
1302 self.attributes.insert(
1303 Attribute::AcpCreateAttr,
1304 SchemaAttribute {
1305 name: Attribute::AcpCreateAttr,
1306 uuid: UUID_SCHEMA_ATTR_ACP_CREATE_ATTR,
1307 description: String::from(
1308 "The set of attribute types that can be created on an entry.",
1309 ),
1310 multivalue: true,
1311 unique: false,
1312 phantom: false,
1313 sync_allowed: false,
1314 replicated: Replicated::True,
1315 indexed: true,
1316 syntax: SyntaxType::Utf8StringInsensitive,
1317 },
1318 );
1319
1320 self.attributes.insert(
1321 Attribute::AcpModifyRemovedAttr,
1322 SchemaAttribute {
1323 name: Attribute::AcpModifyRemovedAttr,
1324 uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVEDATTR,
1325 description: String::from(
1326 "The set of attribute types that could be removed or purged in a modification.",
1327 ),
1328 multivalue: true,
1329 unique: false,
1330 phantom: false,
1331 sync_allowed: false,
1332 replicated: Replicated::True,
1333 indexed: true,
1334 syntax: SyntaxType::Utf8StringInsensitive,
1335 },
1336 );
1337 self.attributes.insert(
1338 Attribute::AcpModifyPresentAttr,
1339 SchemaAttribute {
1340 name: Attribute::AcpModifyPresentAttr,
1341 uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENTATTR,
1342 description: String::from(
1343 "The set of attribute types that could be added or asserted in a modification.",
1344 ),
1345 multivalue: true,
1346 unique: false,
1347 phantom: false,
1348 sync_allowed: false,
1349 replicated: Replicated::True,
1350 indexed: true,
1351 syntax: SyntaxType::Utf8StringInsensitive,
1352 },
1353 );
1354 self.attributes.insert(
1355 Attribute::AcpModifyClass,
1356 SchemaAttribute {
1357 name: Attribute::AcpModifyClass,
1358 uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_CLASS,
1359 description: String::from("The set of class values that could be asserted or added to an entry. Only applies to modify::present operations on class."),
1360 multivalue: true,
1361 unique: false,
1362 phantom: false,
1363 sync_allowed: false,
1364 replicated: Replicated::True,
1365 indexed: true,
1366 syntax: SyntaxType::Utf8StringInsensitive,
1367 },
1368 );
1369 self.attributes.insert(
1370 Attribute::AcpModifyPresentClass,
1371 SchemaAttribute {
1372 name: Attribute::AcpModifyPresentClass,
1373 uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENT_CLASS,
1374 description: String::from("The set of class values that could be asserted or added to an entry. Only applies to modify::present operations on class."),
1375 multivalue: true,
1376 unique: false,
1377 phantom: false,
1378 sync_allowed: false,
1379 replicated: Replicated::True,
1380 indexed: false,
1381 syntax: SyntaxType::Utf8StringInsensitive,
1382 },
1383 );
1384 self.attributes.insert(
1385 Attribute::AcpModifyRemoveClass,
1386 SchemaAttribute {
1387 name: Attribute::AcpModifyRemoveClass,
1388 uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVE_CLASS,
1389 description: String::from("The set of class values that could be asserted or added to an entry. Only applies to modify::remove operations on class."),
1390 multivalue: true,
1391 unique: false,
1392 phantom: false,
1393 sync_allowed: false,
1394 replicated: Replicated::True,
1395 indexed: false,
1396 syntax: SyntaxType::Utf8StringInsensitive,
1397 },
1398 );
1399 self.attributes.insert(
1400 Attribute::EntryManagedBy,
1401 SchemaAttribute {
1402 name: Attribute::EntryManagedBy,
1403 uuid: UUID_SCHEMA_ATTR_ENTRY_MANAGED_BY,
1404 description: String::from(
1405 "A reference to a group that has access to manage the content of this entry.",
1406 ),
1407 multivalue: false,
1408 unique: false,
1409 phantom: false,
1410 sync_allowed: false,
1411 replicated: Replicated::True,
1412 indexed: true,
1413 syntax: SyntaxType::ReferenceUuid,
1414 },
1415 );
1416 self.attributes.insert(
1418 Attribute::MemberOf,
1419 SchemaAttribute {
1420 name: Attribute::MemberOf,
1421 uuid: UUID_SCHEMA_ATTR_MEMBEROF,
1422 description: String::from("reverse group membership of the object"),
1423 multivalue: true,
1424 unique: false,
1425 phantom: false,
1426 sync_allowed: false,
1427 replicated: Replicated::False,
1428 indexed: true,
1429 syntax: SyntaxType::ReferenceUuid,
1430 },
1431 );
1432 self.attributes.insert(
1433 Attribute::DirectMemberOf,
1434 SchemaAttribute {
1435 name: Attribute::DirectMemberOf,
1436 uuid: UUID_SCHEMA_ATTR_DIRECTMEMBEROF,
1437 description: String::from("reverse direct group membership of the object"),
1438 multivalue: true,
1439 unique: false,
1440 phantom: false,
1441 sync_allowed: false,
1442 replicated: Replicated::False,
1443 indexed: true,
1444 syntax: SyntaxType::ReferenceUuid,
1445 },
1446 );
1447 self.attributes.insert(
1448 Attribute::RecycledDirectMemberOf,
1449 SchemaAttribute {
1450 name: Attribute::RecycledDirectMemberOf,
1451 uuid: UUID_SCHEMA_ATTR_RECYCLEDDIRECTMEMBEROF,
1452 description: String::from("recycled reverse direct group membership of the object to assist in revive operations."),
1453 multivalue: true,
1454 unique: false,
1455 phantom: false,
1456 sync_allowed: false,
1457 replicated: Replicated::True,
1462 indexed: true,
1463 syntax: SyntaxType::ReferenceUuid,
1464 },
1465 );
1466 self.attributes.insert(
1467 Attribute::Member,
1468 SchemaAttribute {
1469 name: Attribute::Member,
1470 uuid: UUID_SCHEMA_ATTR_MEMBER,
1471 description: String::from("List of members of the group"),
1472 multivalue: true,
1473 unique: false,
1474 phantom: false,
1475 sync_allowed: true,
1476 replicated: Replicated::True,
1477 indexed: true,
1478 syntax: SyntaxType::ReferenceUuid,
1479 },
1480 );
1481 self.attributes.insert(
1482 Attribute::DynMember,
1483 SchemaAttribute {
1484 name: Attribute::DynMember,
1485 uuid: UUID_SCHEMA_ATTR_DYNMEMBER,
1486 description: String::from("List of dynamic members of the group"),
1487 multivalue: true,
1488 unique: false,
1489 phantom: false,
1490 sync_allowed: true,
1491 replicated: Replicated::False,
1492 indexed: true,
1493 syntax: SyntaxType::ReferenceUuid,
1494 },
1495 );
1496 self.attributes.insert(
1498 Attribute::Version,
1499 SchemaAttribute {
1500 name: Attribute::Version,
1501 uuid: UUID_SCHEMA_ATTR_VERSION,
1502 description: String::from(
1503 "The systems internal migration version for provided objects",
1504 ),
1505 multivalue: false,
1506 unique: false,
1507 phantom: false,
1508 sync_allowed: false,
1509 replicated: Replicated::True,
1510 indexed: false,
1511 syntax: SyntaxType::Uint32,
1512 },
1513 );
1514 self.attributes.insert(
1516 Attribute::Domain,
1517 SchemaAttribute {
1518 name: Attribute::Domain,
1519 uuid: UUID_SCHEMA_ATTR_DOMAIN,
1520 description: String::from("A DNS Domain name entry."),
1521 multivalue: true,
1522 unique: false,
1523 phantom: false,
1524 sync_allowed: false,
1525 replicated: Replicated::True,
1526 indexed: true,
1527 syntax: SyntaxType::Utf8StringIname,
1528 },
1529 );
1530 self.attributes.insert(
1531 Attribute::Claim,
1532 SchemaAttribute {
1533 name: Attribute::Claim,
1534 uuid: UUID_SCHEMA_ATTR_CLAIM,
1535 description: String::from(
1536 "The string identifier of an extracted claim that can be filtered",
1537 ),
1538 multivalue: true,
1539 unique: false,
1540 phantom: true,
1541 sync_allowed: false,
1542 replicated: Replicated::True,
1543 indexed: false,
1544 syntax: SyntaxType::Utf8StringInsensitive,
1545 },
1546 );
1547 self.attributes.insert(
1548 Attribute::Scope,
1549 SchemaAttribute {
1550 name: Attribute::Scope,
1551 uuid: UUID_SCHEMA_ATTR_SCOPE,
1552 description: String::from(
1553 "The string identifier of a permission scope in a session",
1554 ),
1555 multivalue: true,
1556 unique: false,
1557 phantom: true,
1558 sync_allowed: false,
1559 replicated: Replicated::True,
1560 indexed: false,
1561 syntax: SyntaxType::Utf8StringInsensitive,
1562 },
1563 );
1564
1565 self.attributes.insert(
1567 Attribute::SyncExternalId,
1568 SchemaAttribute {
1569 name: Attribute::SyncExternalId,
1570 uuid: UUID_SCHEMA_ATTR_SYNC_EXTERNAL_ID,
1571 description: String::from(
1572 "An external string ID of an entry imported from a sync agreement",
1573 ),
1574 multivalue: false,
1575 unique: true,
1576 phantom: false,
1577 sync_allowed: false,
1578 replicated: Replicated::True,
1579 indexed: true,
1580 syntax: SyntaxType::Utf8StringInsensitive,
1581 },
1582 );
1583 self.attributes.insert(
1584 Attribute::SyncParentUuid,
1585 SchemaAttribute {
1586 name: Attribute::SyncParentUuid,
1587 uuid: UUID_SCHEMA_ATTR_SYNC_PARENT_UUID,
1588 description: String::from(
1589 "The UUID of the parent sync agreement that created this entry.",
1590 ),
1591 multivalue: false,
1592 unique: false,
1593 phantom: false,
1594 sync_allowed: false,
1595 replicated: Replicated::True,
1596 indexed: true,
1597 syntax: SyntaxType::ReferenceUuid,
1598 },
1599 );
1600 self.attributes.insert(
1601 Attribute::SyncClass,
1602 SchemaAttribute {
1603 name: Attribute::SyncClass,
1604 uuid: UUID_SCHEMA_ATTR_SYNC_CLASS,
1605 description: String::from("The set of classes requested by the sync client."),
1606 multivalue: true,
1607 unique: false,
1608 phantom: false,
1609 sync_allowed: false,
1610 replicated: Replicated::True,
1611 indexed: false,
1612 syntax: SyntaxType::Utf8StringInsensitive,
1613 },
1614 );
1615
1616 self.attributes.insert(
1617 Attribute::PasswordImport,
1618 SchemaAttribute {
1619 name: Attribute::PasswordImport,
1620 uuid: UUID_SCHEMA_ATTR_PASSWORD_IMPORT,
1621 description: String::from("An imported password hash from an external system."),
1622 multivalue: false,
1623 unique: false,
1624 phantom: true,
1625 sync_allowed: true,
1626 replicated: Replicated::False,
1627 indexed: false,
1628 syntax: SyntaxType::Utf8String,
1629 },
1630 );
1631
1632 self.attributes.insert(
1633 Attribute::UnixPasswordImport,
1634 SchemaAttribute {
1635 name: Attribute::UnixPasswordImport,
1636 uuid: UUID_SCHEMA_ATTR_UNIX_PASSWORD_IMPORT,
1637 description: String::from(
1638 "An imported unix password hash from an external system.",
1639 ),
1640 multivalue: false,
1641 unique: false,
1642 phantom: true,
1643 sync_allowed: true,
1644 replicated: Replicated::False,
1645 indexed: false,
1646 syntax: SyntaxType::Utf8String,
1647 },
1648 );
1649
1650 self.attributes.insert(
1651 Attribute::TotpImport,
1652 SchemaAttribute {
1653 name: Attribute::TotpImport,
1654 uuid: UUID_SCHEMA_ATTR_TOTP_IMPORT,
1655 description: String::from("An imported totp secret from an external system."),
1656 multivalue: true,
1657 unique: false,
1658 phantom: true,
1659 sync_allowed: true,
1660 replicated: Replicated::False,
1661 indexed: false,
1662 syntax: SyntaxType::TotpSecret,
1663 },
1664 );
1665
1666 self.attributes.insert(
1668 Attribute::Dn,
1669 SchemaAttribute {
1670 name: Attribute::Dn,
1671 uuid: UUID_SCHEMA_ATTR_DN,
1672 description: String::from("An LDAP Compatible DN"),
1673 multivalue: false,
1674 unique: false,
1675 phantom: true,
1676 sync_allowed: false,
1677 replicated: Replicated::False,
1678 indexed: false,
1679 syntax: SyntaxType::Utf8StringInsensitive,
1680 },
1681 );
1682 self.attributes.insert(
1683 Attribute::EntryDn,
1684 SchemaAttribute {
1685 name: Attribute::EntryDn,
1686 uuid: UUID_SCHEMA_ATTR_ENTRYDN,
1687 description: String::from("An LDAP Compatible EntryDN"),
1688 multivalue: false,
1689 unique: false,
1690 phantom: true,
1691 sync_allowed: false,
1692 replicated: Replicated::False,
1693 indexed: false,
1694 syntax: SyntaxType::Utf8StringInsensitive,
1695 },
1696 );
1697 self.attributes.insert(
1698 Attribute::EntryUuid,
1699 SchemaAttribute {
1700 name: Attribute::EntryUuid,
1701 uuid: UUID_SCHEMA_ATTR_ENTRYUUID,
1702 description: String::from("An LDAP Compatible entryUUID"),
1703 multivalue: false,
1704 unique: false,
1705 phantom: true,
1706 sync_allowed: false,
1707 replicated: Replicated::False,
1708 indexed: false,
1709 syntax: SyntaxType::Uuid,
1710 },
1711 );
1712 self.attributes.insert(
1713 Attribute::ObjectClass,
1714 SchemaAttribute {
1715 name: Attribute::ObjectClass,
1716 uuid: UUID_SCHEMA_ATTR_OBJECTCLASS,
1717 description: String::from("An LDAP Compatible objectClass"),
1718 multivalue: true,
1719 unique: false,
1720 phantom: true,
1721 sync_allowed: false,
1722 replicated: Replicated::False,
1723 indexed: false,
1724 syntax: SyntaxType::Utf8StringInsensitive,
1725 },
1726 );
1727 self.attributes.insert(
1728 Attribute::Cn,
1729 SchemaAttribute {
1730 name: Attribute::Cn,
1731 uuid: UUID_SCHEMA_ATTR_CN,
1732 description: String::from("An LDAP Compatible objectClass"),
1733 multivalue: false,
1734 unique: false,
1735 phantom: true,
1736 sync_allowed: false,
1737 replicated: Replicated::False,
1738 indexed: false,
1739 syntax: SyntaxType::Utf8StringIname,
1740 },
1741 );
1742 self.attributes.insert(
1743 Attribute::LdapKeys, SchemaAttribute {
1745 name: Attribute::LdapKeys, uuid: UUID_SCHEMA_ATTR_KEYS,
1747 description: String::from("An LDAP Compatible keys (ssh)"),
1748 multivalue: true,
1749 unique: false,
1750 phantom: true,
1751 sync_allowed: false,
1752 replicated: Replicated::False,
1753 indexed: false,
1754 syntax: SyntaxType::SshKey,
1755 },
1756 );
1757 self.attributes.insert(
1758 Attribute::LdapSshPublicKey,
1759 SchemaAttribute {
1760 name: Attribute::LdapSshPublicKey,
1761 uuid: UUID_SCHEMA_ATTR_SSHPUBLICKEY,
1762 description: String::from("An LDAP Compatible sshPublicKey"),
1763 multivalue: true,
1764 unique: false,
1765 phantom: true,
1766 sync_allowed: false,
1767 replicated: Replicated::False,
1768 indexed: false,
1769 syntax: SyntaxType::SshKey,
1770 },
1771 );
1772 self.attributes.insert(
1773 Attribute::Email,
1774 SchemaAttribute {
1775 name: Attribute::Email,
1776 uuid: UUID_SCHEMA_ATTR_EMAIL,
1777 description: String::from("An LDAP Compatible email"),
1778 multivalue: true,
1779 unique: false,
1780 phantom: true,
1781 sync_allowed: false,
1782 replicated: Replicated::False,
1783 indexed: false,
1784 syntax: SyntaxType::EmailAddress,
1785 },
1786 );
1787 self.attributes.insert(
1788 Attribute::EmailPrimary,
1789 SchemaAttribute {
1790 name: Attribute::EmailPrimary,
1791 uuid: UUID_SCHEMA_ATTR_EMAILPRIMARY,
1792 description: String::from("An LDAP Compatible primary email"),
1793 multivalue: false,
1794 unique: false,
1795 phantom: true,
1796 sync_allowed: false,
1797 replicated: Replicated::False,
1798 indexed: false,
1799 syntax: SyntaxType::EmailAddress,
1800 },
1801 );
1802 self.attributes.insert(
1803 Attribute::EmailAlternative,
1804 SchemaAttribute {
1805 name: Attribute::EmailAlternative,
1806 uuid: UUID_SCHEMA_ATTR_EMAILALTERNATIVE,
1807 description: String::from("An LDAP Compatible alternative email"),
1808 multivalue: false,
1809 unique: false,
1810 phantom: true,
1811 sync_allowed: false,
1812 replicated: Replicated::False,
1813 indexed: false,
1814 syntax: SyntaxType::EmailAddress,
1815 },
1816 );
1817 self.attributes.insert(
1818 Attribute::LdapEmailAddress,
1819 SchemaAttribute {
1820 name: Attribute::LdapEmailAddress,
1821 uuid: UUID_SCHEMA_ATTR_EMAILADDRESS,
1822 description: String::from("An LDAP Compatible emailAddress"),
1823 multivalue: true,
1824 unique: false,
1825 phantom: true,
1826 sync_allowed: false,
1827 replicated: Replicated::False,
1828 indexed: false,
1829 syntax: SyntaxType::EmailAddress,
1830 },
1831 );
1832 self.attributes.insert(
1833 Attribute::Gecos,
1834 SchemaAttribute {
1835 name: Attribute::Gecos,
1836 uuid: UUID_SCHEMA_ATTR_GECOS,
1837 description: String::from("An LDAP Compatible gecos."),
1838 multivalue: false,
1839 unique: false,
1840 phantom: true,
1841 sync_allowed: false,
1842 replicated: Replicated::False,
1843 indexed: false,
1844 syntax: SyntaxType::Utf8String,
1845 },
1846 );
1847 self.attributes.insert(
1848 Attribute::Uid,
1849 SchemaAttribute {
1850 name: Attribute::Uid,
1851 uuid: UUID_SCHEMA_ATTR_UID,
1852 description: String::from("An LDAP Compatible uid."),
1853 multivalue: false,
1854 unique: false,
1855 phantom: true,
1856 sync_allowed: false,
1857 replicated: Replicated::False,
1858 indexed: false,
1859 syntax: SyntaxType::Utf8String,
1860 },
1861 );
1862 self.attributes.insert(
1863 Attribute::UidNumber,
1864 SchemaAttribute {
1865 name: Attribute::UidNumber,
1866 uuid: UUID_SCHEMA_ATTR_UIDNUMBER,
1867 description: String::from("An LDAP Compatible uidNumber."),
1868 multivalue: false,
1869 unique: false,
1870 phantom: true,
1871 sync_allowed: false,
1872 replicated: Replicated::False,
1873 indexed: false,
1874 syntax: SyntaxType::Uint32,
1875 },
1876 );
1877 self.attributes.insert(
1878 Attribute::SudoHost,
1879 SchemaAttribute {
1880 name: Attribute::SudoHost,
1881 uuid: UUID_SCHEMA_ATTR_SUDOHOST,
1882 description: String::from("An LDAP Compatible sudohost."),
1883 multivalue: false,
1884 unique: false,
1885 phantom: true,
1886 sync_allowed: false,
1887 replicated: Replicated::False,
1888 indexed: false,
1889 syntax: SyntaxType::Utf8String,
1890 },
1891 );
1892 self.attributes.insert(
1894 Attribute::Image,
1895 SchemaAttribute {
1896 name: Attribute::Image,
1897 uuid: UUID_SCHEMA_ATTR_IMAGE,
1898 description: String::from("An image for display to end users."),
1899 multivalue: false,
1900 unique: false,
1901 phantom: false,
1902 sync_allowed: true,
1903 replicated: Replicated::True,
1904 indexed: false,
1905 syntax: SyntaxType::Image,
1906 },
1907 );
1908
1909 self.attributes.insert(
1910 Attribute::OAuth2DeviceFlowEnable,
1911 SchemaAttribute {
1912 name: Attribute::OAuth2DeviceFlowEnable,
1913 uuid: UUID_SCHEMA_ATTR_OAUTH2_DEVICE_FLOW_ENABLE,
1914 description: String::from("Enable the OAuth2 Device Flow for this client."),
1915 multivalue: false,
1916 unique: true,
1917 phantom: false,
1918 sync_allowed: false,
1919 replicated: Replicated::True,
1920 indexed: false,
1921 syntax: SyntaxType::Boolean,
1922 },
1923 );
1924
1925 self.classes.insert(
1926 EntryClass::AttributeType.into(),
1927 SchemaClass {
1928 name: EntryClass::AttributeType.into(),
1929 uuid: UUID_SCHEMA_CLASS_ATTRIBUTETYPE,
1930 description: String::from("Definition of a schema attribute"),
1931 systemmay: vec![
1932 Attribute::Replicated,
1933 Attribute::Phantom,
1934 Attribute::SyncAllowed,
1935 Attribute::Index,
1936 Attribute::Indexed,
1937 ],
1938 systemmust: vec![
1939 Attribute::Class,
1940 Attribute::AttributeName,
1941 Attribute::MultiValue,
1942 Attribute::Unique,
1943 Attribute::Syntax,
1944 Attribute::Description,
1945 ],
1946 systemexcludes: vec![EntryClass::ClassType.into()],
1947 ..Default::default()
1948 },
1949 );
1950 self.classes.insert(
1951 EntryClass::ClassType.into(),
1952 SchemaClass {
1953 name: EntryClass::ClassType.into(),
1954 uuid: UUID_SCHEMA_CLASS_CLASSTYPE,
1955 description: String::from("Definition of a schema classtype"),
1956 systemmay: vec![
1957 Attribute::SyncAllowed,
1958 Attribute::SystemMay,
1959 Attribute::May,
1960 Attribute::SystemMust,
1961 Attribute::Must,
1962 Attribute::SystemSupplements,
1963 Attribute::Supplements,
1964 Attribute::SystemExcludes,
1965 Attribute::Excludes,
1966 ],
1967 systemmust: vec![
1968 Attribute::Class,
1969 Attribute::ClassName,
1970 Attribute::Description,
1971 ],
1972 systemexcludes: vec![Attribute::AttributeType.into()],
1973 ..Default::default()
1974 },
1975 );
1976 self.classes.insert(
1977 EntryClass::Object.into(),
1978 SchemaClass {
1979 name: EntryClass::Object.into(),
1980 uuid: UUID_SCHEMA_CLASS_OBJECT,
1981 description: String::from("A system created class that all objects must contain"),
1982 systemmay: vec![Attribute::Description, Attribute::EntryManagedBy],
1983 systemmust: vec![
1984 Attribute::Class,
1985 Attribute::Uuid,
1986 Attribute::LastModifiedCid,
1987 Attribute::CreatedAtCid,
1988 ],
1989 ..Default::default()
1990 },
1991 );
1992 self.classes.insert(
1993 EntryClass::Builtin.into(),
1994 SchemaClass {
1995 name: EntryClass::Builtin.into(),
1996 uuid: UUID_SCHEMA_CLASS_BUILTIN,
1997 description: String::from("A marker class denoting builtin entries"),
1998 ..Default::default()
1999 },
2000 );
2001 self.classes.insert(
2002 EntryClass::MemberOf.into(),
2003 SchemaClass {
2004 name: EntryClass::MemberOf.into(),
2005 uuid: UUID_SCHEMA_CLASS_MEMBEROF,
2006 description: String::from(
2007 "Class that is dynamically added to recipients of memberof or directmemberof",
2008 ),
2009 systemmay: vec![Attribute::MemberOf, Attribute::DirectMemberOf],
2010 ..Default::default()
2011 },
2012 );
2013 self.classes.insert(
2014 EntryClass::ExtensibleObject.into(),
2015 SchemaClass {
2016 name: EntryClass::ExtensibleObject.into(),
2017 uuid: UUID_SCHEMA_CLASS_EXTENSIBLEOBJECT,
2018 description: String::from(
2019 "A class type that has green hair and turns off all rules ...",
2020 ),
2021 ..Default::default()
2022 },
2023 );
2024 self.classes.insert(
2026 EntryClass::Recycled.into(),
2027 SchemaClass {
2028 name: EntryClass::Recycled.into(),
2029 uuid: UUID_SCHEMA_CLASS_RECYCLED,
2030 description: String::from("An object that has been deleted, but still recoverable via the revive operation. Recycled objects are not modifiable, only revivable."),
2031 systemmay: vec![Attribute::RecycledDirectMemberOf],
2032 .. Default::default()
2033 },
2034 );
2035 self.classes.insert(
2036 EntryClass::Tombstone.into(),
2037 SchemaClass {
2038 name: EntryClass::Tombstone.into(),
2039 uuid: UUID_SCHEMA_CLASS_TOMBSTONE,
2040 description: String::from("An object that is purged from the recycle bin. This is a system internal state. Tombstones have no attributes beside UUID."),
2041 systemmust: vec![
2042 Attribute::Class,
2043 Attribute::Uuid,
2044 ],
2045 .. Default::default()
2046 },
2047 );
2048 self.classes.insert(
2049 EntryClass::Conflict.into(),
2050 SchemaClass {
2051 name: EntryClass::Conflict.into(),
2052 uuid: UUID_SCHEMA_CLASS_CONFLICT,
2053 description: String::from(
2054 "An entry representing conflicts that occurred during replication",
2055 ),
2056 systemmust: vec![Attribute::SourceUuid],
2057 systemsupplements: vec![EntryClass::Recycled.into()],
2058 ..Default::default()
2059 },
2060 );
2061 self.classes.insert(
2063 EntryClass::SystemInfo.into(),
2064 SchemaClass {
2065 name: EntryClass::SystemInfo.into(),
2066 uuid: UUID_SCHEMA_CLASS_SYSTEM_INFO,
2067 description: String::from("System metadata object class"),
2068 systemmust: vec![Attribute::Version],
2069 ..Default::default()
2070 },
2071 );
2072 self.classes.insert(
2074 EntryClass::AccessControlSearch.into(),
2075 SchemaClass {
2076 name: EntryClass::AccessControlSearch.into(),
2077 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_SEARCH,
2078 description: String::from("System Access Control Search Class"),
2079 systemmust: vec![Attribute::AcpSearchAttr],
2080 ..Default::default()
2081 },
2082 );
2083 self.classes.insert(
2084 EntryClass::AccessControlDelete.into(),
2085 SchemaClass {
2086 name: EntryClass::AccessControlDelete.into(),
2087 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_DELETE,
2088 description: String::from("System Access Control DELETE Class"),
2089 ..Default::default()
2090 },
2091 );
2092 self.classes.insert(
2093 EntryClass::AccessControlModify.into(),
2094 SchemaClass {
2095 name: EntryClass::AccessControlModify.into(),
2096 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_MODIFY,
2097 description: String::from("System Access Control Modify Class"),
2098 systemmay: vec![
2099 Attribute::AcpModifyRemovedAttr,
2100 Attribute::AcpModifyPresentAttr,
2101 Attribute::AcpModifyClass,
2102 Attribute::AcpModifyPresentClass,
2103 Attribute::AcpModifyRemoveClass,
2104 ],
2105 ..Default::default()
2106 },
2107 );
2108 self.classes.insert(
2109 EntryClass::AccessControlCreate.into(),
2110 SchemaClass {
2111 name: EntryClass::AccessControlCreate.into(),
2112 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_CREATE,
2113 description: String::from("System Access Control Create Class"),
2114 systemmay: vec![Attribute::AcpCreateClass, Attribute::AcpCreateAttr],
2115 ..Default::default()
2116 },
2117 );
2118 self.classes.insert(
2119 EntryClass::AccessControlProfile.into(),
2120 SchemaClass {
2121 name: EntryClass::AccessControlProfile.into(),
2122 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_PROFILE,
2123 description: String::from("System Access Control Profile Class"),
2124 systemmay: vec![Attribute::AcpEnable, Attribute::Description],
2125 systemmust: vec![Attribute::Name],
2126 systemsupplements: vec![
2127 EntryClass::AccessControlSearch.into(),
2128 EntryClass::AccessControlDelete.into(),
2129 EntryClass::AccessControlModify.into(),
2130 EntryClass::AccessControlCreate.into(),
2131 ],
2132 ..Default::default()
2133 },
2134 );
2135 self.classes.insert(
2136 EntryClass::AccessControlReceiverEntryManager.into(),
2137 SchemaClass {
2138 name: EntryClass::AccessControlReceiverEntryManager.into(),
2139 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_RECEIVER_ENTRY_MANAGER,
2140 description: String::from("System Access Control Profile Receiver - Entry Manager"),
2141 systemexcludes: vec![EntryClass::AccessControlReceiverGroup.into()],
2142 systemsupplements: vec![EntryClass::AccessControlProfile.into()],
2143 ..Default::default()
2144 },
2145 );
2146 self.classes.insert(
2147 EntryClass::AccessControlReceiverGroup.into(),
2148 SchemaClass {
2149 name: EntryClass::AccessControlReceiverGroup.into(),
2150 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_RECEIVER_GROUP,
2151 description: String::from("System Access Control Profile Receiver - Group"),
2152 systemmay: vec![Attribute::AcpReceiver],
2153 systemmust: vec![Attribute::AcpReceiverGroup],
2154 systemsupplements: vec![EntryClass::AccessControlProfile.into()],
2155 systemexcludes: vec![EntryClass::AccessControlReceiverEntryManager.into()],
2156 ..Default::default()
2157 },
2158 );
2159 self.classes.insert(
2160 EntryClass::AccessControlTargetScope.into(),
2161 SchemaClass {
2162 name: EntryClass::AccessControlTargetScope.into(),
2163 uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_TARGET_SCOPE,
2164 description: String::from("System Access Control Profile Target - Scope"),
2165 systemmust: vec![Attribute::AcpTargetScope],
2166 systemsupplements: vec![EntryClass::AccessControlProfile.into()],
2167 ..Default::default()
2168 },
2169 );
2170
2171 self.classes.insert(
2173 EntryClass::System.into(),
2174 SchemaClass {
2175 name: EntryClass::System.into(),
2176 uuid: UUID_SCHEMA_CLASS_SYSTEM,
2177 description: String::from("A class denoting that a type is system generated and protected. It has special internal behaviour."),
2178 .. Default::default()
2179 },
2180 );
2181 self.classes.insert(
2182 EntryClass::SyncObject.into(),
2183 SchemaClass {
2184 name: EntryClass::SyncObject.into(),
2185 uuid: UUID_SCHEMA_CLASS_SYNC_OBJECT,
2186 description: String::from("A class denoting that an entry is synchronised from an external source. This entry may not be modifiable."),
2187 systemmust: vec![
2188 Attribute::SyncParentUuid
2189 ],
2190 systemmay: vec![
2191 Attribute::SyncExternalId,
2192 Attribute::SyncClass,
2193 ],
2194 .. Default::default()
2195 },
2196 );
2197
2198 let r = self.validate();
2199 if r.is_empty() {
2200 admin_debug!("schema validate -> passed");
2201 Ok(())
2202 } else {
2203 admin_error!(err = ?r, "schema validate -> errors");
2204 Err(OperationError::ConsistencyError(
2205 r.into_iter().filter_map(|v| v.err()).collect(),
2206 ))
2207 }
2208 }
2209}
2210
2211impl SchemaTransaction for SchemaWriteTransaction<'_> {
2212 fn get_attributes_unique(&self) -> &Vec<Attribute> {
2213 &self.unique_cache
2214 }
2215
2216 fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute> {
2217 &self.ref_cache
2218 }
2219
2220 fn get_classes(&self) -> &HashMap<AttrString, SchemaClass> {
2221 &self.classes
2222 }
2223
2224 fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute> {
2225 &self.attributes
2226 }
2227}
2228
2229impl SchemaTransaction for SchemaReadTransaction {
2230 fn get_attributes_unique(&self) -> &Vec<Attribute> {
2231 &self.unique_cache
2232 }
2233
2234 fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute> {
2235 &self.ref_cache
2236 }
2237
2238 fn get_classes(&self) -> &HashMap<AttrString, SchemaClass> {
2239 &self.classes
2240 }
2241
2242 fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute> {
2243 &self.attributes
2244 }
2245}
2246
2247impl Schema {
2248 pub fn new() -> Result<Self, OperationError> {
2249 let s = Schema {
2250 classes: CowCell::new(HashMap::with_capacity(128)),
2251 attributes: CowCell::new(HashMap::with_capacity(128)),
2252 unique_cache: CowCell::new(Vec::with_capacity(0)),
2253 ref_cache: CowCell::new(HashMap::with_capacity(64)),
2254 };
2255 let mut sw = s.write();
2257 let r1 = sw.generate_in_memory();
2258 debug_assert!(r1.is_ok());
2259 r1?;
2260 let r2 = sw.commit().map(|_| s);
2261 debug_assert!(r2.is_ok());
2262 r2
2263 }
2264
2265 pub fn read(&self) -> SchemaReadTransaction {
2266 SchemaReadTransaction {
2267 classes: self.classes.read(),
2268 attributes: self.attributes.read(),
2269 unique_cache: self.unique_cache.read(),
2270 ref_cache: self.ref_cache.read(),
2271 }
2272 }
2273
2274 pub fn write(&self) -> SchemaWriteTransaction<'_> {
2275 SchemaWriteTransaction {
2276 classes: self.classes.write(),
2277 attributes: self.attributes.write(),
2278 unique_cache: self.unique_cache.write(),
2279 ref_cache: self.ref_cache.write(),
2280 }
2281 }
2282
2283 #[cfg(test)]
2284 pub(crate) fn write_blocking(&self) -> SchemaWriteTransaction<'_> {
2285 self.write()
2286 }
2287}
2288
2289#[cfg(test)]
2290mod tests {
2291 use crate::prelude::*;
2292 use crate::schema::{Schema, SchemaAttribute, SchemaClass, SchemaTransaction, SyntaxType};
2293 use uuid::Uuid;
2294
2295 macro_rules! validate_schema {
2298 ($sch:ident) => {{
2299 let r: Result<Vec<()>, ConsistencyError> = $sch.validate().into_iter().collect();
2301 assert!(r.is_ok());
2302 }};
2303 }
2304
2305 macro_rules! sch_from_entry_ok {
2306 (
2307 $e:expr,
2308 $type:ty
2309 ) => {{
2310 let ev1 = $e.into_sealed_committed();
2311
2312 let r1 = <$type>::try_from(&ev1);
2313 assert!(r1.is_ok());
2314 }};
2315 }
2316
2317 macro_rules! sch_from_entry_err {
2318 (
2319 $e:expr,
2320 $type:ty
2321 ) => {{
2322 let ev1 = $e.into_sealed_committed();
2323
2324 let r1 = <$type>::try_from(&ev1);
2325 assert!(r1.is_err());
2326 }};
2327 }
2328
2329 #[test]
2330 fn test_schema_attribute_from_entry() {
2331 sketching::test_init();
2332
2333 sch_from_entry_err!(
2334 entry_init!(
2335 (Attribute::Class, EntryClass::Object.to_value()),
2336 (Attribute::Class, EntryClass::AttributeType.to_value()),
2337 (
2338 Attribute::AttributeName,
2339 Value::new_iutf8("schema_attr_test")
2340 ),
2341 (
2342 Attribute::Uuid,
2343 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2344 ),
2345 (Attribute::Unique, Value::Bool(false))
2346 ),
2347 SchemaAttribute
2348 );
2349
2350 sch_from_entry_err!(
2351 entry_init!(
2352 (Attribute::Class, EntryClass::Object.to_value()),
2353 (Attribute::Class, EntryClass::AttributeType.to_value()),
2354 (
2355 Attribute::AttributeName,
2356 Value::new_iutf8("schema_attr_test")
2357 ),
2358 (
2359 Attribute::Uuid,
2360 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2361 ),
2362 (Attribute::MultiValue, Value::Bool(false)),
2363 (Attribute::Unique, Value::Bool(false)),
2364 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String))
2365 ),
2366 SchemaAttribute
2367 );
2368
2369 sch_from_entry_err!(
2370 entry_init!(
2371 (Attribute::Class, EntryClass::Object.to_value()),
2372 (Attribute::Class, EntryClass::AttributeType.to_value()),
2373 (
2374 Attribute::AttributeName,
2375 Value::new_iutf8("schema_attr_test")
2376 ),
2377 (
2378 Attribute::Uuid,
2379 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2380 ),
2381 (
2382 Attribute::Description,
2383 Value::Utf8("Test attr parsing".to_string())
2384 ),
2385 (Attribute::MultiValue, Value::Utf8("htouaoeu".to_string())),
2386 (Attribute::Unique, Value::Bool(false)),
2387 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String))
2388 ),
2389 SchemaAttribute
2390 );
2391
2392 sch_from_entry_err!(
2393 entry_init!(
2394 (Attribute::Class, EntryClass::Object.to_value()),
2395 (Attribute::Class, EntryClass::AttributeType.to_value()),
2396 (
2397 Attribute::AttributeName,
2398 Value::new_iutf8("schema_attr_test")
2399 ),
2400 (
2401 Attribute::Uuid,
2402 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2403 ),
2404 (
2405 Attribute::Description,
2406 Value::Utf8("Test attr parsing".to_string())
2407 ),
2408 (Attribute::MultiValue, Value::Bool(false)),
2409 (Attribute::Unique, Value::Bool(false)),
2410 (Attribute::Syntax, Value::Utf8("TNEOUNTUH".to_string()))
2411 ),
2412 SchemaAttribute
2413 );
2414
2415 sch_from_entry_ok!(
2417 entry_init!(
2418 (Attribute::Class, EntryClass::Object.to_value()),
2419 (Attribute::Class, EntryClass::AttributeType.to_value()),
2420 (
2421 Attribute::AttributeName,
2422 Value::new_iutf8("schema_attr_test")
2423 ),
2424 (
2425 Attribute::Uuid,
2426 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2427 ),
2428 (
2429 Attribute::Description,
2430 Value::Utf8("Test attr parsing".to_string())
2431 ),
2432 (Attribute::MultiValue, Value::Bool(false)),
2433 (Attribute::Unique, Value::Bool(false)),
2434 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String))
2435 ),
2436 SchemaAttribute
2437 );
2438
2439 sch_from_entry_ok!(
2441 entry_init!(
2442 (Attribute::Class, EntryClass::Object.to_value()),
2443 (Attribute::Class, EntryClass::AttributeType.to_value()),
2444 (
2445 Attribute::AttributeName,
2446 Value::new_iutf8("schema_attr_test")
2447 ),
2448 (
2449 Attribute::Uuid,
2450 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2451 ),
2452 (
2453 Attribute::Description,
2454 Value::Utf8("Test attr parsing".to_string())
2455 ),
2456 (Attribute::MultiValue, Value::Bool(false)),
2457 (Attribute::Unique, Value::Bool(false)),
2458 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
2459 (Attribute::Index, Value::Bool(true))
2460 ),
2461 SchemaAttribute
2462 );
2463 }
2464
2465 #[test]
2466 fn test_schema_class_from_entry() {
2467 sch_from_entry_err!(
2468 entry_init!(
2469 (Attribute::Class, EntryClass::Object.to_value()),
2470 (Attribute::Class, EntryClass::ClassType.to_value()),
2471 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2472 (
2473 Attribute::Uuid,
2474 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2475 )
2476 ),
2477 SchemaClass
2478 );
2479
2480 sch_from_entry_err!(
2481 entry_init!(
2482 (Attribute::Class, EntryClass::Object.to_value()),
2483 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2484 (
2485 Attribute::Uuid,
2486 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2487 ),
2488 (
2489 Attribute::Description,
2490 Value::Utf8("class test".to_string())
2491 )
2492 ),
2493 SchemaClass
2494 );
2495
2496 sch_from_entry_ok!(
2498 entry_init!(
2499 (Attribute::Class, EntryClass::Object.to_value()),
2500 (Attribute::Class, EntryClass::ClassType.to_value()),
2501 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2502 (
2503 Attribute::Uuid,
2504 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2505 ),
2506 (
2507 Attribute::Description,
2508 Value::Utf8("class test".to_string())
2509 )
2510 ),
2511 SchemaClass
2512 );
2513
2514 sch_from_entry_ok!(
2516 entry_init!(
2517 (Attribute::Class, EntryClass::Object.to_value()),
2518 (Attribute::Class, EntryClass::ClassType.to_value()),
2519 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2520 (
2521 Attribute::Uuid,
2522 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2523 ),
2524 (
2525 Attribute::Description,
2526 Value::Utf8("class test".to_string())
2527 ),
2528 (Attribute::SystemMust, Value::new_iutf8("a"))
2529 ),
2530 SchemaClass
2531 );
2532
2533 sch_from_entry_ok!(
2534 entry_init!(
2535 (Attribute::Class, EntryClass::Object.to_value()),
2536 (Attribute::Class, EntryClass::ClassType.to_value()),
2537 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2538 (
2539 Attribute::Uuid,
2540 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2541 ),
2542 (
2543 Attribute::Description,
2544 Value::Utf8("class test".to_string())
2545 ),
2546 (Attribute::SystemMay, Value::new_iutf8("a"))
2547 ),
2548 SchemaClass
2549 );
2550
2551 sch_from_entry_ok!(
2552 entry_init!(
2553 (Attribute::Class, EntryClass::Object.to_value()),
2554 (Attribute::Class, EntryClass::ClassType.to_value()),
2555 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2556 (
2557 Attribute::Uuid,
2558 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2559 ),
2560 (
2561 Attribute::Description,
2562 Value::Utf8("class test".to_string())
2563 ),
2564 (Attribute::May, Value::new_iutf8("a")),
2565 (Attribute::Must, Value::new_iutf8("b"))
2566 ),
2567 SchemaClass
2568 );
2569
2570 sch_from_entry_ok!(
2571 entry_init!(
2572 (Attribute::Class, EntryClass::Object.to_value()),
2573 (Attribute::Class, EntryClass::ClassType.to_value()),
2574 (Attribute::ClassName, Value::new_iutf8("schema_class_test")),
2575 (
2576 Attribute::Uuid,
2577 Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
2578 ),
2579 (
2580 Attribute::Description,
2581 Value::Utf8("class test".to_string())
2582 ),
2583 (Attribute::May, Value::new_iutf8("a")),
2584 (Attribute::Must, Value::new_iutf8("b")),
2585 (Attribute::SystemMay, Value::new_iutf8("c")),
2586 (Attribute::SystemMust, Value::new_iutf8("d"))
2587 ),
2588 SchemaClass
2589 );
2590 }
2591
2592 #[test]
2593 fn test_schema_attribute_simple() {
2594 let single_value_string = SchemaAttribute {
2598 name: Attribute::from("single_value"),
2599 uuid: Uuid::new_v4(),
2600 description: String::from(""),
2601 syntax: SyntaxType::Utf8StringInsensitive,
2602 ..Default::default()
2603 };
2604
2605 let r1 = single_value_string
2606 .validate_ava(&Attribute::from("single_value"), &(vs_iutf8!["test"] as _));
2607 assert_eq!(r1, Ok(()));
2608
2609 let rvs = vs_iutf8!["test1", "test2"] as _;
2610 let r2 = single_value_string.validate_ava(&Attribute::from("single_value"), &rvs);
2611 assert_eq!(
2612 r2,
2613 Err(SchemaError::InvalidAttributeSyntax(
2614 "single_value".to_string()
2615 ))
2616 );
2617
2618 let multi_value_string = SchemaAttribute {
2621 name: Attribute::from("mv_string"),
2622 uuid: Uuid::new_v4(),
2623 description: String::from(""),
2624 multivalue: true,
2625 syntax: SyntaxType::Utf8String,
2626 ..Default::default()
2627 };
2628
2629 let rvs = vs_utf8!["test1".to_string(), "test2".to_string()] as _;
2630 let r5 = multi_value_string.validate_ava(&Attribute::from("mv_string"), &rvs);
2631 assert_eq!(r5, Ok(()));
2632
2633 let multi_value_boolean = SchemaAttribute {
2634 name: Attribute::from("mv_bool"),
2635 uuid: Uuid::new_v4(),
2636 description: String::from(""),
2637 multivalue: true,
2638 syntax: SyntaxType::Boolean,
2639 ..Default::default()
2640 };
2641
2642 let rvs = vs_bool![true, false];
2659 let r4 = multi_value_boolean.validate_ava(&Attribute::from("mv_bool"), &(rvs as _));
2660 assert_eq!(r4, Ok(()));
2661
2662 let single_value_syntax = SchemaAttribute {
2664 name: Attribute::from("sv_syntax"),
2665 uuid: Uuid::new_v4(),
2666 description: String::from(""),
2667 syntax: SyntaxType::SyntaxId,
2668 ..Default::default()
2669 };
2670
2671 let rvs = vs_syntax![SyntaxType::try_from("UTF8STRING").unwrap()] as _;
2672 let r6 = single_value_syntax.validate_ava(&Attribute::from("sv_syntax"), &rvs);
2673 assert_eq!(r6, Ok(()));
2674
2675 let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
2676 let r7 = single_value_syntax.validate_ava(&Attribute::from("sv_syntax"), &rvs);
2677 assert_eq!(
2678 r7,
2679 Err(SchemaError::InvalidAttributeSyntax("sv_syntax".to_string()))
2680 );
2681
2682 let single_value_index = SchemaAttribute {
2683 name: Attribute::from("sv_index"),
2684 uuid: Uuid::new_v4(),
2685 description: String::from(""),
2686 syntax: SyntaxType::IndexId,
2687 ..Default::default()
2688 };
2689
2690 let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
2691 let r9 = single_value_index.validate_ava(&Attribute::from("sv_index"), &rvs);
2692 assert_eq!(
2693 r9,
2694 Err(SchemaError::InvalidAttributeSyntax("sv_index".to_string()))
2695 );
2696 }
2697
2698 #[test]
2699 fn test_schema_simple() {
2700 let schema = Schema::new().expect("failed to create schema");
2701 let schema_ro = schema.read();
2702 validate_schema!(schema_ro);
2703 }
2704
2705 #[test]
2706 fn test_schema_entries() {
2707 sketching::test_init();
2708 let schema_outer = Schema::new().expect("failed to create schema");
2711 let schema = schema_outer.read();
2712
2713 let e_no_uuid = entry_init!().into_invalid_new();
2714
2715 assert_eq!(
2716 e_no_uuid.validate(&schema),
2717 Err(SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
2718 );
2719
2720 let e_no_class = entry_init!((
2721 Attribute::Uuid,
2722 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2723 ))
2724 .into_invalid_new();
2725
2726 assert_eq!(e_no_class.validate(&schema), Err(SchemaError::NoClassFound));
2727
2728 let e_bad_class = entry_init!(
2729 (
2730 Attribute::Uuid,
2731 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2732 ),
2733 (Attribute::Class, Value::new_class("zzzzzz"))
2734 )
2735 .into_invalid_new();
2736 assert_eq!(
2737 e_bad_class.validate(&schema),
2738 Err(SchemaError::InvalidClass(vec!["zzzzzz".to_string()]))
2739 );
2740
2741 let e_attr_invalid = entry_init!(
2742 (
2743 Attribute::Uuid,
2744 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2745 ),
2746 (Attribute::Class, EntryClass::Object.to_value()),
2747 (Attribute::Class, EntryClass::AttributeType.to_value())
2748 )
2749 .into_invalid_new();
2750 let res = e_attr_invalid.validate(&schema);
2751 matches!(res, Err(SchemaError::MissingMustAttribute(_)));
2752
2753 let e_attr_invalid_may = entry_init!(
2754 (Attribute::Class, EntryClass::Object.to_value()),
2755 (Attribute::Class, EntryClass::AttributeType.to_value()),
2756 (Attribute::AttributeName, Value::new_iutf8("testattr")),
2757 (Attribute::Description, Value::Utf8("testattr".to_string())),
2758 (Attribute::MultiValue, Value::Bool(false)),
2759 (Attribute::Unique, Value::Bool(false)),
2760 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
2761 (
2762 Attribute::Uuid,
2763 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2764 ),
2765 (Attribute::TestAttr, Value::Utf8("zzzz".to_string()))
2766 )
2767 .into_invalid_new();
2768
2769 assert_eq!(
2770 e_attr_invalid_may.validate(&schema),
2771 Err(SchemaError::AttributeNotValidForClass(
2772 Attribute::TestAttr.to_string()
2773 ))
2774 );
2775
2776 let e_attr_invalid_syn = entry_init!(
2777 (Attribute::Class, EntryClass::Object.to_value()),
2778 (Attribute::Class, EntryClass::AttributeType.to_value()),
2779 (Attribute::AttributeName, Value::new_iutf8("testattr")),
2780 (Attribute::Description, Value::Utf8("testattr".to_string())),
2781 (Attribute::MultiValue, Value::Utf8("false".to_string())),
2782 (Attribute::Unique, Value::Bool(false)),
2783 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
2784 (
2785 Attribute::Uuid,
2786 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2787 )
2788 )
2789 .into_invalid_new();
2790
2791 assert_eq!(
2792 e_attr_invalid_syn.validate(&schema),
2793 Err(SchemaError::InvalidAttributeSyntax(
2794 "multivalue".to_string()
2795 ))
2796 );
2797
2798 let e_phantom = entry_init!(
2800 (Attribute::Class, EntryClass::Object.to_value()),
2801 (Attribute::Class, EntryClass::AttributeType.to_value()),
2802 (Attribute::AttributeName, Value::new_iutf8("testattr")),
2803 (Attribute::Description, Value::Utf8("testattr".to_string())),
2804 (Attribute::MultiValue, Value::Bool(false)),
2805 (Attribute::Unique, Value::Bool(false)),
2806 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
2807 (
2808 Attribute::Uuid,
2809 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2810 ),
2811 (
2812 Attribute::PasswordImport,
2813 Value::Utf8("password".to_string())
2814 )
2815 )
2816 .into_invalid_new();
2817 assert!(e_phantom.validate(&schema).is_err());
2818
2819 let e_ok = entry_init!(
2820 (Attribute::Class, EntryClass::Object.to_value()),
2821 (Attribute::Class, EntryClass::AttributeType.to_value()),
2822 (Attribute::AttributeName, Value::new_iutf8("testattr")),
2823 (Attribute::Description, Value::Utf8("testattr".to_string())),
2824 (Attribute::MultiValue, Value::Bool(true)),
2825 (Attribute::Unique, Value::Bool(false)),
2826 (Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
2827 (
2828 Attribute::Uuid,
2829 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2830 )
2831 )
2832 .into_invalid_new();
2833 assert!(e_ok.validate(&schema).is_ok());
2834 }
2835
2836 #[test]
2837 fn test_schema_extensible() {
2838 let schema_outer = Schema::new().expect("failed to create schema");
2839 let schema = schema_outer.read();
2840 let e_extensible_bad = entry_init!(
2842 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2843 (
2844 Attribute::Uuid,
2845 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2846 ),
2847 (Attribute::MultiValue, Value::Utf8("zzzz".to_string()))
2848 )
2849 .into_invalid_new();
2850
2851 assert_eq!(
2852 e_extensible_bad.validate(&schema),
2853 Err(SchemaError::InvalidAttributeSyntax(
2854 "multivalue".to_string()
2855 ))
2856 );
2857
2858 let e_extensible_phantom = entry_init!(
2860 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2861 (
2862 Attribute::Uuid,
2863 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2864 ),
2865 (Attribute::PasswordImport, Value::Utf8("zzzz".to_string()))
2866 )
2867 .into_invalid_new();
2868
2869 assert_eq!(
2870 e_extensible_phantom.validate(&schema),
2871 Err(SchemaError::PhantomAttribute(
2872 Attribute::PasswordImport.to_string()
2873 ))
2874 );
2875
2876 let e_extensible = entry_init!(
2877 (Attribute::Class, EntryClass::ExtensibleObject.to_value()),
2878 (
2879 Attribute::Uuid,
2880 Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
2881 ),
2882 (Attribute::MultiValue, Value::Bool(true))
2883 )
2884 .into_invalid_new();
2885
2886 assert!(e_extensible.validate(&schema).is_ok());
2888 }
2889
2890 #[test]
2891 fn test_schema_filter_validation() {
2892 let schema_outer = Schema::new().expect("failed to create schema");
2893 let schema = schema_outer.read();
2894
2895 let f_bool = filter_all!(f_eq(Attribute::MultiValue, PartialValue::new_iutf8("zzzz")));
2897 assert_eq!(
2898 f_bool.validate(&schema),
2899 Err(SchemaError::InvalidAttributeSyntax(
2900 "multivalue".to_string()
2901 ))
2902 );
2903 let f_insense = filter_all!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
2905 assert_eq!(
2906 f_insense.validate(&schema),
2907 Ok(filter_valid!(f_eq(
2908 Attribute::Class,
2909 EntryClass::AttributeType.into()
2910 )))
2911 );
2912 let f_or_empty = filter_all!(f_or!([]));
2914 assert_eq!(f_or_empty.validate(&schema), Err(SchemaError::EmptyFilter));
2915 let f_or = filter_all!(f_or!([f_eq(
2916 Attribute::MultiValue,
2917 PartialValue::new_iutf8("zzzz")
2918 )]));
2919 assert_eq!(
2920 f_or.validate(&schema),
2921 Err(SchemaError::InvalidAttributeSyntax(
2922 "multivalue".to_string()
2923 ))
2924 );
2925 let f_or_mult = filter_all!(f_and!([
2926 f_eq(Attribute::Class, EntryClass::AttributeType.into()),
2927 f_eq(Attribute::MultiValue, PartialValue::new_iutf8("zzzzzzz")),
2928 ]));
2929 assert_eq!(
2930 f_or_mult.validate(&schema),
2931 Err(SchemaError::InvalidAttributeSyntax(
2932 "multivalue".to_string()
2933 ))
2934 );
2935 let f_or_ok = filter_all!(f_andnot(f_and!([
2937 f_eq(Attribute::Class, EntryClass::AttributeType.into()),
2938 f_sub(Attribute::Class, EntryClass::ClassType.into()),
2939 f_pres(Attribute::Class)
2940 ])));
2941 assert_eq!(
2942 f_or_ok.validate(&schema),
2943 Ok(filter_valid!(f_andnot(f_and!([
2944 f_eq(Attribute::Class, EntryClass::AttributeType.into()),
2945 f_sub(Attribute::Class, EntryClass::ClassType.into()),
2946 f_pres(Attribute::Class)
2947 ]))))
2948 );
2949 }
2950
2951 #[test]
2952 fn test_schema_class_phantom_reject() {
2953 let schema_outer = Schema::new().expect("failed to create schema");
2955 let mut schema = schema_outer.write_blocking();
2956
2957 assert!(schema.validate().is_empty());
2958
2959 let class = SchemaClass {
2961 name: AttrString::from("testobject"),
2962 uuid: Uuid::new_v4(),
2963 description: String::from("test object"),
2964 systemmay: vec![Attribute::Claim],
2965 ..Default::default()
2966 };
2967
2968 assert!(schema.update_classes(vec![class]).is_ok());
2969
2970 assert_eq!(schema.validate().len(), 1);
2971 }
2972
2973 #[test]
2974 fn test_schema_class_exclusion_requires() {
2975 sketching::test_init();
2976
2977 let schema_outer = Schema::new().expect("failed to create schema");
2978 let mut schema = schema_outer.write_blocking();
2979
2980 assert!(schema.validate().is_empty());
2981
2982 let class_account = SchemaClass {
2985 name: Attribute::Account.into(),
2986 uuid: Uuid::new_v4(),
2987 description: String::from("account object"),
2988 systemmust: vec![
2989 Attribute::Class,
2990 Attribute::Uuid,
2991 Attribute::LastModifiedCid,
2992 Attribute::CreatedAtCid,
2993 ],
2994 systemsupplements: vec![EntryClass::Service.into(), EntryClass::Person.into()],
2995 ..Default::default()
2996 };
2997
2998 let class_person = SchemaClass {
2999 name: EntryClass::Person.into(),
3000 uuid: Uuid::new_v4(),
3001 description: String::from("person object"),
3002 systemmust: vec![
3003 Attribute::Class,
3004 Attribute::Uuid,
3005 Attribute::LastModifiedCid,
3006 Attribute::CreatedAtCid,
3007 ],
3008 ..Default::default()
3009 };
3010
3011 let class_service = SchemaClass {
3012 name: EntryClass::Service.into(),
3013 uuid: Uuid::new_v4(),
3014 description: String::from("service object"),
3015 systemmust: vec![
3016 Attribute::Class,
3017 Attribute::Uuid,
3018 Attribute::LastModifiedCid,
3019 Attribute::CreatedAtCid,
3020 ],
3021 excludes: vec![EntryClass::Person.into()],
3022 ..Default::default()
3023 };
3024
3025 assert!(schema
3026 .update_classes(vec![class_account, class_person, class_service])
3027 .is_ok());
3028
3029 let e_account = entry_init!(
3031 (Attribute::Class, EntryClass::Account.to_value()),
3032 (Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
3033 )
3034 .into_invalid_new();
3035
3036 assert_eq!(
3037 e_account.validate(&schema),
3038 Err(SchemaError::SupplementsNotSatisfied(vec![
3039 EntryClass::Service.into(),
3040 EntryClass::Person.into(),
3041 ]))
3042 );
3043
3044 let e_service_person = entry_init!(
3059 (Attribute::Class, EntryClass::Service.to_value()),
3060 (Attribute::Class, EntryClass::Account.to_value()),
3061 (Attribute::Class, EntryClass::Person.to_value()),
3062 (Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
3063 )
3064 .into_invalid_new();
3065
3066 assert_eq!(
3067 e_service_person.validate(&schema),
3068 Err(SchemaError::ExcludesNotSatisfied(vec![
3069 EntryClass::Person.to_string()
3070 ]))
3071 );
3072
3073 let e_service_valid = entry_init!(
3075 (Attribute::Class, EntryClass::Service.to_value()),
3076 (Attribute::Class, EntryClass::Account.to_value()),
3077 (Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
3078 )
3079 .into_invalid_new();
3080
3081 assert!(e_service_valid.validate(&schema).is_ok());
3082
3083 let e_person_valid = entry_init!(
3084 (Attribute::Class, EntryClass::Person.to_value()),
3085 (Attribute::Class, EntryClass::Account.to_value()),
3086 (Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
3087 )
3088 .into_invalid_new();
3089
3090 assert!(e_person_valid.validate(&schema).is_ok());
3091
3092 let e_person_valid = entry_init!(
3093 (Attribute::Class, EntryClass::Person.to_value()),
3094 (Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
3095 )
3096 .into_invalid_new();
3097
3098 assert!(e_person_valid.validate(&schema).is_ok());
3099 }
3100}