Add plugin links,

This commit is contained in:
William Brown 2018-12-29 20:54:02 +10:00
parent b888d9036f
commit db024258f2
5 changed files with 257 additions and 53 deletions

View file

@ -12,7 +12,7 @@ macro_rules! audit_log {
($audit:expr, $($arg:tt)*) => ({
use std::fmt;
if cfg!(test) || cfg!(debug_assertions) {
print!("DEBUG AUDIT -> ");
print!("DEBUG AUDIT ({})-> ", $audit.id());
println!($($arg)*)
}
$audit.log_event(
@ -101,6 +101,10 @@ impl AuditScope {
}
}
pub fn id(&self) -> &str {
self.name.as_str()
}
// Given a new audit event, append it in.
pub fn append_scope(&mut self, scope: AuditScope) {
self.events.push(AuditEvent::scope(scope))

View file

@ -5,7 +5,11 @@ use error::OperationError;
use event::CreateEvent;
use schema::Schema;
mod uuid;
trait Plugin {
fn id() -> &'static str;
fn pre_create(
be: &mut Backend,
au: &mut AuditScope,
@ -45,7 +49,48 @@ trait Plugin {
}
}
mod uuid;
pub struct Plugins{}
macro_rules! run_pre_create_plugin {
(
$be:ident,
$au:ident,
$cand:ident,
$ce:ident,
$schema:ident,
$target_plugin:ty
) => {{
let mut audit_scope = AuditScope::new(<($target_plugin)>::id());
let r = audit_segment!(audit_scope, || {
<($target_plugin)>::pre_create(
$be, &mut audit_scope, $cand, $ce, $schema
)
});
$au.append_scope(audit_scope);
r
}}
}
impl Plugins {
pub fn run_pre_create(
be: &mut Backend,
au: &mut AuditScope,
cand: &mut Vec<Entry>,
ce: &CreateEvent,
schema: &Schema,
) -> Result<(), OperationError> {
audit_segment!(audit_plugin_pre, || {
// map chain?
let uuid_res = run_pre_create_plugin!(be, au, cand, ce, schema, uuid::UUID);
// TODO, actually return the right thing ...
uuid_res
})
}
}
// We should define the order that plugins should run

View file

@ -9,9 +9,12 @@ use event::CreateEvent;
use filter::Filter;
use schema::Schema;
struct UUID {}
pub struct UUID {}
impl Plugin for UUID {
fn id() -> &'static str {
"UUID"
}
// Need to be given the backend(for testing ease)
// audit
// the mut set of entries to create
@ -30,17 +33,36 @@ impl Plugin for UUID {
for entry in cand.iter_mut() {
let name_uuid = String::from("uuid");
audit_log!(au, "UUID check on entry: {:?}", entry);
// if they don't have uuid, create it.
// TODO: get_ava should have a str version for effeciency?
let mut c_uuid = match entry.get_ava(&name_uuid) {
Some(u) => {
// Actually check we have a value, could be empty array ...
let v = u.first().unwrap();
if u.len() > 1 {
audit_log!(au, "Entry defines uuid attr, but multiple values.");
return Err(OperationError::Plugin)
};
let v = match u.first() {
Some(v) => v,
None => {
audit_log!(au, "Entry defines uuid attr, but no value.");
return Err(OperationError::Plugin)
}
};
// This could actually fail, so we probably need to handle
// this better ....
// TODO: Make this a SCHEMA check, not a manual one.
//
match Uuid::parse_str(v.as_str()) {
Ok(up) => up,
Err(_) => return Err(OperationError::Plugin),
Err(_) => {
audit_log!(au, "Entry contains invalid UUID content, rejecting out of principle.");
return Err(OperationError::Plugin)
}
}
}
None => Uuid::new_v4(),
@ -48,7 +70,6 @@ impl Plugin for UUID {
// Make it a string, so we can filter.
let str_uuid = format!("{}", c_uuid);
println!("{}", str_uuid);
let mut au_be = AuditScope::new("be_exist");
@ -58,27 +79,25 @@ impl Plugin for UUID {
let r = be.exists(&mut au_be, &filt);
au.append_scope(au_be);
// end the scope?
// end the scope for the be operation.
match r {
Ok(b) => {
if b == true {
audit_log!(au, "UUID already exists, rejecting.");
return Err(OperationError::Plugin);
}
}
Err(e) => return Err(OperationError::Plugin),
Err(e) => {
audit_log!(au, "Backend error occured checking UUID existance.");
return Err(OperationError::Plugin)
}
}
// check that the uuid is unique in the be (even if one is provided
// we especially need to check that)
// if not unique, generate another, and try again.
// If it's okay, now put it into the entry.
// we may need to inject the base OC required for all objects in our
// server to support this?
let str_uuid = format!("{}", c_uuid);
audit_log!(au, "Set UUID {} to entry", str_uuid);
let ava_uuid: Vec<String> = vec![str_uuid];
entry.set_avas(name_uuid, ava_uuid);
}
// done!
@ -142,9 +161,6 @@ mod tests {
// Check empty create
#[test]
fn test_pre_create_empty() {
// Need a macro to create all the bits here ...
// Macro needs preload entries, the create entries
// schema, identity for create event (later)
let preload: Vec<Entry> = Vec::new();
let mut create: Vec<Entry> = Vec::new();
run_pre_create_test!(
@ -169,9 +185,6 @@ mod tests {
// check create where no uuid
#[test]
fn test_pre_create_no_uuid() {
// Need a macro to create all the bits here ...
// Macro needs preload entries, the create entries
// schema, identity for create event (later)
let preload: Vec<Entry> = Vec::new();
let e: Entry = serde_json::from_str(
@ -208,16 +221,81 @@ mod tests {
}
// check unparseable uuid
#[test]
fn test_pre_create_uuid_invalid() {
let preload: Vec<Entry> = Vec::new();
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"],
"uuid": ["xxxxxx"]
}
}"#,
)
.unwrap();
let mut create = vec![e.clone()];
run_pre_create_test!(
preload,
create,
false,
false,
|be: &mut Backend,
au: &mut AuditScope,
cand: &mut Vec<Entry>,
ce: &CreateEvent,
schema: &Schema| {
let r = UUID::pre_create(be, au, cand, ce, schema);
assert!(r.is_err());
}
);
}
// check entry where uuid is empty list
#[test]
fn test_pre_create_uuid_empty() {
let preload: Vec<Entry> = Vec::new();
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"],
"uuid": []
}
}"#,
)
.unwrap();
let mut create = vec![e.clone()];
run_pre_create_test!(
preload,
create,
false,
false,
|be: &mut Backend,
au: &mut AuditScope,
cand: &mut Vec<Entry>,
ce: &CreateEvent,
schema: &Schema| {
let r = UUID::pre_create(be, au, cand, ce, schema);
assert!(r.is_err());
}
);
}
// check create where provided uuid is valid. It should be unchanged.
// check create where uuid already exists.
#[test]
fn test_pre_create_uuid_exist() {
// Need a macro to create all the bits here ...
// Macro needs preload entries, the create entries
// schema, identity for create event (later)
fn test_pre_create_uuid_valid() {
let preload: Vec<Entry> = Vec::new();
let e: Entry = serde_json::from_str(
@ -234,7 +312,78 @@ mod tests {
.unwrap();
let mut create = vec![e.clone()];
let mut preload = vec![e];
run_pre_create_test!(
preload,
create,
false,
false,
|be: &mut Backend,
au: &mut AuditScope,
cand: &mut Vec<Entry>,
ce: &CreateEvent,
schema: &Schema| {
let r = UUID::pre_create(be, au, cand, ce, schema);
assert!(r.is_ok());
let ue = cand.first().unwrap();
assert!(ue.attribute_equality("uuid", "79724141-3603-4060-b6bb-35c72772611d"));
}
);
}
#[test]
fn test_pre_create_uuid_valid_multi() {
let preload: Vec<Entry> = Vec::new();
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"],
"uuid": ["79724141-3603-4060-b6bb-35c72772611d", "79724141-3603-4060-b6bb-35c72772611d"]
}
}"#,
)
.unwrap();
let mut create = vec![e.clone()];
run_pre_create_test!(
preload,
create,
false,
false,
|be: &mut Backend,
au: &mut AuditScope,
cand: &mut Vec<Entry>,
ce: &CreateEvent,
schema: &Schema| {
let r = UUID::pre_create(be, au, cand, ce, schema);
assert!(r.is_err());
}
);
}
// check create where uuid already exists.
#[test]
fn test_pre_create_uuid_exist() {
let e: Entry = serde_json::from_str(
r#"{
"attrs": {
"class": ["person"],
"name": ["testperson"],
"description": ["testperson"],
"displayname": ["testperson"],
"uuid": ["79724141-3603-4060-b6bb-35c72772611d"]
}
}"#,
)
.unwrap();
let mut create = vec![e.clone()];
let preload = vec![e];
run_pre_create_test!(
preload,

View file

@ -668,21 +668,6 @@ impl Schema {
.map(|s| (s.clone(), self.attributes.get(s).unwrap()))
.collect();
/*
let may: HashMap<String, &SchemaAttribute> = classes
.iter()
// Join our class systemmmust + must into one iter
.flat_map(|(_, cls)| {
cls.systemmust
.iter()
.chain(cls.must.iter())
.chain(cls.systemmay.iter())
.chain(cls.may.iter())
})
.map(|s| (s.clone(), self.attributes.get(s).unwrap()))
.collect();
*/
// FIXME: Error needs to say what is missing
// We need to return *all* missing attributes.
@ -822,7 +807,9 @@ impl Schema {
// Normalise *does not* validate.
// Normalise just fixes some possible common issues, but it
// can't fix *everything* possibly wrong ...
pub fn normalise_filter(&mut self) {}
pub fn normalise_filter(&mut self) {
unimplemented!()
}
}
#[cfg(test)]

View file

@ -7,7 +7,7 @@ use entry::Entry;
use error::OperationError;
use event::{CreateEvent, OpResult, SearchEvent, SearchResult};
use log::EventLog;
use plugins;
use plugins::Plugins;
use schema::Schema;
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
@ -96,16 +96,26 @@ impl QueryServer {
// based on request size in the frontend?
// Copy the entries to a writeable form.
let mut candidates: Vec<_> = ce.entries.iter().collect();
let mut candidates: Vec<Entry> = ce.entries.iter()
.map(|er| er.clone())
.collect();
// Start a txn
// run any pre plugins, giving them the list of mutable candidates.
// pre-plugins are defined here in their correct order of calling!
// I have no intent to make these dynamic or configurable.
// Run any pre checks
// FIXME: Normalise all entries incoming
let mut audit_plugin_pre = AuditScope::new("plugin_pre_create");
let plug_pre_res = Plugins::run_pre_create(&mut self.be, &mut audit_plugin_pre, &mut candidates, ce, &self.schema);
au.append_scope(audit_plugin_pre);
let r = ce.entries.iter().fold(Ok(()), |acc, e| {
if plug_pre_res.is_err() {
audit_log!(au, "Create operation failed (plugin), {:?}", plug_pre_res);
return plug_pre_res;
}
let r = candidates.iter().fold(Ok(()), |acc, e| {
if acc.is_ok() {
self.schema
.validate_entry(e)
@ -115,26 +125,35 @@ impl QueryServer {
}
});
if r.is_err() {
audit_log!(au, "Create operation failed (schema), {:?}", r);
return r;
}
// FIXME: Normalise all entries now.
let mut audit_be = AuditScope::new("backend_create");
// We may change from ce.entries later to something else?
let res = self
.be
.create(&mut audit_be, &ce.entries)
.create(&mut audit_be, &candidates)
.map(|_| ())
.map_err(|e| match e {
BackendError::EmptyRequest => OperationError::EmptyRequest,
_ => OperationError::Backend,
});
au.append_scope(audit_be);
if res.is_err() {
audit_log!(au, "Create operation failed (backend), {:?}", r);
return res;
}
// Run any post plugins
// Commit/Abort the txn
// Commit the txn
// We are complete, finalise logging and return
au.append_scope(audit_be);
audit_log!(au, "Create operation success");
res
}
}