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