From a5ebac54c7e6da2a0a785656e91bc773d8260da8 Mon Sep 17 00:00:00 2001 From: William Brown Date: Thu, 15 Nov 2018 16:49:08 +1300 Subject: [PATCH] add normalise entries --- src/entry.rs | 33 +++++++++++++++- src/schema.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++----- src/server.rs | 1 + 3 files changed, 129 insertions(+), 11 deletions(-) diff --git a/src/entry.rs b/src/entry.rs index e63775e2a..2d95312e0 100644 --- a/src/entry.rs +++ b/src/entry.rs @@ -1,5 +1,5 @@ // use serde_json::{Error, Value}; -use std::collections::btree_map::Iter as BTreeIter; +use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut}; use std::collections::BTreeMap; use std::marker::PhantomData; use std::slice::Iter as SliceIter; @@ -74,6 +74,24 @@ impl<'a> Iterator for EntryAvas<'a> { } } +pub struct EntryAvasMut<'a> { + inner: BTreeIterMut<'a, String, Vec>, +} + +impl<'a> Iterator for EntryAvasMut<'a> { + type Item = (&'a String, &'a mut Vec); + + #[inline] + fn next(&mut self) -> Option<(&'a String, &'a mut Vec)> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + #[derive(Serialize, Deserialize, Debug)] pub struct Entry { attrs: BTreeMap>, @@ -106,6 +124,12 @@ impl Entry { .or_insert(vec![value]); } + // FIXME: Should this collect from iter instead? + pub fn add_avas(&mut self, attr: String, values: Vec) { + // Overwrite the existing value + let _ = self.attrs.insert(attr, values); + } + pub fn get_ava(&self, attr: &String) -> Option<&Vec> { self.attrs.get(attr) } @@ -141,11 +165,16 @@ impl Entry { } pub fn avas(&self) -> EntryAvas { - // Get all attr:value pairs. EntryAvas { inner: self.attrs.iter(), } } + + pub fn avas_mut(&mut self) -> EntryAvasMut { + EntryAvasMut { + inner: self.attrs.iter_mut(), + } + } } impl Clone for Entry { diff --git a/src/schema.rs b/src/schema.rs index 6c734427d..d85c8f213 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -181,14 +181,25 @@ impl SchemaAttribute { } } - pub fn normalise_ava(&self, attr_type: String, attr_value: String) { - // Given some types, we can normalise them in sane and consistent - // ways. This is generally used in add_ava, and filter - // modification. + pub fn normalise_syntax(&self, v: &String) -> String { + v.to_uppercase() + } - // Given the attr_type load the schema_attribute - // given the syntax, normalise. + pub fn normalise_index(&self, v: &String) -> String { + v.to_uppercase() + } + pub fn normalise_utf8string_insensitive(&self, v: &String) -> String { + v.to_lowercase() + } + + pub fn normalise_value(&self, v: &String) -> String { + match self.syntax { + SyntaxType::SYNTAX_ID => self.normalise_syntax(v), + SyntaxType::INDEX_ID => self.normalise_index(v), + SyntaxType::UTF8STRING_INSENSITIVE => self.normalise_utf8string_insensitive(v), + _ => v.clone(), + } } } @@ -705,8 +716,39 @@ impl Schema { Ok(()) } - // Normalise also validates? - pub fn normalise_entry(&mut self) {} + pub fn normalise_entry(&mut self, entry: &Entry) -> Entry { + // We duplicate the entry here, because we can't + // modify what we got on the protocol level. It also + // lets us extend and change things. + + let mut entry_new: Entry = Entry::new(); + // Better hope we have the attribute type ... + let schema_attr_name = self.attributes.get("name").unwrap(); + // For each ava + for (attr_name, avas) in entry.avas() { + let attr_name_normal: String = schema_attr_name.normalise_value(attr_name); + // Get the needed schema type + let schema_a_r = self.attributes.get(&attr_name_normal); + // if we can't find schema_a, clone and push + // else + + let avas_normal: Vec = match schema_a_r { + Some(schema_a) => { + avas.iter() + .map(|av| { + // normalise those based on schema? + schema_a.normalise_value(av) + }) + .collect() + } + None => avas.clone(), + }; + // now push those to the new entry. + entry_new.add_avas(attr_name_normal, avas_normal); + } + // Done! + entry_new + } // This needs to be recursive? pub fn validate_filter(&self, filt: &Filter) -> Result<(), SchemaError> { @@ -1045,10 +1087,56 @@ mod tests { }"#, ) .unwrap(); - assert_eq!(schema.validate_entry(&e_ok), Ok(())); } + #[test] + fn test_schema_entry_normalise() { + // Check that entries can be normalised sanely + let mut schema = Schema::new(); + schema.bootstrap_core(); + + // Check syntax to upper + // check index to upper + // insense to lower + // attr name to lower + let e_test: Entry = serde_json::from_str( + r#"{ + "attrs": { + "class": ["extensibleobject"], + "name": ["TestPerson"], + "displayName": ["testperson"], + "syntax": ["utf8string"], + "index": ["equality"] + } + }"#, + ) + .unwrap(); + assert_eq!( + schema.validate_entry(&e_test), + Err(SchemaError::INVALID_ATTRIBUTE_SYNTAX) + ); + + let e_expect: Entry = serde_json::from_str( + r#"{ + "attrs": { + "class": ["extensibleobject"], + "name": ["testperson"], + "displayname": ["testperson"], + "syntax": ["UTF8STRING"], + "index": ["EQUALITY"] + } + }"#, + ) + .unwrap(); + assert_eq!(schema.validate_entry(&e_expect), Ok(())); + + let e_normalised = schema.normalise_entry(&e_test); + + assert_eq!(schema.validate_entry(&e_normalised), Ok(())); + assert_eq!(e_expect, e_normalised); + } + #[test] fn test_schema_extensible() { let schema = Schema::new(); diff --git a/src/server.rs b/src/server.rs index e705620f7..0e56dea25 100644 --- a/src/server.rs +++ b/src/server.rs @@ -69,6 +69,7 @@ impl QueryServer { pub fn create(&mut self, au: &mut AuditEvent, ce: &CreateEvent) -> Result<(), ()> { // Start a txn // Run any pre checks + // FIXME: Normalise all entries incoming let r = ce.entries.iter().fold(Ok(()), |acc, e| { if acc.is_ok() {