use std::collections::BTreeSet;
use concread::cowcell::*;
use hashbrown::{HashMap, HashSet};
use tracing::trace;
use uuid::Uuid;
use crate::be::IdxKey;
use crate::prelude::*;
use crate::valueset::ValueSet;
pub struct Schema {
classes: CowCell<HashMap<AttrString, SchemaClass>>,
attributes: CowCell<HashMap<Attribute, SchemaAttribute>>,
unique_cache: CowCell<Vec<Attribute>>,
ref_cache: CowCell<HashMap<Attribute, SchemaAttribute>>,
}
pub struct SchemaWriteTransaction<'a> {
classes: CowCellWriteTxn<'a, HashMap<AttrString, SchemaClass>>,
attributes: CowCellWriteTxn<'a, HashMap<Attribute, SchemaAttribute>>,
unique_cache: CowCellWriteTxn<'a, Vec<Attribute>>,
ref_cache: CowCellWriteTxn<'a, HashMap<Attribute, SchemaAttribute>>,
}
pub struct SchemaReadTransaction {
classes: CowCellReadTxn<HashMap<AttrString, SchemaClass>>,
attributes: CowCellReadTxn<HashMap<Attribute, SchemaAttribute>>,
unique_cache: CowCellReadTxn<Vec<Attribute>>,
ref_cache: CowCellReadTxn<HashMap<Attribute, SchemaAttribute>>,
}
#[derive(Debug, Clone, Default)]
pub struct SchemaAttribute {
pub name: Attribute,
pub uuid: Uuid,
pub description: String,
pub multivalue: bool,
pub unique: bool,
pub phantom: bool,
pub sync_allowed: bool,
pub replicated: bool,
pub index: Vec<IndexType>,
pub syntax: SyntaxType,
}
impl SchemaAttribute {
pub fn try_from(value: &Entry<EntrySealed, EntryCommitted>) -> Result<Self, OperationError> {
let uuid = value.get_uuid();
if !value.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into()) {
admin_error!(
"class {} not present - {:?}",
EntryClass::AttributeType,
uuid
);
return Err(OperationError::InvalidSchemaState(format!(
"missing {}",
EntryClass::AttributeType
)));
}
let name = value
.get_ava_single_iutf8(Attribute::AttributeName)
.map(|s| s.into())
.ok_or_else(|| {
admin_error!("missing {} - {:?}", Attribute::AttributeName, uuid);
OperationError::InvalidSchemaState("missing attributename".to_string())
})?;
let description = value
.get_ava_single_utf8(Attribute::Description)
.map(|s| s.to_string())
.ok_or_else(|| {
admin_error!("missing {} - {}", Attribute::Description, name);
OperationError::InvalidSchemaState("missing description".to_string())
})?;
let multivalue = value
.get_ava_single_bool(Attribute::MultiValue)
.ok_or_else(|| {
admin_error!("missing {} - {}", Attribute::MultiValue, name);
OperationError::InvalidSchemaState("missing multivalue".to_string())
})?;
let unique = value
.get_ava_single_bool(Attribute::Unique)
.ok_or_else(|| {
admin_error!("missing {} - {}", Attribute::Unique, name);
OperationError::InvalidSchemaState("missing unique".to_string())
})?;
let phantom = value
.get_ava_single_bool(Attribute::Phantom)
.unwrap_or(false);
let sync_allowed = value
.get_ava_single_bool(Attribute::SyncAllowed)
.unwrap_or(false);
let replicated = value
.get_ava_single_bool(Attribute::Replicated)
.unwrap_or(true);
let index = value.get_ava_opt_index(Attribute::Index).ok_or_else(|| {
admin_error!("invalid {} - {}", Attribute::Index, name);
OperationError::InvalidSchemaState(format!("invalid {}", Attribute::Index))
})?;
let syntax = value
.get_ava_single_syntax(Attribute::Syntax)
.ok_or_else(|| {
admin_error!("missing {} - {}", Attribute::Syntax, name);
OperationError::InvalidSchemaState(format!("missing {}", Attribute::Syntax))
})?;
Ok(SchemaAttribute {
name,
uuid,
description,
multivalue,
unique,
phantom,
sync_allowed,
replicated,
index,
syntax,
})
}
pub fn validate_partialvalue(
&self,
a: &Attribute,
v: &PartialValue,
) -> Result<(), SchemaError> {
let r = match self.syntax {
SyntaxType::Boolean => matches!(v, PartialValue::Bool(_)),
SyntaxType::SyntaxId => matches!(v, PartialValue::Syntax(_)),
SyntaxType::IndexId => matches!(v, PartialValue::Index(_)),
SyntaxType::Uuid => matches!(v, PartialValue::Uuid(_)),
SyntaxType::ReferenceUuid => matches!(v, PartialValue::Refer(_)),
SyntaxType::Utf8StringInsensitive => matches!(v, PartialValue::Iutf8(_)),
SyntaxType::Utf8StringIname => matches!(v, PartialValue::Iname(_)),
SyntaxType::Utf8String => matches!(v, PartialValue::Utf8(_)),
SyntaxType::JsonFilter => matches!(v, PartialValue::JsonFilt(_)),
SyntaxType::Credential => matches!(v, PartialValue::Cred(_)),
SyntaxType::SecretUtf8String => matches!(v, PartialValue::SecretValue),
SyntaxType::SshKey => matches!(v, PartialValue::SshKey(_)),
SyntaxType::SecurityPrincipalName => matches!(v, PartialValue::Spn(_, _)),
SyntaxType::Uint32 => matches!(v, PartialValue::Uint32(_)),
SyntaxType::Cid => matches!(v, PartialValue::Cid(_)),
SyntaxType::NsUniqueId => matches!(v, PartialValue::Nsuniqueid(_)),
SyntaxType::DateTime => matches!(v, PartialValue::DateTime(_)),
SyntaxType::EmailAddress => matches!(v, PartialValue::EmailAddress(_)),
SyntaxType::Url => matches!(v, PartialValue::Url(_)),
SyntaxType::OauthScope => matches!(v, PartialValue::OauthScope(_)),
SyntaxType::OauthScopeMap => matches!(v, PartialValue::Refer(_)),
SyntaxType::OauthClaimMap => {
matches!(v, PartialValue::Iutf8(_))
|| matches!(v, PartialValue::Refer(_))
|| matches!(v, PartialValue::OauthClaimValue(_, _, _))
|| matches!(v, PartialValue::OauthClaim(_, _))
}
SyntaxType::PrivateBinary => matches!(v, PartialValue::PrivateBinary),
SyntaxType::IntentToken => matches!(v, PartialValue::IntentToken(_)),
SyntaxType::Passkey => matches!(v, PartialValue::Passkey(_)),
SyntaxType::AttestedPasskey => matches!(v, PartialValue::AttestedPasskey(_)),
SyntaxType::Session => matches!(v, PartialValue::Refer(_)),
SyntaxType::ApiToken => matches!(v, PartialValue::Refer(_)),
SyntaxType::Oauth2Session => matches!(v, PartialValue::Refer(_)),
SyntaxType::JwsKeyEs256 => matches!(v, PartialValue::Iutf8(_)),
SyntaxType::JwsKeyRs256 => matches!(v, PartialValue::Iutf8(_)),
SyntaxType::UiHint => matches!(v, PartialValue::UiHint(_)),
SyntaxType::EcKeyPrivate => matches!(v, PartialValue::SecretValue),
SyntaxType::TotpSecret => matches!(v, PartialValue::Utf8(_)),
SyntaxType::AuditLogString => matches!(v, PartialValue::Utf8(_)),
SyntaxType::Image => matches!(v, PartialValue::Utf8(_)),
SyntaxType::CredentialType => matches!(v, PartialValue::CredentialType(_)),
SyntaxType::HexString | SyntaxType::Certificate | SyntaxType::KeyInternal => {
matches!(v, PartialValue::HexString(_))
}
SyntaxType::WebauthnAttestationCaList => false,
SyntaxType::ApplicationPassword => {
matches!(v, PartialValue::Uuid(_)) || matches!(v, PartialValue::Refer(_))
}
};
if r {
Ok(())
} else {
error!(
?a,
?self,
?v,
"validate_partialvalue InvalidAttributeSyntax"
);
Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
}
}
pub fn validate_value(&self, a: &Attribute, v: &Value) -> Result<(), SchemaError> {
let r = v.validate()
&& match self.syntax {
SyntaxType::Boolean => matches!(v, Value::Bool(_)),
SyntaxType::SyntaxId => matches!(v, Value::Syntax(_)),
SyntaxType::IndexId => matches!(v, Value::Index(_)),
SyntaxType::Uuid => matches!(v, Value::Uuid(_)),
SyntaxType::ReferenceUuid => matches!(v, Value::Refer(_)),
SyntaxType::Utf8StringInsensitive => matches!(v, Value::Iutf8(_)),
SyntaxType::Utf8StringIname => matches!(v, Value::Iname(_)),
SyntaxType::Utf8String => matches!(v, Value::Utf8(_)),
SyntaxType::JsonFilter => matches!(v, Value::JsonFilt(_)),
SyntaxType::Credential => matches!(v, Value::Cred(_, _)),
SyntaxType::SecretUtf8String => matches!(v, Value::SecretValue(_)),
SyntaxType::SshKey => matches!(v, Value::SshKey(_, _)),
SyntaxType::SecurityPrincipalName => matches!(v, Value::Spn(_, _)),
SyntaxType::Uint32 => matches!(v, Value::Uint32(_)),
SyntaxType::Cid => matches!(v, Value::Cid(_)),
SyntaxType::NsUniqueId => matches!(v, Value::Nsuniqueid(_)),
SyntaxType::DateTime => matches!(v, Value::DateTime(_)),
SyntaxType::EmailAddress => matches!(v, Value::EmailAddress(_, _)),
SyntaxType::Url => matches!(v, Value::Url(_)),
SyntaxType::OauthScope => matches!(v, Value::OauthScope(_)),
SyntaxType::OauthScopeMap => matches!(v, Value::OauthScopeMap(_, _)),
SyntaxType::OauthClaimMap => {
matches!(v, Value::OauthClaimValue(_, _, _))
|| matches!(v, Value::OauthClaimMap(_, _))
}
SyntaxType::PrivateBinary => matches!(v, Value::PrivateBinary(_)),
SyntaxType::IntentToken => matches!(v, Value::IntentToken(_, _)),
SyntaxType::Passkey => matches!(v, Value::Passkey(_, _, _)),
SyntaxType::AttestedPasskey => matches!(v, Value::AttestedPasskey(_, _, _)),
SyntaxType::Session => matches!(v, Value::Session(_, _)),
SyntaxType::ApiToken => matches!(v, Value::ApiToken(_, _)),
SyntaxType::Oauth2Session => matches!(v, Value::Oauth2Session(_, _)),
SyntaxType::JwsKeyEs256 => matches!(v, Value::JwsKeyEs256(_)),
SyntaxType::JwsKeyRs256 => matches!(v, Value::JwsKeyRs256(_)),
SyntaxType::UiHint => matches!(v, Value::UiHint(_)),
SyntaxType::TotpSecret => matches!(v, Value::TotpSecret(_, _)),
SyntaxType::AuditLogString => matches!(v, Value::Utf8(_)),
SyntaxType::EcKeyPrivate => matches!(v, Value::EcKeyPrivate(_)),
SyntaxType::Image => matches!(v, Value::Image(_)),
SyntaxType::CredentialType => matches!(v, Value::CredentialType(_)),
SyntaxType::WebauthnAttestationCaList => {
matches!(v, Value::WebauthnAttestationCaList(_))
}
SyntaxType::KeyInternal => matches!(v, Value::KeyInternal { .. }),
SyntaxType::HexString => matches!(v, Value::HexString(_)),
SyntaxType::Certificate => matches!(v, Value::Certificate(_)),
SyntaxType::ApplicationPassword => matches!(v, Value::ApplicationPassword(..)),
};
if r {
Ok(())
} else {
error!(
?a,
?self,
?v,
"validate_value failure - InvalidAttributeSyntax"
);
Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
}
}
pub fn validate_ava(&self, a: &Attribute, ava: &ValueSet) -> Result<(), SchemaError> {
trace!("Checking for valid {:?} -> {:?}", self.name, ava);
if !self.multivalue && ava.len() > 1 {
admin_error!("Ava len > 1 on single value attribute!");
return Err(SchemaError::InvalidAttributeSyntax(a.to_string()));
};
let valid = self.syntax == ava.syntax();
if valid && ava.validate(self) {
Ok(())
} else {
error!(
?a,
"validate_ava - InvalidAttributeSyntax for {:?}", self.syntax
);
Err(SchemaError::InvalidAttributeSyntax(a.to_string()))
}
}
}
impl From<SchemaAttribute> for EntryInitNew {
fn from(value: SchemaAttribute) -> Self {
let mut entry = EntryInitNew::new();
entry.set_ava(
Attribute::AttributeName,
vec![Value::new_iutf8(value.name.as_str())],
);
entry.add_ava(Attribute::MultiValue, Value::Bool(value.multivalue));
entry.set_ava(Attribute::Syntax, vec![Value::Syntax(value.syntax)]);
entry.set_ava(Attribute::Unique, vec![Value::Bool(value.unique)]);
entry.set_ava(Attribute::Index, value.index.into_iter().map(Value::Index));
entry.set_ava(
Attribute::Class,
vec![
EntryClass::Object.to_value(),
EntryClass::System.into(),
EntryClass::AttributeType.to_value(),
],
);
entry.set_ava(
Attribute::Description,
vec![Value::new_utf8s(&value.description)],
);
entry.set_ava(
Attribute::SyncAllowed,
vec![Value::Bool(value.sync_allowed)],
);
entry.set_ava(Attribute::Uuid, vec![Value::Uuid(value.uuid)]);
entry
}
}
#[derive(Debug, Clone, Default)]
pub struct SchemaClass {
pub name: AttrString,
pub uuid: Uuid,
pub description: String,
pub sync_allowed: bool,
pub systemmay: Vec<Attribute>,
pub may: Vec<Attribute>,
pub systemmust: Vec<Attribute>,
pub must: Vec<Attribute>,
pub systemsupplements: Vec<AttrString>,
pub supplements: Vec<AttrString>,
pub systemexcludes: Vec<AttrString>,
pub excludes: Vec<AttrString>,
}
impl SchemaClass {
pub fn try_from(value: &Entry<EntrySealed, EntryCommitted>) -> Result<Self, OperationError> {
let uuid = value.get_uuid();
if !value.attribute_equality(Attribute::Class, &EntryClass::ClassType.into()) {
error!("class classtype not present - {:?}", uuid);
return Err(OperationError::InvalidSchemaState(
"missing classtype".to_string(),
));
}
let name = value
.get_ava_single_iutf8(Attribute::ClassName)
.map(AttrString::from)
.ok_or_else(|| {
error!("missing {} - {:?}", Attribute::ClassName, uuid);
OperationError::InvalidSchemaState(format!("missing {}", Attribute::ClassName))
})?;
let description = value
.get_ava_single_utf8(Attribute::Description)
.map(String::from)
.ok_or_else(|| {
error!("missing {} - {}", Attribute::Description, name);
OperationError::InvalidSchemaState(format!("missing {}", Attribute::Description))
})?;
let sync_allowed = value
.get_ava_single_bool(Attribute::SyncAllowed)
.unwrap_or(false);
let systemmay = value
.get_ava_iter_iutf8(Attribute::SystemMay)
.into_iter()
.flat_map(|iter| iter.map(Attribute::from))
.collect();
let systemmust = value
.get_ava_iter_iutf8(Attribute::SystemMust)
.into_iter()
.flat_map(|iter| iter.map(Attribute::from))
.collect();
let may = value
.get_ava_iter_iutf8(Attribute::May)
.into_iter()
.flat_map(|iter| iter.map(Attribute::from))
.collect();
let must = value
.get_ava_iter_iutf8(Attribute::Must)
.into_iter()
.flat_map(|iter| iter.map(Attribute::from))
.collect();
let systemsupplements = value
.get_ava_iter_iutf8(Attribute::SystemSupplements)
.map(|i| i.map(|v| v.into()).collect())
.unwrap_or_default();
let supplements = value
.get_ava_iter_iutf8(Attribute::Supplements)
.map(|i| i.map(|v| v.into()).collect())
.unwrap_or_default();
let systemexcludes = value
.get_ava_iter_iutf8(Attribute::SystemExcludes)
.map(|i| i.map(|v| v.into()).collect())
.unwrap_or_default();
let excludes = value
.get_ava_iter_iutf8(Attribute::Excludes)
.map(|i| i.map(|v| v.into()).collect())
.unwrap_or_default();
Ok(SchemaClass {
name,
uuid,
description,
sync_allowed,
systemmay,
may,
systemmust,
must,
systemsupplements,
supplements,
systemexcludes,
excludes,
})
}
pub fn may_iter(&self) -> impl Iterator<Item = &Attribute> {
self.systemmay
.iter()
.chain(self.may.iter())
.chain(self.systemmust.iter())
.chain(self.must.iter())
}
}
impl From<SchemaClass> for EntryInitNew {
fn from(value: SchemaClass) -> Self {
let mut entry = EntryInitNew::new();
entry.set_ava(Attribute::ClassName, vec![Value::new_iutf8(&value.name)]);
entry.set_ava(
Attribute::Class,
vec![
EntryClass::Object.to_value(),
EntryClass::System.into(),
EntryClass::ClassType.into(),
],
);
entry.set_ava(
Attribute::Description,
vec![Value::new_utf8s(&value.description)],
);
entry.set_ava(
Attribute::SyncAllowed,
vec![Value::Bool(value.sync_allowed)],
);
entry.set_ava(Attribute::Uuid, vec![Value::Uuid(value.uuid)]);
if !value.systemmay.is_empty() {
entry.set_ava(
Attribute::SystemMay,
value.systemmay.iter().map(|s| Value::new_iutf8(s.as_str())),
);
}
if !value.systemexcludes.is_empty() {
entry.set_ava(
Attribute::SystemExcludes,
value.systemexcludes.iter().map(|s| Value::new_iutf8(s)),
);
}
if !value.systemmust.is_empty() {
entry.set_ava(
Attribute::SystemMust,
value
.systemmust
.iter()
.map(|s| Value::new_iutf8(s.as_str())),
);
}
if !value.systemsupplements.is_empty() {
entry.set_ava(
Attribute::SystemSupplements,
value.systemsupplements.iter().map(|s| Value::new_iutf8(s)),
);
}
entry
}
}
pub trait SchemaTransaction {
fn get_classes(&self) -> &HashMap<AttrString, SchemaClass>;
fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute>;
fn get_attributes_unique(&self) -> &Vec<Attribute>;
fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute>;
fn validate(&self) -> Vec<Result<(), ConsistencyError>> {
let mut res = Vec::with_capacity(0);
let class_snapshot = self.get_classes();
let attribute_snapshot = self.get_attributes();
let mut unique_uuid_set = HashSet::new();
class_snapshot
.values()
.map(|class| &class.uuid)
.chain(attribute_snapshot.values().map(|attr| &attr.uuid))
.for_each(|uuid| {
if !unique_uuid_set.insert(uuid) {
res.push(Err(ConsistencyError::SchemaUuidNotUnique(*uuid)))
}
});
class_snapshot.values().for_each(|class| {
class
.systemmay
.iter()
.chain(class.may.iter())
.chain(class.systemmust.iter())
.chain(class.must.iter())
.for_each(|a| {
match attribute_snapshot.get(a) {
Some(attr) => {
if attr.phantom {
res.push(Err(ConsistencyError::SchemaClassPhantomAttribute(
class.name.to_string(),
a.to_string(),
)))
}
}
None => {
res.push(Err(ConsistencyError::SchemaClassMissingAttribute(
class.name.to_string(),
a.to_string(),
)))
}
}
})
}); res
}
fn is_replicated(&self, attr: &Attribute) -> bool {
match self.get_attributes().get(attr) {
Some(a_schema) => {
a_schema.replicated && !a_schema.phantom
}
None => {
warn!(
"Attribute {} was not found in schema during replication request",
attr
);
false
}
}
}
fn is_multivalue(&self, attr: &Attribute) -> Result<bool, SchemaError> {
match self.get_attributes().get(attr) {
Some(a_schema) => Ok(a_schema.multivalue),
None => {
Err(SchemaError::InvalidAttribute(attr.to_string()))
}
}
}
fn normalise_attr_if_exists(&self, an: &str) -> Option<Attribute> {
let attr = Attribute::from(an);
if self.get_attributes().contains_key(&attr) {
Some(attr)
} else {
None
}
}
fn query_attrs_difference(
&self,
prev_class: &BTreeSet<&str>,
new_class: &BTreeSet<&str>,
) -> Result<(BTreeSet<&str>, BTreeSet<&str>), SchemaError> {
let schema_classes = self.get_classes();
let mut invalid_classes = Vec::with_capacity(0);
let prev_attrs: BTreeSet<&str> = prev_class
.iter()
.filter_map(|cls| match schema_classes.get(*cls) {
Some(x) => Some(x.may_iter()),
None => {
admin_debug!("invalid class: {:?}", cls);
invalid_classes.push(cls.to_string());
None
}
})
.flatten()
.map(|s| s.as_str())
.collect();
if !invalid_classes.is_empty() {
return Err(SchemaError::InvalidClass(invalid_classes));
};
let new_attrs: BTreeSet<&str> = new_class
.iter()
.filter_map(|cls| match schema_classes.get(*cls) {
Some(x) => Some(x.may_iter()),
None => {
admin_debug!("invalid class: {:?}", cls);
invalid_classes.push(cls.to_string());
None
}
})
.flatten()
.map(|s| s.as_str())
.collect();
if !invalid_classes.is_empty() {
return Err(SchemaError::InvalidClass(invalid_classes));
};
let removed = prev_attrs.difference(&new_attrs).copied().collect();
let added = new_attrs.difference(&prev_attrs).copied().collect();
Ok((added, removed))
}
}
impl SchemaWriteTransaction<'_> {
pub fn commit(self) -> Result<(), OperationError> {
let SchemaWriteTransaction {
classes,
attributes,
unique_cache,
ref_cache,
} = self;
unique_cache.commit();
ref_cache.commit();
classes.commit();
attributes.commit();
Ok(())
}
pub fn update_attributes(
&mut self,
attributetypes: Vec<SchemaAttribute>,
) -> Result<(), OperationError> {
self.attributes.clear();
self.unique_cache.clear();
self.ref_cache.clear();
attributetypes.into_iter().for_each(|a| {
if a.syntax == SyntaxType::ReferenceUuid ||
a.syntax == SyntaxType::OauthScopeMap ||
a.syntax == SyntaxType::OauthClaimMap ||
a.syntax == SyntaxType::Oauth2Session ||
a.syntax == SyntaxType::ApplicationPassword
{
self.ref_cache.insert(a.name.clone(), a.clone());
}
if a.unique {
self.unique_cache.push(a.name.clone());
}
self.attributes.insert(a.name.clone(), a);
});
Ok(())
}
pub fn update_classes(&mut self, classtypes: Vec<SchemaClass>) -> Result<(), OperationError> {
self.classes.clear();
classtypes.into_iter().for_each(|a| {
self.classes.insert(a.name.clone(), a);
});
Ok(())
}
pub fn to_entries(&self) -> Vec<Entry<EntryInit, EntryNew>> {
let r: Vec<_> = self
.attributes
.values()
.map(Entry::<EntryInit, EntryNew>::from)
.chain(
self.classes
.values()
.map(Entry::<EntryInit, EntryNew>::from),
)
.collect();
r
}
pub fn reload_idxmeta(&self) -> Vec<IdxKey> {
self.get_attributes()
.values()
.flat_map(|a| {
a.index.iter().map(move |itype: &IndexType| IdxKey {
attr: a.name.clone(),
itype: *itype,
})
})
.collect()
}
#[instrument(level = "debug", name = "schema::generate_in_memory", skip_all)]
pub fn generate_in_memory(&mut self) -> Result<(), OperationError> {
self.classes.clear();
self.attributes.clear();
self.attributes.insert(
Attribute::Class,
SchemaAttribute {
name: Attribute::Class,
uuid: UUID_SCHEMA_ATTR_CLASS,
description: String::from("The set of classes defining an object"),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality, IndexType::Presence],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Uuid,
SchemaAttribute {
name: Attribute::Uuid,
uuid: UUID_SCHEMA_ATTR_UUID,
description: String::from("The universal unique id of the object"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality, IndexType::Presence],
syntax: SyntaxType::Uuid,
},
);
self.attributes.insert(
Attribute::SourceUuid,
SchemaAttribute {
name: Attribute::SourceUuid,
uuid: UUID_SCHEMA_ATTR_SOURCE_UUID,
description: String::from(
"The universal unique id of the source object(s) which conflicted with this entry",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality, IndexType::Presence],
syntax: SyntaxType::Uuid,
},
);
self.attributes.insert(
Attribute::CreatedAtCid,
SchemaAttribute {
name: Attribute::CreatedAtCid,
uuid: UUID_SCHEMA_ATTR_CREATED_AT_CID,
description: String::from("The cid when this entry was created"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Cid,
},
);
self.attributes.insert(
Attribute::LastModifiedCid,
SchemaAttribute {
name: Attribute::LastModifiedCid,
uuid: UUID_SCHEMA_ATTR_LAST_MOD_CID,
description: String::from("The cid of the last change to this object"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Cid,
},
);
self.attributes.insert(
Attribute::Name,
SchemaAttribute {
name: Attribute::Name,
uuid: UUID_SCHEMA_ATTR_NAME,
description: String::from("The shortform name of an object"),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: true,
replicated: true,
index: vec![
IndexType::Equality,
IndexType::Presence,
IndexType::SubString,
],
syntax: SyntaxType::Utf8StringIname,
},
);
self.attributes.insert(
Attribute::Spn,
SchemaAttribute {
name: Attribute::Spn,
uuid: UUID_SCHEMA_ATTR_SPN,
description: String::from(
"The Security Principal Name of an object, unique across all domain trusts",
),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::SecurityPrincipalName,
},
);
self.attributes.insert(
Attribute::AttributeName,
SchemaAttribute {
name: Attribute::AttributeName,
uuid: UUID_SCHEMA_ATTR_ATTRIBUTENAME,
description: String::from("The name of a schema attribute"),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::ClassName,
SchemaAttribute {
name: Attribute::ClassName,
uuid: UUID_SCHEMA_ATTR_CLASSNAME,
description: String::from("The name of a schema class"),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Description,
SchemaAttribute {
name: Attribute::Description,
uuid: UUID_SCHEMA_ATTR_DESCRIPTION,
description: String::from("A description of an attribute, object or class"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: true,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(Attribute::MultiValue, SchemaAttribute {
name: Attribute::MultiValue,
uuid: UUID_SCHEMA_ATTR_MULTIVALUE,
description: String::from("If true, this attribute is able to store multiple values rather than just a single value."),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
});
self.attributes.insert(Attribute::Phantom, SchemaAttribute {
name: Attribute::Phantom,
uuid: UUID_SCHEMA_ATTR_PHANTOM,
description: String::from("If true, this attribute must NOT be present in any may/must sets of a class as. This represents generated attributes."),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
});
self.attributes.insert(Attribute::SyncAllowed, SchemaAttribute {
name: Attribute::SyncAllowed,
uuid: UUID_SCHEMA_ATTR_SYNC_ALLOWED,
description: String::from("If true, this attribute or class can by synchronised by an external scim import"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
});
self.attributes.insert(Attribute::Replicated, SchemaAttribute {
name: Attribute::Replicated,
uuid: UUID_SCHEMA_ATTR_REPLICATED,
description: String::from("If true, this attribute or class can by replicated between nodes in the topology"),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
});
self.attributes.insert(
Attribute::Unique,
SchemaAttribute {
name: Attribute::Unique,
uuid: UUID_SCHEMA_ATTR_UNIQUE,
description: String::from(
"If true, this attribute must store a unique value through out the database.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
},
);
self.attributes.insert(
Attribute::Index,
SchemaAttribute {
name: Attribute::Index,
uuid: UUID_SCHEMA_ATTR_INDEX,
description: String::from(
"Describe the indexes to apply to instances of this attribute.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::IndexId,
},
);
self.attributes.insert(
Attribute::Syntax,
SchemaAttribute {
name: Attribute::Syntax,
uuid: UUID_SCHEMA_ATTR_SYNTAX,
description: String::from(
"Describe the syntax of this attribute. This affects indexing and sorting.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::SyntaxId,
},
);
self.attributes.insert(
Attribute::SystemMay,
SchemaAttribute {
name: Attribute::SystemMay,
uuid: UUID_SCHEMA_ATTR_SYSTEMMAY,
description: String::from(
"A list of system provided optional attributes this class can store.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::May,
SchemaAttribute {
name: Attribute::May,
uuid: UUID_SCHEMA_ATTR_MAY,
description: String::from(
"A user modifiable list of optional attributes this class can store.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::SystemMust,
SchemaAttribute {
name: Attribute::SystemMust,
uuid: UUID_SCHEMA_ATTR_SYSTEMMUST,
description: String::from(
"A list of system provided required attributes this class must store.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Must,
SchemaAttribute {
name: Attribute::Must,
uuid: UUID_SCHEMA_ATTR_MUST,
description: String::from(
"A user modifiable list of required attributes this class must store.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::SystemSupplements,
SchemaAttribute {
name: Attribute::SystemSupplements,
uuid: UUID_SCHEMA_ATTR_SYSTEMSUPPLEMENTS,
description: String::from(
"A set of classes that this type supplements too, where this class can't exist without their presence.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Supplements,
SchemaAttribute {
name: Attribute::Supplements,
uuid: UUID_SCHEMA_ATTR_SUPPLEMENTS,
description: String::from(
"A set of user modifiable classes, where this determines that at least one other type must supplement this type",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::SystemExcludes,
SchemaAttribute {
name: Attribute::SystemExcludes,
uuid: UUID_SCHEMA_ATTR_SYSTEMEXCLUDES,
description: String::from(
"A set of classes that are denied presence in connection to this class",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Excludes,
SchemaAttribute {
name: Attribute::Excludes,
uuid: UUID_SCHEMA_ATTR_EXCLUDES,
description: String::from(
"A set of user modifiable classes that are denied presence in connection to this class",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpEnable,
SchemaAttribute {
name: Attribute::AcpEnable,
uuid: UUID_SCHEMA_ATTR_ACP_ENABLE,
description: String::from("A flag to determine if this ACP is active for application. True is enabled, and enforce. False is checked but not enforced."),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Boolean,
},
);
self.attributes.insert(
Attribute::AcpReceiver,
SchemaAttribute {
name: Attribute::AcpReceiver,
uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER,
description: String::from(
"Who the ACP applies to, constraining or allowing operations.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::JsonFilter,
},
);
self.attributes.insert(
Attribute::AcpReceiverGroup,
SchemaAttribute {
name: Attribute::AcpReceiverGroup,
uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER_GROUP,
description: String::from(
"The group that receives this access control to allow access",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::AcpTargetScope,
SchemaAttribute {
name: Attribute::AcpTargetScope,
uuid: UUID_SCHEMA_ATTR_ACP_TARGETSCOPE,
description: String::from(
"The effective targets of the ACP, e.g. what will be acted upon.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::JsonFilter,
},
);
self.attributes.insert(
Attribute::AcpSearchAttr,
SchemaAttribute {
name: Attribute::AcpSearchAttr,
uuid: UUID_SCHEMA_ATTR_ACP_SEARCH_ATTR,
description: String::from(
"The attributes that may be viewed or searched by the receiver on targetscope.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpCreateClass,
SchemaAttribute {
name: Attribute::AcpCreateClass,
uuid: UUID_SCHEMA_ATTR_ACP_CREATE_CLASS,
description: String::from("The set of classes that can be created on a new entry."),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpCreateAttr,
SchemaAttribute {
name: Attribute::AcpCreateAttr,
uuid: UUID_SCHEMA_ATTR_ACP_CREATE_ATTR,
description: String::from(
"The set of attribute types that can be created on an entry.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpModifyRemovedAttr,
SchemaAttribute {
name: Attribute::AcpModifyRemovedAttr,
uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_REMOVEDATTR,
description: String::from(
"The set of attribute types that could be removed or purged in a modification.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpModifyPresentAttr,
SchemaAttribute {
name: Attribute::AcpModifyPresentAttr,
uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_PRESENTATTR,
description: String::from(
"The set of attribute types that could be added or asserted in a modification.",
),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::AcpModifyClass,
SchemaAttribute {
name: Attribute::AcpModifyClass,
uuid: UUID_SCHEMA_ATTR_ACP_MODIFY_CLASS,
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."),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::EntryManagedBy,
SchemaAttribute {
name: Attribute::EntryManagedBy,
uuid: UUID_SCHEMA_ATTR_ENTRY_MANAGED_BY,
description: String::from(
"A reference to a group that has access to manage the content of this entry.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::MemberOf,
SchemaAttribute {
name: Attribute::MemberOf,
uuid: UUID_SCHEMA_ATTR_MEMBEROF,
description: String::from("reverse group membership of the object"),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: false,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::DirectMemberOf,
SchemaAttribute {
name: Attribute::DirectMemberOf,
uuid: UUID_SCHEMA_ATTR_DIRECTMEMBEROF,
description: String::from("reverse direct group membership of the object"),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: false,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::RecycledDirectMemberOf,
SchemaAttribute {
name: Attribute::RecycledDirectMemberOf,
uuid: UUID_SCHEMA_ATTR_RECYCLEDDIRECTMEMBEROF,
description: String::from("recycled reverse direct group membership of the object to assist in revive operations."),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::Member,
SchemaAttribute {
name: Attribute::Member,
uuid: UUID_SCHEMA_ATTR_MEMBER,
description: String::from("List of members of the group"),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: true,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::DynMember,
SchemaAttribute {
name: Attribute::DynMember,
uuid: UUID_SCHEMA_ATTR_DYNMEMBER,
description: String::from("List of dynamic members of the group"),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: true,
replicated: false,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::Version,
SchemaAttribute {
name: Attribute::Version,
uuid: UUID_SCHEMA_ATTR_VERSION,
description: String::from(
"The systems internal migration version for provided objects",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Uint32,
},
);
self.attributes.insert(
Attribute::Domain,
SchemaAttribute {
name: Attribute::Domain,
uuid: UUID_SCHEMA_ATTR_DOMAIN,
description: String::from("A DNS Domain name entry."),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringIname,
},
);
self.attributes.insert(
Attribute::Claim,
SchemaAttribute {
name: Attribute::Claim,
uuid: UUID_SCHEMA_ATTR_CLAIM,
description: String::from(
"The string identifier of an extracted claim that can be filtered",
),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Scope,
SchemaAttribute {
name: Attribute::Scope,
uuid: UUID_SCHEMA_ATTR_SCOPE,
description: String::from(
"The string identifier of a permission scope in a session",
),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::SyncExternalId,
SchemaAttribute {
name: Attribute::SyncExternalId,
uuid: UUID_SCHEMA_ATTR_SYNC_EXTERNAL_ID,
description: String::from(
"An external string ID of an entry imported from a sync agreement",
),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::SyncParentUuid,
SchemaAttribute {
name: Attribute::SyncParentUuid,
uuid: UUID_SCHEMA_ATTR_SYNC_PARENT_UUID,
description: String::from(
"The UUID of the parent sync agreement that created this entry.",
),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::ReferenceUuid,
},
);
self.attributes.insert(
Attribute::SyncClass,
SchemaAttribute {
name: Attribute::SyncClass,
uuid: UUID_SCHEMA_ATTR_SYNC_CLASS,
description: String::from("The set of classes requested by the sync client."),
multivalue: true,
unique: false,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::PasswordImport,
SchemaAttribute {
name: Attribute::PasswordImport,
uuid: UUID_SCHEMA_ATTR_PASSWORD_IMPORT,
description: String::from("An imported password hash from an external system."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: true,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::UnixPasswordImport,
SchemaAttribute {
name: Attribute::UnixPasswordImport,
uuid: UUID_SCHEMA_ATTR_UNIX_PASSWORD_IMPORT,
description: String::from(
"An imported unix password hash from an external system.",
),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: true,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::TotpImport,
SchemaAttribute {
name: Attribute::TotpImport,
uuid: UUID_SCHEMA_ATTR_TOTP_IMPORT,
description: String::from("An imported totp secret from an external system."),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: true,
replicated: false,
index: vec![],
syntax: SyntaxType::TotpSecret,
},
);
self.attributes.insert(
Attribute::Dn,
SchemaAttribute {
name: Attribute::Dn,
uuid: UUID_SCHEMA_ATTR_DN,
description: String::from("An LDAP Compatible DN"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::EntryDn,
SchemaAttribute {
name: Attribute::EntryDn,
uuid: UUID_SCHEMA_ATTR_ENTRYDN,
description: String::from("An LDAP Compatible EntryDN"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::EntryUuid,
SchemaAttribute {
name: Attribute::EntryUuid,
uuid: UUID_SCHEMA_ATTR_ENTRYUUID,
description: String::from("An LDAP Compatible entryUUID"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Uuid,
},
);
self.attributes.insert(
Attribute::ObjectClass,
SchemaAttribute {
name: Attribute::ObjectClass,
uuid: UUID_SCHEMA_ATTR_OBJECTCLASS,
description: String::from("An LDAP Compatible objectClass"),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8StringInsensitive,
},
);
self.attributes.insert(
Attribute::Cn,
SchemaAttribute {
name: Attribute::Cn,
uuid: UUID_SCHEMA_ATTR_CN,
description: String::from("An LDAP Compatible objectClass"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8StringIname,
},
);
self.attributes.insert(
Attribute::LdapKeys, SchemaAttribute {
name: Attribute::LdapKeys, uuid: UUID_SCHEMA_ATTR_KEYS,
description: String::from("An LDAP Compatible keys (ssh)"),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::SshKey,
},
);
self.attributes.insert(
Attribute::LdapSshPublicKey,
SchemaAttribute {
name: Attribute::LdapSshPublicKey,
uuid: UUID_SCHEMA_ATTR_SSHPUBLICKEY,
description: String::from("An LDAP Compatible sshPublicKey"),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::SshKey,
},
);
self.attributes.insert(
Attribute::Email,
SchemaAttribute {
name: Attribute::Email,
uuid: UUID_SCHEMA_ATTR_EMAIL,
description: String::from("An LDAP Compatible email"),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::EmailAddress,
},
);
self.attributes.insert(
Attribute::EmailPrimary,
SchemaAttribute {
name: Attribute::EmailPrimary,
uuid: UUID_SCHEMA_ATTR_EMAILPRIMARY,
description: String::from("An LDAP Compatible primary email"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::EmailAddress,
},
);
self.attributes.insert(
Attribute::EmailAlternative,
SchemaAttribute {
name: Attribute::EmailAlternative,
uuid: UUID_SCHEMA_ATTR_EMAILALTERNATIVE,
description: String::from("An LDAP Compatible alternative email"),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::EmailAddress,
},
);
self.attributes.insert(
Attribute::LdapEmailAddress,
SchemaAttribute {
name: Attribute::LdapEmailAddress,
uuid: UUID_SCHEMA_ATTR_EMAILADDRESS,
description: String::from("An LDAP Compatible emailAddress"),
multivalue: true,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::EmailAddress,
},
);
self.attributes.insert(
Attribute::Gecos,
SchemaAttribute {
name: Attribute::Gecos,
uuid: UUID_SCHEMA_ATTR_GECOS,
description: String::from("An LDAP Compatible gecos."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::Uid,
SchemaAttribute {
name: Attribute::Uid,
uuid: UUID_SCHEMA_ATTR_UID,
description: String::from("An LDAP Compatible uid."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::UidNumber,
SchemaAttribute {
name: Attribute::UidNumber,
uuid: UUID_SCHEMA_ATTR_UIDNUMBER,
description: String::from("An LDAP Compatible uidNumber."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Uint32,
},
);
self.attributes.insert(
Attribute::SudoHost,
SchemaAttribute {
name: Attribute::SudoHost,
uuid: UUID_SCHEMA_ATTR_SUDOHOST,
description: String::from("An LDAP Compatible sudohost."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::Image,
SchemaAttribute {
name: Attribute::Image,
uuid: UUID_SCHEMA_ATTR_IMAGE,
description: String::from("An image for display to end users."),
multivalue: false,
unique: false,
phantom: false,
sync_allowed: true,
replicated: true,
index: vec![],
syntax: SyntaxType::Image,
},
);
self.attributes.insert(
Attribute::OAuth2DeviceFlowEnable,
SchemaAttribute {
name: Attribute::OAuth2DeviceFlowEnable,
uuid: UUID_SCHEMA_ATTR_OAUTH2_DEVICE_FLOW_ENABLE,
description: String::from("Enable the OAuth2 Device Flow for this client."),
multivalue: false,
unique: true,
phantom: false,
sync_allowed: false,
replicated: true,
index: vec![],
syntax: SyntaxType::Boolean,
},
);
self.classes.insert(
EntryClass::AttributeType.into(),
SchemaClass {
name: EntryClass::AttributeType.into(),
uuid: UUID_SCHEMA_CLASS_ATTRIBUTETYPE,
description: String::from("Definition of a schema attribute"),
systemmay: vec![
Attribute::Replicated,
Attribute::Phantom,
Attribute::SyncAllowed,
Attribute::Index,
],
systemmust: vec![
Attribute::Class,
Attribute::AttributeName,
Attribute::MultiValue,
Attribute::Unique,
Attribute::Syntax,
Attribute::Description,
],
systemexcludes: vec![EntryClass::ClassType.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::ClassType.into(),
SchemaClass {
name: EntryClass::ClassType.into(),
uuid: UUID_SCHEMA_CLASS_CLASSTYPE,
description: String::from("Definition of a schema classtype"),
systemmay: vec![
Attribute::SyncAllowed,
Attribute::SystemMay,
Attribute::May,
Attribute::SystemMust,
Attribute::Must,
Attribute::SystemSupplements,
Attribute::Supplements,
Attribute::SystemExcludes,
Attribute::Excludes,
],
systemmust: vec![
Attribute::Class,
Attribute::ClassName,
Attribute::Description,
],
systemexcludes: vec![Attribute::AttributeType.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::Object.into(),
SchemaClass {
name: EntryClass::Object.into(),
uuid: UUID_SCHEMA_CLASS_OBJECT,
description: String::from("A system created class that all objects must contain"),
systemmay: vec![Attribute::Description, Attribute::EntryManagedBy],
systemmust: vec![
Attribute::Class,
Attribute::Uuid,
Attribute::LastModifiedCid,
Attribute::CreatedAtCid,
],
..Default::default()
},
);
self.classes.insert(
EntryClass::Builtin.into(),
SchemaClass {
name: EntryClass::Builtin.into(),
uuid: UUID_SCHEMA_CLASS_BUILTIN,
description: String::from("A marker class denoting builtin entries"),
..Default::default()
},
);
self.classes.insert(
EntryClass::MemberOf.into(),
SchemaClass {
name: EntryClass::MemberOf.into(),
uuid: UUID_SCHEMA_CLASS_MEMBEROF,
description: String::from(
"Class that is dynamically added to recipients of memberof or directmemberof",
),
systemmay: vec![Attribute::MemberOf, Attribute::DirectMemberOf],
..Default::default()
},
);
self.classes.insert(
EntryClass::ExtensibleObject.into(),
SchemaClass {
name: EntryClass::ExtensibleObject.into(),
uuid: UUID_SCHEMA_CLASS_EXTENSIBLEOBJECT,
description: String::from(
"A class type that has green hair and turns off all rules ...",
),
..Default::default()
},
);
self.classes.insert(
EntryClass::Recycled.into(),
SchemaClass {
name: EntryClass::Recycled.into(),
uuid: UUID_SCHEMA_CLASS_RECYCLED,
description: String::from("An object that has been deleted, but still recoverable via the revive operation. Recycled objects are not modifiable, only revivable."),
systemmay: vec![Attribute::RecycledDirectMemberOf],
.. Default::default()
},
);
self.classes.insert(
EntryClass::Tombstone.into(),
SchemaClass {
name: EntryClass::Tombstone.into(),
uuid: UUID_SCHEMA_CLASS_TOMBSTONE,
description: String::from("An object that is purged from the recycle bin. This is a system internal state. Tombstones have no attributes beside UUID."),
systemmust: vec![
Attribute::Class,
Attribute::Uuid,
],
.. Default::default()
},
);
self.classes.insert(
EntryClass::Conflict.into(),
SchemaClass {
name: EntryClass::Conflict.into(),
uuid: UUID_SCHEMA_CLASS_CONFLICT,
description: String::from(
"An entry representing conflicts that occurred during replication",
),
systemmust: vec![Attribute::SourceUuid],
systemsupplements: vec![EntryClass::Recycled.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::SystemInfo.into(),
SchemaClass {
name: EntryClass::SystemInfo.into(),
uuid: UUID_SCHEMA_CLASS_SYSTEM_INFO,
description: String::from("System metadata object class"),
systemmust: vec![Attribute::Version],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlSearch.into(),
SchemaClass {
name: EntryClass::AccessControlSearch.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_SEARCH,
description: String::from("System Access Control Search Class"),
systemmust: vec![Attribute::AcpSearchAttr],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlDelete.into(),
SchemaClass {
name: EntryClass::AccessControlDelete.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_DELETE,
description: String::from("System Access Control DELETE Class"),
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlModify.into(),
SchemaClass {
name: EntryClass::AccessControlModify.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_MODIFY,
description: String::from("System Access Control Modify Class"),
systemmay: vec![
Attribute::AcpModifyRemovedAttr,
Attribute::AcpModifyPresentAttr,
Attribute::AcpModifyClass,
],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlCreate.into(),
SchemaClass {
name: EntryClass::AccessControlCreate.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_CREATE,
description: String::from("System Access Control Create Class"),
systemmay: vec![Attribute::AcpCreateClass, Attribute::AcpCreateAttr],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlProfile.into(),
SchemaClass {
name: EntryClass::AccessControlProfile.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_PROFILE,
description: String::from("System Access Control Profile Class"),
systemmay: vec![Attribute::AcpEnable, Attribute::Description],
systemmust: vec![Attribute::Name],
systemsupplements: vec![
EntryClass::AccessControlSearch.into(),
EntryClass::AccessControlDelete.into(),
EntryClass::AccessControlModify.into(),
EntryClass::AccessControlCreate.into(),
],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlReceiverEntryManager.into(),
SchemaClass {
name: EntryClass::AccessControlReceiverEntryManager.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_RECEIVER_ENTRY_MANAGER,
description: String::from("System Access Control Profile Receiver - Entry Manager"),
systemexcludes: vec![EntryClass::AccessControlReceiverGroup.into()],
systemsupplements: vec![EntryClass::AccessControlProfile.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlReceiverGroup.into(),
SchemaClass {
name: EntryClass::AccessControlReceiverGroup.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_RECEIVER_GROUP,
description: String::from("System Access Control Profile Receiver - Group"),
systemmay: vec![Attribute::AcpReceiver],
systemmust: vec![Attribute::AcpReceiverGroup],
systemsupplements: vec![EntryClass::AccessControlProfile.into()],
systemexcludes: vec![EntryClass::AccessControlReceiverEntryManager.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::AccessControlTargetScope.into(),
SchemaClass {
name: EntryClass::AccessControlTargetScope.into(),
uuid: UUID_SCHEMA_CLASS_ACCESS_CONTROL_TARGET_SCOPE,
description: String::from("System Access Control Profile Target - Scope"),
systemmust: vec![Attribute::AcpTargetScope],
systemsupplements: vec![EntryClass::AccessControlProfile.into()],
..Default::default()
},
);
self.classes.insert(
EntryClass::System.into(),
SchemaClass {
name: EntryClass::System.into(),
uuid: UUID_SCHEMA_CLASS_SYSTEM,
description: String::from("A class denoting that a type is system generated and protected. It has special internal behaviour."),
.. Default::default()
},
);
self.classes.insert(
EntryClass::SyncObject.into(),
SchemaClass {
name: EntryClass::SyncObject.into(),
uuid: UUID_SCHEMA_CLASS_SYNC_OBJECT,
description: String::from("A class denoting that an entry is synchronised from an external source. This entry may not be modifiable."),
systemmust: vec![
Attribute::SyncParentUuid
],
systemmay: vec![
Attribute::SyncExternalId,
Attribute::SyncClass,
],
.. Default::default()
},
);
let r = self.validate();
if r.is_empty() {
admin_debug!("schema validate -> passed");
Ok(())
} else {
admin_error!(err = ?r, "schema validate -> errors");
Err(OperationError::ConsistencyError(
r.into_iter().filter_map(|v| v.err()).collect(),
))
}
}
}
impl SchemaTransaction for SchemaWriteTransaction<'_> {
fn get_attributes_unique(&self) -> &Vec<Attribute> {
&self.unique_cache
}
fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute> {
&self.ref_cache
}
fn get_classes(&self) -> &HashMap<AttrString, SchemaClass> {
&self.classes
}
fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute> {
&self.attributes
}
}
impl SchemaTransaction for SchemaReadTransaction {
fn get_attributes_unique(&self) -> &Vec<Attribute> {
&self.unique_cache
}
fn get_reference_types(&self) -> &HashMap<Attribute, SchemaAttribute> {
&self.ref_cache
}
fn get_classes(&self) -> &HashMap<AttrString, SchemaClass> {
&self.classes
}
fn get_attributes(&self) -> &HashMap<Attribute, SchemaAttribute> {
&self.attributes
}
}
impl Schema {
pub fn new() -> Result<Self, OperationError> {
let s = Schema {
classes: CowCell::new(HashMap::with_capacity(128)),
attributes: CowCell::new(HashMap::with_capacity(128)),
unique_cache: CowCell::new(Vec::with_capacity(0)),
ref_cache: CowCell::new(HashMap::with_capacity(64)),
};
let mut sw = s.write();
let r1 = sw.generate_in_memory();
debug_assert!(r1.is_ok());
r1?;
let r2 = sw.commit().map(|_| s);
debug_assert!(r2.is_ok());
r2
}
pub fn read(&self) -> SchemaReadTransaction {
SchemaReadTransaction {
classes: self.classes.read(),
attributes: self.attributes.read(),
unique_cache: self.unique_cache.read(),
ref_cache: self.ref_cache.read(),
}
}
pub fn write(&self) -> SchemaWriteTransaction<'_> {
SchemaWriteTransaction {
classes: self.classes.write(),
attributes: self.attributes.write(),
unique_cache: self.unique_cache.write(),
ref_cache: self.ref_cache.write(),
}
}
#[cfg(test)]
pub(crate) fn write_blocking(&self) -> SchemaWriteTransaction<'_> {
self.write()
}
}
#[cfg(test)]
mod tests {
use crate::prelude::*;
use crate::schema::{
IndexType, Schema, SchemaAttribute, SchemaClass, SchemaTransaction, SyntaxType,
};
use uuid::Uuid;
macro_rules! validate_schema {
($sch:ident) => {{
let r: Result<Vec<()>, ConsistencyError> = $sch.validate().into_iter().collect();
assert!(r.is_ok());
}};
}
macro_rules! sch_from_entry_ok {
(
$e:expr,
$type:ty
) => {{
let ev1 = $e.into_sealed_committed();
let r1 = <$type>::try_from(&ev1);
assert!(r1.is_ok());
}};
}
macro_rules! sch_from_entry_err {
(
$e:expr,
$type:ty
) => {{
let ev1 = $e.into_sealed_committed();
let r1 = <$type>::try_from(&ev1);
assert!(r1.is_err());
}};
}
#[test]
fn test_schema_attribute_from_entry() {
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(Attribute::Unique, Value::Bool(false))
),
SchemaAttribute
);
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(Attribute::Index, Value::Index(IndexType::Equality))
),
SchemaAttribute
);
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("Test attr parsing".to_string())
),
(Attribute::MultiValue, Value::Utf8("htouaoeu".to_string())),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(Attribute::Index, Value::Index(IndexType::Equality))
),
SchemaAttribute
);
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("Test attr parsing".to_string())
),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(Attribute::Index, Value::Utf8("NTEHNOU".to_string()))
),
SchemaAttribute
);
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("Test attr parsing".to_string())
),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Utf8("TNEOUNTUH".to_string())),
(Attribute::Index, Value::Index(IndexType::Equality))
),
SchemaAttribute
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("Test attr parsing".to_string())
),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String))
),
SchemaAttribute
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(
Attribute::AttributeName,
Value::new_iutf8("schema_attr_test")
),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("Test attr parsing".to_string())
),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(Attribute::Index, Value::Index(IndexType::Equality))
),
SchemaAttribute
);
}
#[test]
fn test_schema_class_from_entry() {
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
)
),
SchemaClass
);
sch_from_entry_err!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
)
),
SchemaClass
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
)
),
SchemaClass
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
),
(Attribute::SystemMust, Value::new_iutf8("a"))
),
SchemaClass
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
),
(Attribute::SystemMay, Value::new_iutf8("a"))
),
SchemaClass
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
),
(Attribute::May, Value::new_iutf8("a")),
(Attribute::Must, Value::new_iutf8("b"))
),
SchemaClass
);
sch_from_entry_ok!(
entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::ClassType.to_value()),
(Attribute::ClassName, Value::new_iutf8("schema_class_test")),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("66c68b2f-d02c-4243-8013-7946e40fe321"))
),
(
Attribute::Description,
Value::Utf8("class test".to_string())
),
(Attribute::May, Value::new_iutf8("a")),
(Attribute::Must, Value::new_iutf8("b")),
(Attribute::SystemMay, Value::new_iutf8("c")),
(Attribute::SystemMust, Value::new_iutf8("d"))
),
SchemaClass
);
}
#[test]
fn test_schema_attribute_simple() {
let single_value_string = SchemaAttribute {
name: Attribute::from("single_value"),
uuid: Uuid::new_v4(),
description: String::from(""),
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8StringInsensitive,
..Default::default()
};
let r1 = single_value_string
.validate_ava(&Attribute::from("single_value"), &(vs_iutf8!["test"] as _));
assert_eq!(r1, Ok(()));
let rvs = vs_iutf8!["test1", "test2"] as _;
let r2 = single_value_string.validate_ava(&Attribute::from("single_value"), &rvs);
assert_eq!(
r2,
Err(SchemaError::InvalidAttributeSyntax(
"single_value".to_string()
))
);
let multi_value_string = SchemaAttribute {
name: Attribute::from("mv_string"),
uuid: Uuid::new_v4(),
description: String::from(""),
multivalue: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Utf8String,
..Default::default()
};
let rvs = vs_utf8!["test1".to_string(), "test2".to_string()] as _;
let r5 = multi_value_string.validate_ava(&Attribute::from("mv_string"), &rvs);
assert_eq!(r5, Ok(()));
let multi_value_boolean = SchemaAttribute {
name: Attribute::from("mv_bool"),
uuid: Uuid::new_v4(),
description: String::from(""),
multivalue: true,
index: vec![IndexType::Equality],
syntax: SyntaxType::Boolean,
..Default::default()
};
let rvs = vs_bool![true, false];
let r4 = multi_value_boolean.validate_ava(&Attribute::from("mv_bool"), &(rvs as _));
assert_eq!(r4, Ok(()));
let single_value_syntax = SchemaAttribute {
name: Attribute::from("sv_syntax"),
uuid: Uuid::new_v4(),
description: String::from(""),
index: vec![IndexType::Equality],
syntax: SyntaxType::SyntaxId,
..Default::default()
};
let rvs = vs_syntax![SyntaxType::try_from("UTF8STRING").unwrap()] as _;
let r6 = single_value_syntax.validate_ava(&Attribute::from("sv_syntax"), &rvs);
assert_eq!(r6, Ok(()));
let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
let r7 = single_value_syntax.validate_ava(&Attribute::from("sv_syntax"), &rvs);
assert_eq!(
r7,
Err(SchemaError::InvalidAttributeSyntax("sv_syntax".to_string()))
);
let single_value_index = SchemaAttribute {
name: Attribute::from("sv_index"),
uuid: Uuid::new_v4(),
description: String::from(""),
index: vec![IndexType::Equality],
syntax: SyntaxType::IndexId,
..Default::default()
};
let rvs = vs_index![IndexType::try_from("EQUALITY").unwrap()] as _;
let r8 = single_value_index.validate_ava(&Attribute::from("sv_index"), &rvs);
assert_eq!(r8, Ok(()));
let rvs = vs_utf8!["thaeountaheu".to_string()] as _;
let r9 = single_value_index.validate_ava(&Attribute::from("sv_index"), &rvs);
assert_eq!(
r9,
Err(SchemaError::InvalidAttributeSyntax("sv_index".to_string()))
);
}
#[test]
fn test_schema_simple() {
let schema = Schema::new().expect("failed to create schema");
let schema_ro = schema.read();
validate_schema!(schema_ro);
}
#[test]
fn test_schema_entries() {
sketching::test_init();
let schema_outer = Schema::new().expect("failed to create schema");
let schema = schema_outer.read();
let e_no_uuid = entry_init!().into_invalid_new();
assert_eq!(
e_no_uuid.validate(&schema),
Err(SchemaError::MissingMustAttribute(vec![Attribute::Uuid]))
);
let e_no_class = entry_init!((
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
))
.into_invalid_new();
assert_eq!(e_no_class.validate(&schema), Err(SchemaError::NoClassFound));
let e_bad_class = entry_init!(
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::Class, Value::new_class("zzzzzz"))
)
.into_invalid_new();
assert_eq!(
e_bad_class.validate(&schema),
Err(SchemaError::InvalidClass(vec!["zzzzzz".to_string()]))
);
let e_attr_invalid = entry_init!(
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value())
)
.into_invalid_new();
let res = e_attr_invalid.validate(&schema);
matches!(res, Err(SchemaError::MissingMustAttribute(_)));
let e_attr_invalid_may = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(Attribute::AttributeName, Value::new_iutf8("testattr")),
(Attribute::Description, Value::Utf8("testattr".to_string())),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::TestAttr, Value::Utf8("zzzz".to_string()))
)
.into_invalid_new();
assert_eq!(
e_attr_invalid_may.validate(&schema),
Err(SchemaError::AttributeNotValidForClass(
Attribute::TestAttr.to_string()
))
);
let e_attr_invalid_syn = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(Attribute::AttributeName, Value::new_iutf8("testattr")),
(Attribute::Description, Value::Utf8("testattr".to_string())),
(Attribute::MultiValue, Value::Utf8("false".to_string())),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
)
)
.into_invalid_new();
assert_eq!(
e_attr_invalid_syn.validate(&schema),
Err(SchemaError::InvalidAttributeSyntax(
"multivalue".to_string()
))
);
let e_phantom = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(Attribute::AttributeName, Value::new_iutf8("testattr")),
(Attribute::Description, Value::Utf8("testattr".to_string())),
(Attribute::MultiValue, Value::Bool(false)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(
Attribute::PasswordImport,
Value::Utf8("password".to_string())
)
)
.into_invalid_new();
assert!(e_phantom.validate(&schema).is_err());
let e_ok = entry_init!(
(Attribute::Class, EntryClass::Object.to_value()),
(Attribute::Class, EntryClass::AttributeType.to_value()),
(Attribute::AttributeName, Value::new_iutf8("testattr")),
(Attribute::Description, Value::Utf8("testattr".to_string())),
(Attribute::MultiValue, Value::Bool(true)),
(Attribute::Unique, Value::Bool(false)),
(Attribute::Syntax, Value::Syntax(SyntaxType::Utf8String)),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
)
)
.into_invalid_new();
assert!(e_ok.validate(&schema).is_ok());
}
#[test]
fn test_schema_extensible() {
let schema_outer = Schema::new().expect("failed to create schema");
let schema = schema_outer.read();
let e_extensible_bad = entry_init!(
(Attribute::Class, EntryClass::ExtensibleObject.to_value()),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::MultiValue, Value::Utf8("zzzz".to_string()))
)
.into_invalid_new();
assert_eq!(
e_extensible_bad.validate(&schema),
Err(SchemaError::InvalidAttributeSyntax(
"multivalue".to_string()
))
);
let e_extensible_phantom = entry_init!(
(Attribute::Class, EntryClass::ExtensibleObject.to_value()),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::PasswordImport, Value::Utf8("zzzz".to_string()))
)
.into_invalid_new();
assert_eq!(
e_extensible_phantom.validate(&schema),
Err(SchemaError::PhantomAttribute(
Attribute::PasswordImport.to_string()
))
);
let e_extensible = entry_init!(
(Attribute::Class, EntryClass::ExtensibleObject.to_value()),
(
Attribute::Uuid,
Value::Uuid(uuid::uuid!("db237e8a-0079-4b8c-8a56-593b22aa44d1"))
),
(Attribute::MultiValue, Value::Bool(true))
)
.into_invalid_new();
assert!(e_extensible.validate(&schema).is_ok());
}
#[test]
fn test_schema_filter_validation() {
let schema_outer = Schema::new().expect("failed to create schema");
let schema = schema_outer.read();
let f_bool = filter_all!(f_eq(Attribute::MultiValue, PartialValue::new_iutf8("zzzz")));
assert_eq!(
f_bool.validate(&schema),
Err(SchemaError::InvalidAttributeSyntax(
"multivalue".to_string()
))
);
let f_insense = filter_all!(f_eq(Attribute::Class, EntryClass::AttributeType.into()));
assert_eq!(
f_insense.validate(&schema),
Ok(filter_valid!(f_eq(
Attribute::Class,
EntryClass::AttributeType.into()
)))
);
let f_or_empty = filter_all!(f_or!([]));
assert_eq!(f_or_empty.validate(&schema), Err(SchemaError::EmptyFilter));
let f_or = filter_all!(f_or!([f_eq(
Attribute::MultiValue,
PartialValue::new_iutf8("zzzz")
)]));
assert_eq!(
f_or.validate(&schema),
Err(SchemaError::InvalidAttributeSyntax(
"multivalue".to_string()
))
);
let f_or_mult = filter_all!(f_and!([
f_eq(Attribute::Class, EntryClass::AttributeType.into()),
f_eq(Attribute::MultiValue, PartialValue::new_iutf8("zzzzzzz")),
]));
assert_eq!(
f_or_mult.validate(&schema),
Err(SchemaError::InvalidAttributeSyntax(
"multivalue".to_string()
))
);
let f_or_ok = filter_all!(f_andnot(f_and!([
f_eq(Attribute::Class, EntryClass::AttributeType.into()),
f_sub(Attribute::Class, EntryClass::ClassType.into()),
f_pres(Attribute::Class)
])));
assert_eq!(
f_or_ok.validate(&schema),
Ok(filter_valid!(f_andnot(f_and!([
f_eq(Attribute::Class, EntryClass::AttributeType.into()),
f_sub(Attribute::Class, EntryClass::ClassType.into()),
f_pres(Attribute::Class)
]))))
);
}
#[test]
fn test_schema_class_phantom_reject() {
let schema_outer = Schema::new().expect("failed to create schema");
let mut schema = schema_outer.write_blocking();
assert!(schema.validate().is_empty());
let class = SchemaClass {
name: AttrString::from("testobject"),
uuid: Uuid::new_v4(),
description: String::from("test object"),
systemmay: vec![Attribute::Claim],
..Default::default()
};
assert!(schema.update_classes(vec![class]).is_ok());
assert_eq!(schema.validate().len(), 1);
}
#[test]
fn test_schema_class_exclusion_requires() {
sketching::test_init();
let schema_outer = Schema::new().expect("failed to create schema");
let mut schema = schema_outer.write_blocking();
assert!(schema.validate().is_empty());
let class_account = SchemaClass {
name: Attribute::Account.into(),
uuid: Uuid::new_v4(),
description: String::from("account object"),
systemmust: vec![
Attribute::Class,
Attribute::Uuid,
Attribute::LastModifiedCid,
Attribute::CreatedAtCid,
],
systemsupplements: vec![EntryClass::Service.into(), EntryClass::Person.into()],
..Default::default()
};
let class_person = SchemaClass {
name: EntryClass::Person.into(),
uuid: Uuid::new_v4(),
description: String::from("person object"),
systemmust: vec![
Attribute::Class,
Attribute::Uuid,
Attribute::LastModifiedCid,
Attribute::CreatedAtCid,
],
..Default::default()
};
let class_service = SchemaClass {
name: EntryClass::Service.into(),
uuid: Uuid::new_v4(),
description: String::from("service object"),
systemmust: vec![
Attribute::Class,
Attribute::Uuid,
Attribute::LastModifiedCid,
Attribute::CreatedAtCid,
],
excludes: vec![EntryClass::Person.into()],
..Default::default()
};
assert!(schema
.update_classes(vec![class_account, class_person, class_service])
.is_ok());
let e_account = entry_init!(
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
)
.into_invalid_new();
assert_eq!(
e_account.validate(&schema),
Err(SchemaError::SupplementsNotSatisfied(vec![
EntryClass::Service.into(),
EntryClass::Person.into(),
]))
);
let e_service_person = entry_init!(
(Attribute::Class, EntryClass::Service.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
)
.into_invalid_new();
assert_eq!(
e_service_person.validate(&schema),
Err(SchemaError::ExcludesNotSatisfied(vec![
EntryClass::Person.to_string()
]))
);
let e_service_valid = entry_init!(
(Attribute::Class, EntryClass::Service.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
)
.into_invalid_new();
assert!(e_service_valid.validate(&schema).is_ok());
let e_person_valid = entry_init!(
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
)
.into_invalid_new();
assert!(e_person_valid.validate(&schema).is_ok());
let e_person_valid = entry_init!(
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Uuid, Value::Uuid(Uuid::new_v4()))
)
.into_invalid_new();
assert!(e_person_valid.validate(&schema).is_ok());
}
}