mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Implement attribute uniqueness. (#82)
Implements #72, attribute uniqueness. This extends schema to have a field "unique" which means a value should be unique for that object, and all other live objects (recycle and tombstones excluded). Note UUID has to retain special handling in base.rs and can't usethis due to needing to check with recycled too..
This commit is contained in:
parent
b4fc71b27d
commit
d436291eff
|
@ -65,6 +65,7 @@ pub enum ConsistencyError {
|
|||
RefintNotUpheld(u64),
|
||||
MemberOfInvalid(u64),
|
||||
InvalidAttributeType(&'static str),
|
||||
DuplicateUniqueAttribute(String),
|
||||
}
|
||||
|
||||
/* ===== higher level types ===== */
|
||||
|
|
|
@ -489,7 +489,9 @@ pub trait AccessControlsTransaction {
|
|||
// true -> entry is allowed in result set
|
||||
// false -> the entry is not allowed to be searched by this entity, so is
|
||||
// excluded.
|
||||
requested_attrs.is_subset(&allowed_attrs)
|
||||
let decision = requested_attrs.is_subset(&allowed_attrs);
|
||||
audit_log!(audit, "search attr decision --> {:?}", decision);
|
||||
decision
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
@ -148,9 +148,12 @@ pub static JSON_ANONYMOUS_V1: &'static str = r#"{
|
|||
pub static UUID_SCHEMA_ATTR_CLASS: &'static str = "00000000-0000-0000-0000-ffff00000000";
|
||||
pub static UUID_SCHEMA_ATTR_UUID: &'static str = "00000000-0000-0000-0000-ffff00000001";
|
||||
pub static UUID_SCHEMA_ATTR_NAME: &'static str = "00000000-0000-0000-0000-ffff00000002";
|
||||
pub static UUID_SCHEMA_ATTR_ATTRIBUTENAME: &'static str = "00000000-0000-0000-0000-ffff00000048";
|
||||
pub static UUID_SCHEMA_ATTR_CLASSNAME: &'static str = "00000000-0000-0000-0000-ffff00000049";
|
||||
pub static UUID_SCHEMA_ATTR_PRINCIPAL_NAME: &'static str = "00000000-0000-0000-0000-ffff00000003";
|
||||
pub static UUID_SCHEMA_ATTR_DESCRIPTION: &'static str = "00000000-0000-0000-0000-ffff00000004";
|
||||
pub static UUID_SCHEMA_ATTR_MULTIVALUE: &'static str = "00000000-0000-0000-0000-ffff00000005";
|
||||
pub static UUID_SCHEMA_ATTR_UNIQUE: &'static str = "00000000-0000-0000-0000-ffff00000047";
|
||||
pub static UUID_SCHEMA_ATTR_INDEX: &'static str = "00000000-0000-0000-0000-ffff00000006";
|
||||
pub static UUID_SCHEMA_ATTR_SYNTAX: &'static str = "00000000-0000-0000-0000-ffff00000007";
|
||||
pub static UUID_SCHEMA_ATTR_SYSTEMMAY: &'static str = "00000000-0000-0000-0000-ffff00000008";
|
||||
|
@ -215,10 +218,13 @@ pub static JSON_SCHEMA_ATTR_DISPLAYNAME: &'static str = r#"{
|
|||
"index": [
|
||||
"EQUALITY"
|
||||
],
|
||||
"unique": [
|
||||
"false"
|
||||
],
|
||||
"multivalue": [
|
||||
"false"
|
||||
],
|
||||
"name": [
|
||||
"attributename": [
|
||||
"displayname"
|
||||
],
|
||||
"syntax": [
|
||||
|
@ -248,10 +254,13 @@ pub static JSON_SCHEMA_ATTR_MAIL: &'static str = r#"
|
|||
"index": [
|
||||
"EQUALITY"
|
||||
],
|
||||
"unique": [
|
||||
"true"
|
||||
],
|
||||
"multivalue": [
|
||||
"true"
|
||||
],
|
||||
"name": [
|
||||
"attributename": [
|
||||
"mail"
|
||||
],
|
||||
"syntax": [
|
||||
|
@ -280,10 +289,13 @@ pub static JSON_SCHEMA_ATTR_SSH_PUBLICKEY: &'static str = r#"
|
|||
"SSH public keys of the object"
|
||||
],
|
||||
"index": [],
|
||||
"unique": [
|
||||
"false"
|
||||
],
|
||||
"multivalue": [
|
||||
"true"
|
||||
],
|
||||
"name": [
|
||||
"attributename": [
|
||||
"ssh_publickey"
|
||||
],
|
||||
"syntax": [
|
||||
|
@ -313,10 +325,13 @@ pub static JSON_SCHEMA_ATTR_PRIMARY_CREDENTIAL: &'static str = r#"
|
|||
"Primary credential material of the account for authentication interactively."
|
||||
],
|
||||
"index": [],
|
||||
"unique": [
|
||||
"false"
|
||||
],
|
||||
"multivalue": [
|
||||
"false"
|
||||
],
|
||||
"name": [
|
||||
"attributename": [
|
||||
"primary_credential"
|
||||
],
|
||||
"syntax": [
|
||||
|
@ -345,7 +360,7 @@ pub static JSON_SCHEMA_CLASS_PERSON: &'static str = r#"
|
|||
"description": [
|
||||
"Object representation of a person"
|
||||
],
|
||||
"name": [
|
||||
"classname": [
|
||||
"person"
|
||||
],
|
||||
"systemmay": [
|
||||
|
@ -379,7 +394,7 @@ pub static JSON_SCHEMA_CLASS_GROUP: &'static str = r#"
|
|||
"description": [
|
||||
"Object representation of a group"
|
||||
],
|
||||
"name": [
|
||||
"classname": [
|
||||
"group"
|
||||
],
|
||||
"systemmay": [
|
||||
|
@ -410,7 +425,7 @@ pub static JSON_SCHEMA_CLASS_ACCOUNT: &'static str = r#"
|
|||
"description": [
|
||||
"Object representation of a person"
|
||||
],
|
||||
"name": [
|
||||
"classname": [
|
||||
"account"
|
||||
],
|
||||
"systemmay": [
|
||||
|
|
|
@ -251,7 +251,7 @@ impl Entry<EntryInvalid, EntryNew> {
|
|||
.map(|(k, vs)| {
|
||||
let attr = k.to_lowercase();
|
||||
let vv: BTreeSet<Value> = match attr.as_str() {
|
||||
"name" | "version" | "domain" => {
|
||||
"name" | "attributename" | "classname" | "version" | "domain" => {
|
||||
vs.into_iter().map(|v| Value::new_iutf8(v)).collect()
|
||||
}
|
||||
"userid" | "uidnumber" => {
|
||||
|
@ -277,7 +277,7 @@ impl Entry<EntryInvalid, EntryNew> {
|
|||
"member" | "memberof" | "directmemberof" => {
|
||||
vs.into_iter().map(|v| Value::new_refer_s(v.as_str()).unwrap() ).collect()
|
||||
}
|
||||
"acp_enable" | "multivalue" => {
|
||||
"acp_enable" | "multivalue" | "unique" => {
|
||||
vs.into_iter().map(|v| Value::new_bools(v.as_str())
|
||||
.unwrap_or_else(|| {
|
||||
warn!("WARNING: Allowing syntax incorrect attribute to be presented UTF8 string");
|
||||
|
@ -1298,6 +1298,7 @@ impl From<&SchemaAttribute> for Entry<EntryValid, EntryNew> {
|
|||
let desc_v = btreeset![Value::new_utf8(s.description.clone())];
|
||||
|
||||
let multivalue_v = btreeset![Value::from(s.multivalue)];
|
||||
let unique_v = btreeset![Value::from(s.unique)];
|
||||
|
||||
let index_v: BTreeSet<_> = s.index.iter().map(|i| Value::from(i.clone())).collect();
|
||||
|
||||
|
@ -1305,10 +1306,11 @@ impl From<&SchemaAttribute> for Entry<EntryValid, EntryNew> {
|
|||
|
||||
// Build the BTreeMap of the attributes relevant
|
||||
let mut attrs: BTreeMap<String, BTreeSet<Value>> = BTreeMap::new();
|
||||
attrs.insert("name".to_string(), name_v);
|
||||
attrs.insert("attributename".to_string(), name_v);
|
||||
attrs.insert("description".to_string(), desc_v);
|
||||
attrs.insert("uuid".to_string(), uuid_v);
|
||||
attrs.insert("multivalue".to_string(), multivalue_v);
|
||||
attrs.insert("unique".to_string(), unique_v);
|
||||
attrs.insert("index".to_string(), index_v);
|
||||
attrs.insert("syntax".to_string(), syntax_v);
|
||||
attrs.insert(
|
||||
|
@ -1339,7 +1341,7 @@ impl From<&SchemaClass> for Entry<EntryValid, EntryNew> {
|
|||
let desc_v = btreeset![Value::new_utf8(s.description.clone())];
|
||||
|
||||
let mut attrs: BTreeMap<String, BTreeSet<Value>> = BTreeMap::new();
|
||||
attrs.insert("name".to_string(), name_v);
|
||||
attrs.insert("classname".to_string(), name_v);
|
||||
attrs.insert("description".to_string(), desc_v);
|
||||
attrs.insert("uuid".to_string(), uuid_v);
|
||||
attrs.insert(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#![deny(warnings)]
|
||||
// #![deny(warnings)]
|
||||
#![warn(unused_extern_crates)]
|
||||
|
||||
#[macro_use]
|
||||
|
|
357
rsidmd/src/lib/plugins/attrunique.rs
Normal file
357
rsidmd/src/lib/plugins/attrunique.rs
Normal file
|
@ -0,0 +1,357 @@
|
|||
// Attribute uniqueness plugin. We read the schema and determine if the
|
||||
// value should be unique, and how to handle if it is not. This will
|
||||
// matter a lot when it comes to replication based on first-wins or
|
||||
// both change approaches.
|
||||
//
|
||||
//
|
||||
use crate::audit::AuditScope;
|
||||
use crate::entry::{Entry, EntryCommitted, EntryInvalid, EntryNew};
|
||||
use crate::event::{CreateEvent, ModifyEvent};
|
||||
use crate::plugins::Plugin;
|
||||
use crate::schema::SchemaTransaction;
|
||||
use crate::server::{
|
||||
QueryServerReadTransaction, QueryServerTransaction, QueryServerWriteTransaction,
|
||||
};
|
||||
use crate::value::PartialValue;
|
||||
use rsidm_proto::v1::{ConsistencyError, OperationError};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub struct AttrUnique;
|
||||
|
||||
fn get_cand_attr_set<VALID, STATE>(
|
||||
au: &mut AuditScope,
|
||||
cand: &Vec<Entry<VALID, STATE>>,
|
||||
attr: &str,
|
||||
) -> Result<BTreeMap<PartialValue, PartialValue>, OperationError> {
|
||||
let mut cand_attr: BTreeMap<PartialValue, PartialValue> = BTreeMap::new();
|
||||
|
||||
for e in cand.iter() {
|
||||
let uuid = match e.get_ava_single("uuid") {
|
||||
Some(v) => v.to_partialvalue(),
|
||||
None => {
|
||||
return Err(OperationError::InvalidEntryState);
|
||||
}
|
||||
};
|
||||
// Get the value and uuid
|
||||
//for each value in the ava.
|
||||
let mut values: Vec<PartialValue> = match e.get_ava(attr) {
|
||||
Some(vs) => {
|
||||
// We have values, map them.
|
||||
vs.into_iter().map(|v| v.to_partialvalue()).collect()
|
||||
}
|
||||
None => {
|
||||
// No values, so empty set.
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
for v in values.drain(..) {
|
||||
match cand_attr.insert(v, uuid.clone()) {
|
||||
// Didn't exist, move on.
|
||||
None => {}
|
||||
// The duplicate/rejected value moved out of the tree
|
||||
Some(vr) => {
|
||||
audit_log!(
|
||||
au,
|
||||
"ava already exists -> {:?}: {:?} on {:?}",
|
||||
attr,
|
||||
vr,
|
||||
uuid
|
||||
);
|
||||
return Err(OperationError::Plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(cand_attr)
|
||||
}
|
||||
|
||||
fn enforce_unique<STATE>(
|
||||
au: &mut AuditScope,
|
||||
qs: &mut QueryServerWriteTransaction,
|
||||
cand: &Vec<Entry<EntryInvalid, STATE>>,
|
||||
attr: &str,
|
||||
) -> Result<(), OperationError> {
|
||||
debug!("{:?}", attr);
|
||||
|
||||
// Build a set of all the value -> uuid for the cands.
|
||||
// If already exist, reject due to dup.
|
||||
let cand_attr = try_audit!(au, get_cand_attr_set(au, cand, attr));
|
||||
|
||||
debug!("{:?}", cand_attr);
|
||||
|
||||
// No candidates to check!
|
||||
if cand_attr.len() == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Now do an internal search on name and !uuid for each
|
||||
|
||||
// Or
|
||||
let filt_in = filter!(f_or(
|
||||
// for each cand_attr
|
||||
cand_attr
|
||||
.into_iter()
|
||||
.map(|(v, uuid)| {
|
||||
// and[ attr eq k, andnot [ uuid eq v ]]
|
||||
// Basically this says where name but also not self.
|
||||
f_and(vec![FC::Eq(attr, v), f_andnot(FC::Eq("uuid", uuid))])
|
||||
})
|
||||
.collect()
|
||||
));
|
||||
|
||||
debug!("{:?}", filt_in);
|
||||
|
||||
// If any results, reject.
|
||||
let conflict_cand = try_audit!(au, qs.internal_search(au, filt_in));
|
||||
|
||||
// If all okay, okay!
|
||||
if conflict_cand.len() > 0 {
|
||||
return Err(OperationError::Plugin);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl Plugin for AttrUnique {
|
||||
fn id() -> &'static str {
|
||||
"plugin_attrunique"
|
||||
}
|
||||
|
||||
fn pre_create_transform(
|
||||
au: &mut AuditScope,
|
||||
qs: &mut QueryServerWriteTransaction,
|
||||
cand: &mut Vec<Entry<EntryInvalid, EntryNew>>,
|
||||
_ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
// Needs to clone to avoid a borrow issue?
|
||||
let uniqueattrs = {
|
||||
let schema = qs.get_schema();
|
||||
schema.get_attributes_unique()
|
||||
};
|
||||
|
||||
let r: Result<(), OperationError> = uniqueattrs
|
||||
.iter()
|
||||
.map(|attr| enforce_unique(au, qs, cand, attr.as_str()))
|
||||
.collect();
|
||||
r
|
||||
}
|
||||
|
||||
fn pre_modify(
|
||||
au: &mut AuditScope,
|
||||
qs: &mut QueryServerWriteTransaction,
|
||||
cand: &mut Vec<Entry<EntryInvalid, EntryCommitted>>,
|
||||
_me: &ModifyEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
// Needs to clone to avoid a borrow issue?
|
||||
let uniqueattrs = {
|
||||
let schema = qs.get_schema();
|
||||
schema.get_attributes_unique()
|
||||
};
|
||||
|
||||
let r: Result<(), OperationError> = uniqueattrs
|
||||
.iter()
|
||||
.map(|attr| enforce_unique(au, qs, cand, attr.as_str()))
|
||||
.collect();
|
||||
r
|
||||
}
|
||||
|
||||
fn verify(
|
||||
au: &mut AuditScope,
|
||||
qs: &QueryServerReadTransaction,
|
||||
) -> Vec<Result<(), ConsistencyError>> {
|
||||
// Only check live entries, not recycled.
|
||||
let filt_in = filter!(f_pres("class"));
|
||||
|
||||
let all_cand = match qs
|
||||
.internal_search(au, filt_in)
|
||||
.map_err(|_| Err(ConsistencyError::QueryServerSearchFailure))
|
||||
{
|
||||
Ok(all_cand) => all_cand,
|
||||
Err(e) => return vec![e],
|
||||
};
|
||||
|
||||
let uniqueattrs = {
|
||||
let schema = qs.get_schema();
|
||||
schema.get_attributes_unique()
|
||||
};
|
||||
|
||||
let mut res: Vec<Result<(), ConsistencyError>> = Vec::new();
|
||||
|
||||
for attr in uniqueattrs.iter() {
|
||||
// We do a fully in memory check.
|
||||
if get_cand_attr_set(au, &all_cand, attr.as_str()).is_err() {
|
||||
res.push(Err(ConsistencyError::DuplicateUniqueAttribute(
|
||||
attr.clone(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
debug!("{:?}", res);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::entry::{Entry, EntryInvalid, EntryNew};
|
||||
use crate::modify::{Modify, ModifyList};
|
||||
use crate::value::{PartialValue, Value};
|
||||
use rsidm_proto::v1::OperationError;
|
||||
// Test entry in db, and same name, reject.
|
||||
#[test]
|
||||
fn test_pre_create_name_unique() {
|
||||
let e: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"name": ["testperson"],
|
||||
"description": ["testperson"],
|
||||
"displayname": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let create = vec![e.clone()];
|
||||
let preload = vec![e];
|
||||
|
||||
run_create_test!(
|
||||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
||||
// Test two entries in create that would have same name, reject.
|
||||
#[test]
|
||||
fn test_pre_create_name_unique_2() {
|
||||
let e: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"name": ["testperson"],
|
||||
"description": ["testperson"],
|
||||
"displayname": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let create = vec![e.clone(), e];
|
||||
let preload = Vec::new();
|
||||
|
||||
run_create_test!(
|
||||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
create,
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
||||
// Remember, an entry can't have a duplicate value within itself so we don't need to
|
||||
// test this case.
|
||||
|
||||
// A mod to something that exists, reject.
|
||||
#[test]
|
||||
fn test_pre_modify_name_unique() {
|
||||
let ea: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["group"],
|
||||
"name": ["testgroup_a"],
|
||||
"description": ["testgroup"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let eb: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["group"],
|
||||
"name": ["testgroup_b"],
|
||||
"description": ["testgroup"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let preload = vec![ea, eb];
|
||||
|
||||
run_modify_test!(
|
||||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
filter!(f_or!([f_eq(
|
||||
"name",
|
||||
PartialValue::new_iutf8s("testgroup_b")
|
||||
),])),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Purged("name".to_string()),
|
||||
Modify::Present("name".to_string(), Value::new_iutf8s("testgroup_a"))
|
||||
]),
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
||||
// Two items modded to have the same value, reject.
|
||||
#[test]
|
||||
fn test_pre_modify_name_unique_2() {
|
||||
let ea: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["group"],
|
||||
"name": ["testgroup_a"],
|
||||
"description": ["testgroup"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let eb: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
r#"{
|
||||
"valid": null,
|
||||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["group"],
|
||||
"name": ["testgroup_b"],
|
||||
"description": ["testgroup"]
|
||||
}
|
||||
}"#,
|
||||
);
|
||||
|
||||
let preload = vec![ea, eb];
|
||||
|
||||
run_modify_test!(
|
||||
Err(OperationError::Plugin),
|
||||
preload,
|
||||
filter!(f_or!([
|
||||
f_eq("name", PartialValue::new_iutf8s("testgroup_a")),
|
||||
f_eq("name", PartialValue::new_iutf8s("testgroup_b")),
|
||||
])),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Purged("name".to_string()),
|
||||
Modify::Present("name".to_string(), Value::new_iutf8s("testgroup"))
|
||||
]),
|
||||
None,
|
||||
|_, _| {}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verify_name_unique() {
|
||||
// Can we preload two dups and verify to show we detect?
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ use rsidm_proto::v1::{ConsistencyError, OperationError};
|
|||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
mod attrunique;
|
||||
mod base;
|
||||
mod failure;
|
||||
mod memberof;
|
||||
|
@ -277,7 +278,10 @@ impl Plugins {
|
|||
ce: &CreateEvent,
|
||||
) -> Result<(), OperationError> {
|
||||
audit_segment!(au, || {
|
||||
let res = run_pre_create_transform_plugin!(au, qs, cand, ce, base::Base);
|
||||
let res =
|
||||
run_pre_create_transform_plugin!(au, qs, cand, ce, base::Base).and_then(|_| {
|
||||
run_pre_create_transform_plugin!(au, qs, cand, ce, attrunique::AttrUnique)
|
||||
});
|
||||
|
||||
res
|
||||
})
|
||||
|
@ -318,7 +322,8 @@ impl Plugins {
|
|||
) -> Result<(), OperationError> {
|
||||
audit_segment!(au, || {
|
||||
let res = run_pre_modify_plugin!(au, qs, cand, me, protected::Protected)
|
||||
.and_then(|_| run_pre_modify_plugin!(au, qs, cand, me, base::Base));
|
||||
.and_then(|_| run_pre_modify_plugin!(au, qs, cand, me, base::Base))
|
||||
.and_then(|_| run_pre_modify_plugin!(au, qs, cand, me, attrunique::AttrUnique));
|
||||
|
||||
res
|
||||
})
|
||||
|
@ -374,6 +379,7 @@ impl Plugins {
|
|||
) -> Vec<Result<(), ConsistencyError>> {
|
||||
let mut results = Vec::new();
|
||||
run_verify_plugin!(au, qs, &mut results, base::Base);
|
||||
run_verify_plugin!(au, qs, &mut results, attrunique::AttrUnique);
|
||||
run_verify_plugin!(au, qs, &mut results, refint::ReferentialIntegrity);
|
||||
run_verify_plugin!(au, qs, &mut results, memberof::MemberOf);
|
||||
results
|
||||
|
|
|
@ -184,7 +184,7 @@ mod tests {
|
|||
"acp_targetscope": [
|
||||
"{\"Pres\":\"class\"}"
|
||||
],
|
||||
"acp_search_attr": ["name", "class", "uuid"],
|
||||
"acp_search_attr": ["name", "class", "uuid", "classname", "attributename"],
|
||||
"acp_modify_class": ["system"],
|
||||
"acp_modify_removedattr": ["class", "displayname", "may", "must"],
|
||||
"acp_modify_presentattr": ["class", "displayname", "may", "must"],
|
||||
|
@ -295,7 +295,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["testclass"],
|
||||
"classname": ["testclass"],
|
||||
"uuid": ["cfcae205-31c3-484b-8ced-667d1709c5e3"],
|
||||
"description": ["Test Class"]
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ mod tests {
|
|||
run_modify_test!(
|
||||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq("name", PartialValue::new_iutf8s("testclass"))),
|
||||
filter!(f_eq("classname", PartialValue::new_iutf8s("testclass"))),
|
||||
modlist!([
|
||||
m_pres("may", &Value::new_iutf8s("name")),
|
||||
m_pres("must", &Value::new_iutf8s("name")),
|
||||
|
|
|
@ -193,7 +193,7 @@ impl Plugin for ReferentialIntegrity {
|
|||
) -> Vec<Result<(), ConsistencyError>> {
|
||||
// Get all entries as cand
|
||||
// build a cand-uuid set
|
||||
let filt_in = filter!(f_pres("class"));
|
||||
let filt_in = filter_all!(f_pres("class"));
|
||||
|
||||
let all_cand = match qs
|
||||
.internal_search(au, filt_in)
|
||||
|
|
|
@ -32,6 +32,7 @@ pub struct SchemaAttribute {
|
|||
// Perhaps later add aliases?
|
||||
pub description: String,
|
||||
pub multivalue: bool,
|
||||
pub unique: bool,
|
||||
pub index: Vec<IndexType>,
|
||||
pub syntax: SyntaxType,
|
||||
}
|
||||
|
@ -55,8 +56,8 @@ impl SchemaAttribute {
|
|||
let name = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_string("name")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing name"))
|
||||
.get_ava_single_string("attributename")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing attributename"))
|
||||
);
|
||||
// description
|
||||
let description = try_audit!(
|
||||
|
@ -73,6 +74,12 @@ impl SchemaAttribute {
|
|||
.get_ava_single_bool("multivalue")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing multivalue"))
|
||||
);
|
||||
let unique = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_bool("unique")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing unique"))
|
||||
);
|
||||
// index vec
|
||||
// even if empty, it SHOULD be present ... (is that value to put an empty set?)
|
||||
// The get_ava_opt_index handles the optional case for us :)
|
||||
|
@ -100,6 +107,7 @@ impl SchemaAttribute {
|
|||
uuid: uuid,
|
||||
description: description,
|
||||
multivalue: multivalue,
|
||||
unique: unique,
|
||||
index: index,
|
||||
syntax: syntax,
|
||||
})
|
||||
|
@ -340,8 +348,8 @@ impl SchemaClass {
|
|||
let name = try_audit!(
|
||||
audit,
|
||||
value
|
||||
.get_ava_single_string("name")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing name"))
|
||||
.get_ava_single_string("classname")
|
||||
.ok_or(OperationError::InvalidSchemaState("missing classname"))
|
||||
);
|
||||
// description
|
||||
let description = try_audit!(
|
||||
|
@ -408,6 +416,14 @@ pub trait SchemaTransaction {
|
|||
an.to_lowercase()
|
||||
}
|
||||
|
||||
fn get_attributes_unique(&self) -> Vec<String> {
|
||||
// This could be improved by caching this set on schema reload!
|
||||
self.get_attributes()
|
||||
.iter()
|
||||
.filter_map(|(k, v)| if v.unique { Some(k.clone()) } else { None })
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Probably need something like get_classes or similar
|
||||
// so that externals can call and use this data.
|
||||
|
||||
|
@ -449,6 +465,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The set of classes defining an object"),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -461,6 +478,9 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The universal unique id of the object"),
|
||||
multivalue: false,
|
||||
// Uniqueness is handled by base.rs, not attrunique here due to
|
||||
// needing to check recycled objects too.
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UUID,
|
||||
},
|
||||
|
@ -473,6 +493,33 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The shortform name of an object"),
|
||||
multivalue: false,
|
||||
unique: true,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
);
|
||||
s.attributes.insert(
|
||||
String::from("attributename"),
|
||||
SchemaAttribute {
|
||||
name: String::from("attributename"),
|
||||
uuid: Uuid::parse_str(UUID_SCHEMA_ATTR_ATTRIBUTENAME)
|
||||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The name of a schema attribute"),
|
||||
multivalue: false,
|
||||
unique: true,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
);
|
||||
s.attributes.insert(
|
||||
String::from("classname"),
|
||||
SchemaAttribute {
|
||||
name: String::from("classname"),
|
||||
uuid: Uuid::parse_str(UUID_SCHEMA_ATTR_CLASSNAME)
|
||||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The name of a schema class"),
|
||||
multivalue: false,
|
||||
unique: true,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -485,6 +532,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("A description of an attribute, object or class"),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::UTF8STRING,
|
||||
},
|
||||
|
@ -494,6 +542,16 @@ impl SchemaInner {
|
|||
uuid: Uuid::parse_str(UUID_SCHEMA_ATTR_MULTIVALUE).expect("unable to parse static uuid"),
|
||||
description: String::from("If true, this attribute is able to store multiple values rather than just a single value."),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::BOOLEAN,
|
||||
});
|
||||
s.attributes.insert(String::from("unique"), SchemaAttribute {
|
||||
name: String::from("unique"),
|
||||
uuid: Uuid::parse_str(UUID_SCHEMA_ATTR_UNIQUE).expect("unable to parse static uuid"),
|
||||
description: String::from("If true, this attribute must store a unique value through out the database."),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::BOOLEAN,
|
||||
});
|
||||
|
@ -507,6 +565,7 @@ impl SchemaInner {
|
|||
"Describe the indexes to apply to instances of this attribute.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::INDEX_ID,
|
||||
},
|
||||
|
@ -521,6 +580,7 @@ impl SchemaInner {
|
|||
"Describe the syntax of this attribute. This affects indexing and sorting.",
|
||||
),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::SYNTAX_ID,
|
||||
},
|
||||
|
@ -535,6 +595,7 @@ impl SchemaInner {
|
|||
"A list of system provided optional attributes this class can store.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -549,6 +610,7 @@ impl SchemaInner {
|
|||
"A user modifiable list of optional attributes this class can store.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -563,6 +625,7 @@ impl SchemaInner {
|
|||
"A list of system provided required attributes this class must store.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -577,6 +640,7 @@ impl SchemaInner {
|
|||
"A user modifiable list of required attributes this class must store.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -591,6 +655,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
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,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::BOOLEAN,
|
||||
},
|
||||
|
@ -606,6 +671,7 @@ impl SchemaInner {
|
|||
"Who the ACP applies to, constraining or allowing operations.",
|
||||
),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY, IndexType::SUBSTRING],
|
||||
syntax: SyntaxType::JSON_FILTER,
|
||||
},
|
||||
|
@ -620,6 +686,7 @@ impl SchemaInner {
|
|||
"The effective targets of the ACP, IE what will be acted upon.",
|
||||
),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY, IndexType::SUBSTRING],
|
||||
syntax: SyntaxType::JSON_FILTER,
|
||||
},
|
||||
|
@ -632,6 +699,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The attributes that may be viewed or searched by the reciever on targetscope."),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -646,6 +714,7 @@ impl SchemaInner {
|
|||
"The set of classes that can be created on a new entry.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -660,6 +729,7 @@ impl SchemaInner {
|
|||
"The set of attribute types that can be created on an entry.",
|
||||
),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -673,6 +743,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The set of attribute types that could be removed or purged in a modification."),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -685,6 +756,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("The set of attribute types that could be added or asserted in a modification."),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -697,6 +769,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
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,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -710,6 +783,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("reverse group membership of the object"),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::REFERENCE_UUID,
|
||||
},
|
||||
|
@ -722,6 +796,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("reverse direct group membership of the object"),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::REFERENCE_UUID,
|
||||
},
|
||||
|
@ -734,6 +809,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("List of members of the group"),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::REFERENCE_UUID,
|
||||
},
|
||||
|
@ -749,6 +825,7 @@ impl SchemaInner {
|
|||
"The systems internal migration version for provided objects",
|
||||
),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -762,6 +839,7 @@ impl SchemaInner {
|
|||
.expect("unable to parse static uuid"),
|
||||
description: String::from("A DNS Domain name entry."),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
},
|
||||
|
@ -778,8 +856,9 @@ impl SchemaInner {
|
|||
may: vec![],
|
||||
systemmust: vec![
|
||||
String::from("class"),
|
||||
String::from("name"),
|
||||
String::from("attributename"),
|
||||
String::from("multivalue"),
|
||||
String::from("unique"),
|
||||
String::from("syntax"),
|
||||
String::from("description"),
|
||||
],
|
||||
|
@ -802,7 +881,7 @@ impl SchemaInner {
|
|||
may: vec![],
|
||||
systemmust: vec![
|
||||
String::from("class"),
|
||||
String::from("name"),
|
||||
String::from("classname"),
|
||||
String::from("description"),
|
||||
],
|
||||
must: vec![],
|
||||
|
@ -819,11 +898,7 @@ impl SchemaInner {
|
|||
),
|
||||
systemmay: vec![String::from("description"), String::from("name")],
|
||||
may: vec![],
|
||||
systemmust: vec![
|
||||
String::from("class"),
|
||||
// String::from("name"),
|
||||
String::from("uuid"),
|
||||
],
|
||||
systemmust: vec![String::from("class"), String::from("uuid")],
|
||||
must: vec![],
|
||||
},
|
||||
);
|
||||
|
@ -1253,7 +1328,8 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"unique": ["false"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1267,9 +1343,10 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"index": ["EQUALITY"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
@ -1284,10 +1361,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"description": ["Test attr parsing"],
|
||||
"multivalue": ["htouaoeu"],
|
||||
"unique": ["false"],
|
||||
"index": ["EQUALITY"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
@ -1302,10 +1380,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"description": ["Test attr parsing"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"index": ["NTEHNOU"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
@ -1320,10 +1399,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"description": ["Test attr parsing"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"index": ["EQUALITY"],
|
||||
"syntax": ["TNEOUNTUH"]
|
||||
}
|
||||
|
@ -1339,10 +1419,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"description": ["Test attr parsing"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1357,10 +1438,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["schema_attr_test"],
|
||||
"attributename": ["schema_attr_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"description": ["Test attr parsing"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"index": ["EQUALITY"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
@ -1380,7 +1462,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -1394,7 +1476,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"]
|
||||
}
|
||||
|
@ -1410,7 +1492,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"]
|
||||
}
|
||||
|
@ -1426,7 +1508,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"systemmust": ["d"]
|
||||
|
@ -1442,7 +1524,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"systemmay": ["c"]
|
||||
|
@ -1458,7 +1540,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"may": ["a"],
|
||||
|
@ -1475,7 +1557,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["schema_class_test"],
|
||||
"classname": ["schema_class_test"],
|
||||
"description": ["class test"],
|
||||
"uuid": ["66c68b2f-d02c-4243-8013-7946e40fe321"],
|
||||
"may": ["a"],
|
||||
|
@ -1500,6 +1582,7 @@ mod tests {
|
|||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING_INSENSITIVE,
|
||||
};
|
||||
|
@ -1521,6 +1604,7 @@ mod tests {
|
|||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::UTF8STRING,
|
||||
};
|
||||
|
@ -1537,6 +1621,7 @@ mod tests {
|
|||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
multivalue: true,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::BOOLEAN,
|
||||
};
|
||||
|
@ -1559,6 +1644,7 @@ mod tests {
|
|||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::SYNTAX_ID,
|
||||
};
|
||||
|
@ -1576,6 +1662,7 @@ mod tests {
|
|||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
multivalue: false,
|
||||
unique: false,
|
||||
index: vec![IndexType::EQUALITY],
|
||||
syntax: SyntaxType::INDEX_ID,
|
||||
};
|
||||
|
@ -1667,9 +1754,10 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["testattr"],
|
||||
"attributename": ["testattr"],
|
||||
"description": ["testattr"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"syntax": ["UTF8STRING"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"zzzzz": ["zzzz"]
|
||||
|
@ -1688,9 +1776,10 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["testattr"],
|
||||
"attributename": ["testattr"],
|
||||
"description": ["testattr"],
|
||||
"multivalue": ["zzzzz"],
|
||||
"unique": ["false"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
@ -1708,9 +1797,10 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["testattr"],
|
||||
"attributename": ["testattr"],
|
||||
"description": ["testattr"],
|
||||
"multivalue": ["true"],
|
||||
"unique": ["false"],
|
||||
"uuid": ["db237e8a-0079-4b8c-8a56-593b22aa44d1"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
|
|
|
@ -1101,12 +1101,11 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter_mut()
|
||||
.for_each(|er| er.apply_modlist(&me.modlist));
|
||||
|
||||
// let mut candidates = try_audit!(au, candidates);
|
||||
|
||||
audit_log!(au, "modify: candidates -> {:?}", candidates);
|
||||
|
||||
// Pre mod plugins
|
||||
let mut audit_plugin_pre = AuditScope::new("plugin_pre_modify");
|
||||
// We should probably supply the pre-post cands here.
|
||||
let plug_pre_res =
|
||||
Plugins::run_pre_modify(&mut audit_plugin_pre, self, &mut candidates, me);
|
||||
au.append_scope(audit_plugin_pre);
|
||||
|
@ -1122,9 +1121,6 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// optimisations, this could be premature - so we for now, just
|
||||
// do the CORRECT thing and recommit as we may find later we always
|
||||
// want to add CSN's or other.
|
||||
//
|
||||
// memberOf actually wants the pre cand list and the norm_cand list to see what
|
||||
// changed. Could be optimised, but this is correct still ...
|
||||
|
||||
let res: Result<Vec<Entry<EntryValid, EntryCommitted>>, SchemaError> = candidates
|
||||
.into_iter()
|
||||
|
@ -1149,6 +1145,9 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
// Post Plugins
|
||||
//
|
||||
// memberOf actually wants the pre cand list and the norm_cand list to see what
|
||||
// changed. Could be optimised, but this is correct still ...
|
||||
let mut audit_plugin_post = AuditScope::new("plugin_post_modify");
|
||||
let plug_post_res = Plugins::run_post_modify(
|
||||
&mut audit_plugin_post,
|
||||
|
@ -2496,7 +2495,7 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "classtype"],
|
||||
"name": ["testclass"],
|
||||
"classname": ["testclass"],
|
||||
"uuid": ["cfcae205-31c3-484b-8ced-667d1709c5e3"],
|
||||
"description": ["Test Class"]
|
||||
}
|
||||
|
@ -2529,7 +2528,7 @@ mod tests {
|
|||
// delete the class
|
||||
let de_class = unsafe {
|
||||
DeleteEvent::new_internal_invalid(filter!(f_eq(
|
||||
"name",
|
||||
"classname",
|
||||
PartialValue::new_iutf8s("testclass")
|
||||
)))
|
||||
};
|
||||
|
@ -2580,10 +2579,11 @@ mod tests {
|
|||
"state": null,
|
||||
"attrs": {
|
||||
"class": ["object", "attributetype"],
|
||||
"name": ["testattr"],
|
||||
"attributename": ["testattr"],
|
||||
"uuid": ["cfcae205-31c3-484b-8ced-667d1709c5e3"],
|
||||
"description": ["Test Attribute"],
|
||||
"multivalue": ["false"],
|
||||
"unique": ["false"],
|
||||
"syntax": ["UTF8STRING"]
|
||||
}
|
||||
}"#,
|
||||
|
@ -2615,7 +2615,7 @@ mod tests {
|
|||
// delete the attr
|
||||
let de_attr = unsafe {
|
||||
DeleteEvent::new_internal_invalid(filter!(f_eq(
|
||||
"name",
|
||||
"attributename",
|
||||
PartialValue::new_iutf8s("testattr")
|
||||
)))
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue