Most changes for state validated modlist done, just need to write the validate!

This commit is contained in:
William Brown 2019-03-12 15:20:08 +10:00
parent 108a097bd0
commit 658e409d90
4 changed files with 95 additions and 28 deletions

View file

@ -2,7 +2,7 @@
use super::proto_v1::Entry as ProtoEntry; use super::proto_v1::Entry as ProtoEntry;
use error::SchemaError; use error::SchemaError;
use filter::{Filter, FilterValid}; use filter::{Filter, FilterValid};
use modify::{Modify, ModifyList}; use modify::{Modify, ModifyList, ModifyValid, ModifyInvalid};
use schema::{SchemaAttribute, SchemaClass, SchemaReadTransaction}; use schema::{SchemaAttribute, SchemaClass, SchemaReadTransaction};
use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut}; use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -503,7 +503,7 @@ impl<STATE> Entry<EntryValid, STATE> {
pub fn gen_modlist_assert( pub fn gen_modlist_assert(
&self, &self,
schema: &SchemaReadTransaction, schema: &SchemaReadTransaction,
) -> Result<ModifyList, SchemaError> { ) -> Result<ModifyList<ModifyInvalid>, SchemaError> {
// Create a modlist from this entry. We make this assuming we want the entry // Create a modlist from this entry. We make this assuming we want the entry
// to have this one as a subset of values. This means if we have single // to have this one as a subset of values. This means if we have single
// values, we'll replace, if they are multivalue, we present them. // values, we'll replace, if they are multivalue, we present them.
@ -645,7 +645,7 @@ where
} }
// Should this be schemaless, relying on checks of the modlist, and the entry validate after? // Should this be schemaless, relying on checks of the modlist, and the entry validate after?
pub fn apply_modlist(&self, modlist: &ModifyList) -> Result<Entry<EntryInvalid, STATE>, ()> { pub fn apply_modlist(&self, modlist: &ModifyList<ModifyValid>) -> Result<Entry<EntryInvalid, STATE>, ()> {
// Apply a modlist, generating a new entry that conforms to the changes. // Apply a modlist, generating a new entry that conforms to the changes.
// This is effectively clone-and-transform // This is effectively clone-and-transform
@ -658,7 +658,7 @@ where
}; };
// mutate // mutate
for modify in modlist.mods.iter() { for modify in modlist {
match modify { match modify {
Modify::Present(a, v) => ne.add_ava(a.clone(), v.clone()), Modify::Present(a, v) => ne.add_ava(a.clone(), v.clone()),
Modify::Removed(a, v) => ne.remove_ava(a, v), Modify::Removed(a, v) => ne.remove_ava(a, v),
@ -770,10 +770,12 @@ mod tests {
let mut e: Entry<EntryInvalid, EntryNew> = Entry::new(); let mut e: Entry<EntryInvalid, EntryNew> = Entry::new();
e.add_ava(String::from("userid"), String::from("william")); e.add_ava(String::from("userid"), String::from("william"));
let mods = ModifyList::new_list(vec![Modify::Present( let mods = unsafe {
String::from("attr"), ModifyList::new_valid_list(vec![Modify::Present(
String::from("value"), String::from("attr"),
)]); String::from("value"),
)])
};
let ne = e.apply_modlist(&mods).unwrap(); let ne = e.apply_modlist(&mods).unwrap();

View file

@ -6,7 +6,7 @@ use super::proto_v1::{
}; };
use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid}; use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
// use error::OperationError; // use error::OperationError;
use modify::ModifyList; use modify::{ModifyList, ModifyInvalid};
use actix::prelude::*; use actix::prelude::*;
@ -189,7 +189,7 @@ impl DeleteEvent {
#[derive(Debug)] #[derive(Debug)]
pub struct ModifyEvent { pub struct ModifyEvent {
pub filter: Filter<FilterInvalid>, pub filter: Filter<FilterInvalid>,
pub modlist: ModifyList, pub modlist: ModifyList<ModifyInvalid>,
pub internal: bool, pub internal: bool,
} }
@ -203,7 +203,7 @@ impl ModifyEvent {
} }
#[cfg(test)] #[cfg(test)]
pub fn from_filter(filter: Filter<FilterInvalid>, modlist: ModifyList) -> Self { pub fn from_filter(filter: Filter<FilterInvalid>, modlist: ModifyList<ModifyInvalid>) -> Self {
ModifyEvent { ModifyEvent {
filter: filter, filter: filter,
modlist: modlist, modlist: modlist,
@ -211,7 +211,7 @@ impl ModifyEvent {
} }
} }
pub fn new_internal(filter: Filter<FilterInvalid>, modlist: ModifyList) -> Self { pub fn new_internal(filter: Filter<FilterInvalid>, modlist: ModifyList<ModifyInvalid>) -> Self {
ModifyEvent { ModifyEvent {
filter: filter, filter: filter,
modlist: modlist, modlist: modlist,

View file

@ -1,6 +1,17 @@
use proto_v1::Modify as ProtoModify; use proto_v1::Modify as ProtoModify;
use proto_v1::ModifyList as ProtoModifyList; use proto_v1::ModifyList as ProtoModifyList;
use error::SchemaError;
use schema::{SchemaAttribute, SchemaReadTransaction};
// Should this be std?
use std::slice;
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifyValid;
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifyInvalid;
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Modify { pub enum Modify {
// This value *should* exist. // This value *should* exist.
@ -22,36 +33,79 @@ impl Modify {
} }
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct ModifyList { pub struct ModifyList<VALID> {
// And ordered list of changes to apply. Should this be state based? valid: VALID,
pub mods: Vec<Modify>, // The order of this list matters. Each change must be done in order.
mods: Vec<Modify>,
} }
// TODO: ModifyList should be like filter and have valid/invalid to schema. // TODO: ModifyList should be like filter and have valid/invalid to schema.
// Or do we not care because the entry will be invalid at the end? // Or do we not care because the entry will be invalid at the end?
impl ModifyList { impl<'a> IntoIterator for &'a ModifyList<ModifyValid> {
type Item = &'a Modify;
type IntoIter = slice::Iter<'a, Modify>;
fn into_iter(self) -> Self::IntoIter {
self.mods.iter()
}
}
impl ModifyList<ModifyInvalid> {
pub fn new() -> Self { pub fn new() -> Self {
ModifyList { mods: Vec::new() } ModifyList {
valid: ModifyInvalid,
mods: Vec::new()
}
} }
pub fn new_list(mods: Vec<Modify>) -> Self { pub fn new_list(mods: Vec<Modify>) -> Self {
ModifyList { mods: mods } ModifyList {
valid: ModifyInvalid,
mods: mods
}
} }
pub fn push_mod(&mut self, modify: Modify) { pub fn push_mod(&mut self, modify: Modify) {
self.mods.push(modify) self.mods.push(modify)
} }
pub fn len(&self) -> usize {
self.mods.len()
}
pub fn from(ml: &ProtoModifyList) -> Self { pub fn from(ml: &ProtoModifyList) -> Self {
// For each ProtoModify, do a from. // For each ProtoModify, do a from.
ModifyList { ModifyList {
valid: ModifyInvalid,
mods: ml.mods.iter().map(|pm| Modify::from(pm)).collect(), mods: ml.mods.iter().map(|pm| Modify::from(pm)).collect(),
} }
} }
pub fn validate(&self,
schema: &SchemaReadTransaction,
) -> Result<ModifyList<ModifyValid>, SchemaError> {
// Check that all attributes exist in the schema
// Normalise them
// Validate them
// Return new ModifyList!
unimplemented!()
}
}
impl ModifyList<ModifyValid> {
#[cfg(test)]
pub unsafe fn new_valid_list(mods: Vec<Modify>) -> Self {
ModifyList {
valid: ModifyValid,
mods: mods
}
}
}
impl<VALID> ModifyList<VALID> {
pub fn len(&self) -> usize {
self.mods.len()
}
} }

View file

@ -13,7 +13,7 @@ use entry::{Entry, EntryCommitted, EntryInvalid, EntryNew, EntryValid};
use error::{OperationError, SchemaError}; use error::{OperationError, SchemaError};
use event::{CreateEvent, DeleteEvent, ExistsEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent}; use event::{CreateEvent, DeleteEvent, ExistsEvent, ModifyEvent, ReviveRecycledEvent, SearchEvent};
use filter::{Filter, FilterInvalid}; use filter::{Filter, FilterInvalid};
use modify::{Modify, ModifyList}; use modify::{Modify, ModifyList, ModifyInvalid};
use plugins::Plugins; use plugins::Plugins;
use schema::{Schema, SchemaReadTransaction, SchemaTransaction, SchemaWriteTransaction}; use schema::{Schema, SchemaReadTransaction, SchemaTransaction, SchemaWriteTransaction};
@ -448,11 +448,16 @@ impl<'a> QueryServerWriteTransaction<'a> {
return Err(OperationError::NoMatchingEntries); return Err(OperationError::NoMatchingEntries);
}; };
let modlist = ModifyList::new_list(vec![Modify::Present( let modlist_inv = ModifyList::new_list(vec![Modify::Present(
String::from("class"), String::from("class"),
String::from("recycled"), String::from("recycled"),
)]); )]);
let modlist = match modlist_inv.validate(&self.schema) {
Ok(ml) => ml,
Err(e) => return Err(OperationError::SchemaViolation(e)),
};
let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
.into_iter() .into_iter()
.map(|er| { .map(|er| {
@ -647,6 +652,12 @@ impl<'a> QueryServerWriteTransaction<'a> {
return Err(OperationError::EmptyRequest); return Err(OperationError::EmptyRequest);
} }
// Is the modlist valid?
let modlist = match me.modlist.validate(&self.schema) {
Ok(ml) => ml,
Err(e) => return Err(OperationError::SchemaViolation(e)),
};
// Is the filter invalid to schema? // Is the filter invalid to schema?
// WARNING! Check access controls here!!!! // WARNING! Check access controls here!!!!
@ -675,7 +686,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
.into_iter() .into_iter()
.map(|er| { .map(|er| {
// TODO: Deal with this properly william // TODO: Deal with this properly william
er.invalidate().apply_modlist(&me.modlist).unwrap() er.invalidate().apply_modlist(&modlist).unwrap()
}) })
.collect(); .collect();
@ -786,7 +797,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
&self, &self,
audit: &mut AuditScope, audit: &mut AuditScope,
filter: Filter<FilterInvalid>, filter: Filter<FilterInvalid>,
modlist: ModifyList, modlist: ModifyList<ModifyInvalid>,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
let mut audit_int = AuditScope::new("internal_modify"); let mut audit_int = AuditScope::new("internal_modify");
let me = ModifyEvent::new_internal(filter, modlist); let me = ModifyEvent::new_internal(filter, modlist);
@ -799,7 +810,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
&self, &self,
audit: &mut AuditScope, audit: &mut AuditScope,
filter: Filter<FilterInvalid>, filter: Filter<FilterInvalid>,
modlist: ModifyList, modlist: ModifyList<ModifyInvalid>,
) -> Result<(), OperationError> { ) -> Result<(), OperationError> {
let mut audit_int = AuditScope::new("impersonate_modify"); let mut audit_int = AuditScope::new("impersonate_modify");
let me = ModifyEvent::new_internal(filter, modlist); let me = ModifyEvent::new_internal(filter, modlist);