Fix most of the error handling, but now may need to fix schema validation check of candidates

This commit is contained in:
William Brown 2019-02-25 15:25:21 +10:00
parent 39da85aefd
commit 62ec6dd603
10 changed files with 98 additions and 79 deletions

View file

@ -85,7 +85,7 @@ impl Message for AuditScope {
impl fmt::Display for AuditScope { impl fmt::Display for AuditScope {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut depth = 0; let mut _depth = 0;
// write!(f, "{}: begin -> {}", self.time, self.name); // write!(f, "{}: begin -> {}", self.time, self.name);
let d = serde_json::to_string_pretty(self).unwrap(); let d = serde_json::to_string_pretty(self).unwrap();
write!(f, "{}", d) write!(f, "{}", d)

View file

@ -10,6 +10,7 @@ use serde_json;
use audit::AuditScope; use audit::AuditScope;
use entry::{Entry, EntryCommitted, EntryNew, EntryValid}; use entry::{Entry, EntryCommitted, EntryNew, EntryValid};
use filter::{Filter, FilterValid}; use filter::{Filter, FilterValid};
use error::OperationError;
mod idl; mod idl;
mod mem_be; mod mem_be;
@ -392,7 +393,7 @@ impl BackendWriteTransaction {
unimplemented!() unimplemented!()
} }
pub fn commit(mut self) -> Result<(), ()> { pub fn commit(mut self) -> Result<(), OperationError> {
println!("Commiting txn"); println!("Commiting txn");
assert!(!self.committed); assert!(!self.committed);
self.committed = true; self.committed = true;
@ -401,7 +402,7 @@ impl BackendWriteTransaction {
.map(|_| ()) .map(|_| ())
.map_err(|e| { .map_err(|e| {
println!("{:?}", e); println!("{:?}", e);
() OperationError::BackendEngine
}) })
} }
@ -490,7 +491,7 @@ impl BackendWriteTransaction {
// In the future this will do the routing between the chosen backends etc. // In the future this will do the routing between the chosen backends etc.
impl Backend { impl Backend {
pub fn new(audit: &mut AuditScope, path: &str) -> Result<Self, ()> { pub fn new(audit: &mut AuditScope, path: &str) -> Result<Self, OperationError> {
// this has a ::memory() type, but will path == "" work? // this has a ::memory() type, but will path == "" work?
audit_segment!(audit, || { audit_segment!(audit, || {
let manager = SqliteConnectionManager::file(path); let manager = SqliteConnectionManager::file(path);
@ -755,4 +756,12 @@ mod tests {
assert!(be.delete(audit, &vec![r2.clone(), r3.clone()]).is_ok()); assert!(be.delete(audit, &vec![r2.clone(), r3.clone()]).is_ok());
}); });
} }
#[test]
fn test_backup_restore() {
run_test!(|audit: &mut AuditScope, be: &BackendWriteTransaction| {
be.restore();
be.backup();
});
)
} }

View file

@ -2,8 +2,8 @@
use actix::Actor; use actix::Actor;
use actix_web::middleware::session::{self, RequestSession}; use actix_web::middleware::session::{self, RequestSession};
use actix_web::{ use actix_web::{
error, http, middleware, App, AsyncResponder, Error, FutureResponse, HttpMessage, HttpRequest, error, http, middleware, App, Error, HttpMessage, HttpRequest,
HttpResponse, Path, Result, State, HttpResponse, Result, State,
}; };
use bytes::BytesMut; use bytes::BytesMut;
@ -13,11 +13,10 @@ use super::config::Configuration;
// SearchResult // SearchResult
use super::event::{AuthEvent, CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; use super::event::{AuthEvent, CreateEvent, DeleteEvent, ModifyEvent, SearchEvent};
use super::filter::Filter;
use super::interval::IntervalActor; use super::interval::IntervalActor;
use super::log; use super::log;
use super::proto_v1::{ use super::proto_v1::{
AuthRequest, AuthResponse, CreateRequest, DeleteRequest, ModifyRequest, SearchRequest, AuthRequest, CreateRequest, DeleteRequest, ModifyRequest, SearchRequest,
}; };
use super::server; use super::server;

View file

@ -140,6 +140,7 @@ pub struct Entry<VALID, STATE> {
} }
impl Entry<EntryInvalid, EntryNew> { impl Entry<EntryInvalid, EntryNew> {
/*
pub fn new() -> Self { pub fn new() -> Self {
Entry { Entry {
// This means NEVER COMMITED // This means NEVER COMMITED
@ -149,6 +150,7 @@ impl Entry<EntryInvalid, EntryNew> {
attrs: BTreeMap::new(), attrs: BTreeMap::new(),
} }
} }
*/
// FIXME: Can we consume protoentry? // FIXME: Can we consume protoentry?
pub fn from(e: &ProtoEntry) -> Self { pub fn from(e: &ProtoEntry) -> Self {
@ -182,7 +184,7 @@ impl<STATE> Entry<EntryInvalid, STATE> {
// We need to clone before we start, as well be mutating content. // We need to clone before we start, as well be mutating content.
// We destructure: // We destructure:
let Entry { let Entry {
valid, valid: _,
state, state,
id, id,
attrs, attrs,
@ -356,6 +358,7 @@ impl<VALID, STATE> Entry<VALID, STATE> {
} }
} }
#[cfg(test)]
pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> { pub unsafe fn to_valid_committed(self) -> Entry<EntryValid, EntryCommitted> {
Entry { Entry {
valid: EntryValid, valid: EntryValid,
@ -514,6 +517,7 @@ impl<STATE> Entry<EntryValid, STATE> {
mods.push_mod(Modify::Purged(k.clone())); mods.push_mod(Modify::Purged(k.clone()));
} }
} }
// TODO: Do something with this error properly.
Err(e) => return Err(()), Err(e) => return Err(()),
} }
for v in vs { for v in vs {

View file

@ -1,4 +1,6 @@
#[derive(Debug, PartialEq)] // use rusqlite::Error as RusqliteError;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum SchemaError { pub enum SchemaError {
NotImplemented, NotImplemented,
InvalidClass, InvalidClass,
@ -16,10 +18,11 @@ pub enum OperationError {
EmptyRequest, EmptyRequest,
Backend, Backend,
NoMatchingEntries, NoMatchingEntries,
SchemaViolation, SchemaViolation(SchemaError),
Plugin, Plugin,
FilterGeneration, FilterGeneration,
InvalidDBState, InvalidDBState,
InvalidRequestState, InvalidRequestState,
InvalidState, InvalidState,
BackendEngine,
} }

View file

@ -4,8 +4,7 @@
use error::SchemaError; use error::SchemaError;
use proto_v1::Filter as ProtoFilter; use proto_v1::Filter as ProtoFilter;
use regex::Regex; use schema::{SchemaReadTransaction};
use schema::{SchemaAttribute, SchemaClass, SchemaReadTransaction};
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -177,7 +176,7 @@ impl Filter<FilterInvalid> {
ProtoFilter::Pres(a) => Filter::Pres(a.clone()), ProtoFilter::Pres(a) => Filter::Pres(a.clone()),
ProtoFilter::Or(l) => Filter::Or(l.iter().map(|f| Self::from(f)).collect()), ProtoFilter::Or(l) => Filter::Or(l.iter().map(|f| Self::from(f)).collect()),
ProtoFilter::And(l) => Filter::And(l.iter().map(|f| Self::from(f)).collect()), ProtoFilter::And(l) => Filter::And(l.iter().map(|f| Self::from(f)).collect()),
ProtoFilter::AndNot(l) => Filter::AndNot(Box::new(Self::from(f))), ProtoFilter::AndNot(l) => Filter::AndNot(Box::new(Self::from(l))),
} }
} }
} }

View file

@ -2,12 +2,12 @@ use plugins::Plugin;
use uuid::Uuid; use uuid::Uuid;
use audit::AuditScope; use audit::AuditScope;
use be::{BackendReadTransaction, BackendTransaction, BackendWriteTransaction}; use be::{BackendReadTransaction, BackendWriteTransaction};
use entry::{Entry, EntryInvalid, EntryNew}; use entry::{Entry, EntryInvalid, EntryNew};
use error::OperationError; use error::OperationError;
use event::CreateEvent; use event::CreateEvent;
use filter::Filter; use filter::Filter;
use schema::{SchemaTransaction, SchemaWriteTransaction}; use schema::{SchemaWriteTransaction};
// TO FINISH // TO FINISH
/* /*

View file

@ -1,9 +1,9 @@
use audit::AuditScope; use audit::AuditScope;
use be::{BackendTransaction, BackendWriteTransaction}; use be::{BackendWriteTransaction};
use entry::{Entry, EntryInvalid, EntryNew}; use entry::{Entry, EntryInvalid, EntryNew};
use error::OperationError; use error::OperationError;
use event::CreateEvent; use event::CreateEvent;
use schema::{SchemaTransaction, SchemaWriteTransaction}; use schema::{SchemaWriteTransaction};
mod base; mod base;
mod protected; mod protected;

View file

@ -1,7 +1,7 @@
use super::audit::AuditScope; use super::audit::AuditScope;
use super::constants::*; use super::constants::*;
// use super::entry::Entry; // use super::entry::Entry;
use super::error::SchemaError; use super::error::{SchemaError, OperationError};
// use super::filter::Filter; // use super::filter::Filter;
use std::collections::HashMap; use std::collections::HashMap;
// Apparently this is nightly only? // Apparently this is nightly only?
@ -289,7 +289,7 @@ pub struct SchemaInner {
pub trait SchemaReadTransaction { pub trait SchemaReadTransaction {
fn get_inner(&self) -> &SchemaInner; fn get_inner(&self) -> &SchemaInner;
fn validate(&self, audit: &mut AuditScope) -> Result<(), ()> { fn validate(&self, audit: &mut AuditScope) -> Result<(), OperationError> {
self.get_inner().validate(audit) self.get_inner().validate(audit)
} }
@ -314,7 +314,7 @@ pub trait SchemaReadTransaction {
} }
impl SchemaInner { impl SchemaInner {
pub fn new(audit: &mut AuditScope) -> Result<Self, ()> { pub fn new(audit: &mut AuditScope) -> Result<Self, OperationError> {
let mut au = AuditScope::new("schema_new"); let mut au = AuditScope::new("schema_new");
let r = audit_segment!(au, || { let r = audit_segment!(au, || {
// //
@ -643,7 +643,7 @@ impl SchemaInner {
} }
// This shouldn't fail? // This shouldn't fail?
pub fn bootstrap_core(&mut self, audit: &mut AuditScope) -> Result<(), ()> { pub fn bootstrap_core(&mut self, audit: &mut AuditScope) -> Result<(), OperationError> {
// This will create a set of sane, system core schema that we can use // This will create a set of sane, system core schema that we can use
// main types are users, groups // main types are users, groups
let mut au = AuditScope::new("schema_bootstrap_core"); let mut au = AuditScope::new("schema_bootstrap_core");
@ -845,9 +845,7 @@ impl SchemaInner {
r r
} }
pub fn validate(&self, audit: &mut AuditScope) -> Result<(), ()> { pub fn validate(&self, audit: &mut AuditScope) -> Result<(), OperationError> {
// FIXME: How can we make this return a proper result?
//
// TODO: Does this need to validate anything further at all? The UUID // TODO: Does this need to validate anything further at all? The UUID
// will be checked as part of the schema migration on startup, so I think // will be checked as part of the schema migration on startup, so I think
// just that all the content is sane is fine. // just that all the content is sane is fine.
@ -862,14 +860,14 @@ impl SchemaInner {
a a
); );
if !self.attributes.contains_key(a) { if !self.attributes.contains_key(a) {
return Err(()); return Err(OperationError::SchemaViolation(SchemaError::Corrupted));
} }
} }
for a in &class.may { for a in &class.may {
// report the attribute. // report the attribute.
audit_log!(audit, "validate may class:attr -> {}:{}", class.name, a); audit_log!(audit, "validate may class:attr -> {}:{}", class.name, a);
if !self.attributes.contains_key(a) { if !self.attributes.contains_key(a) {
return Err(()); return Err(OperationError::SchemaViolation(SchemaError::Corrupted));
} }
} }
for a in &class.systemmust { for a in &class.systemmust {
@ -881,14 +879,14 @@ impl SchemaInner {
a a
); );
if !self.attributes.contains_key(a) { if !self.attributes.contains_key(a) {
return Err(()); return Err(OperationError::SchemaViolation(SchemaError::Corrupted));
} }
} }
for a in &class.must { for a in &class.must {
// report the attribute. // report the attribute.
audit_log!(audit, "validate must class:attr -> {}:{}", class.name, a); audit_log!(audit, "validate must class:attr -> {}:{}", class.name, a);
if !self.attributes.contains_key(a) { if !self.attributes.contains_key(a) {
return Err(()); return Err(OperationError::SchemaViolation(SchemaError::Corrupted));
} }
} }
} }
@ -924,7 +922,7 @@ pub struct SchemaWriteTransaction<'a> {
} }
impl<'a> SchemaWriteTransaction<'a> { impl<'a> SchemaWriteTransaction<'a> {
pub fn bootstrap_core(&mut self, audit: &mut AuditScope) -> Result<(), ()> { pub fn bootstrap_core(&mut self, audit: &mut AuditScope) -> Result<(), OperationError> {
self.inner.bootstrap_core(audit) self.inner.bootstrap_core(audit)
} }
@ -933,8 +931,9 @@ impl<'a> SchemaWriteTransaction<'a> {
// first, then schema to ensure that the be content matches our schema. Saying this, if your // first, then schema to ensure that the be content matches our schema. Saying this, if your
// schema commit fails we need to roll back still .... How great are transactions. // schema commit fails we need to roll back still .... How great are transactions.
// At the least, this is what validation is for! // At the least, this is what validation is for!
pub fn commit(self) { pub fn commit(self) -> Result<(), OperationError> {
self.inner.commit(); self.inner.commit();
Ok(())
} }
} }
@ -957,7 +956,7 @@ impl SchemaReadTransaction for SchemaTransaction {
} }
impl Schema { impl Schema {
pub fn new(audit: &mut AuditScope) -> Result<Self, ()> { pub fn new(audit: &mut AuditScope) -> Result<Self, OperationError> {
SchemaInner::new(audit).map(|si| Schema { SchemaInner::new(audit).map(|si| Schema {
inner: CowCell::new(si), inner: CowCell::new(si),
}) })

View file

@ -110,7 +110,8 @@ pub trait QueryServerReadTransaction {
// TODO: Validate the filter // TODO: Validate the filter
let vf = match se.filter.validate(self.get_schema()) { let vf = match se.filter.validate(self.get_schema()) {
Ok(f) => f, Ok(f) => f,
Err(e) => return Err(OperationError::SchemaViolation), // TODO: Do something with this error
Err(e) => return Err(OperationError::SchemaViolation(e)),
}; };
// TODO: Assert access control allows the filter and requested attrs. // TODO: Assert access control allows the filter and requested attrs.
@ -139,7 +140,8 @@ pub trait QueryServerReadTransaction {
// How to get schema? // How to get schema?
let vf = match ee.filter.validate(self.get_schema()) { let vf = match ee.filter.validate(self.get_schema()) {
Ok(f) => f, Ok(f) => f,
Err(e) => return Err(OperationError::SchemaViolation), // TODO: Do something with this error
Err(e) => return Err(OperationError::SchemaViolation(e)),
}; };
let res = self let res = self
@ -316,6 +318,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
return plug_pre_res; return plug_pre_res;
} }
// TODO: Rework this to be better.
let (norm_cand, invalid_cand): ( let (norm_cand, invalid_cand): (
Vec<Result<Entry<EntryValid, EntryNew>, _>>, Vec<Result<Entry<EntryValid, EntryNew>, _>>,
Vec<Result<_, SchemaError>>, Vec<Result<_, SchemaError>>,
@ -328,8 +331,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
audit_log!(au, "Schema Violation: {:?}", err); audit_log!(au, "Schema Violation: {:?}", err);
} }
if invalid_cand.len() > 0 { for err in invalid_cand.iter() {
return Err(OperationError::SchemaViolation); return Err(OperationError::SchemaViolation(err.unwrap_err()));
} }
let norm_cand: Vec<Entry<EntryValid, EntryNew>> = norm_cand let norm_cand: Vec<Entry<EntryValid, EntryNew>> = norm_cand
@ -399,7 +402,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
String::from("recycled"), String::from("recycled"),
)]); )]);
let mut candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
.into_iter() .into_iter()
.map(|er| { .map(|er| {
// TODO: Deal with this properly william // TODO: Deal with this properly william
@ -426,8 +429,9 @@ impl<'a> QueryServerWriteTransaction<'a> {
audit_log!(au, "Schema Violation: {:?}", err); audit_log!(au, "Schema Violation: {:?}", err);
} }
if invalid_cand.len() > 0 { // TODO: Make this better
return Err(OperationError::SchemaViolation); for err in invalid_cand.iter() {
return Err(OperationError::SchemaViolation(err.unwrap_err()));
} }
let del_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand let del_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand
@ -602,7 +606,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
// Clone a set of writeables. // Clone a set of writeables.
// Apply the modlist -> Remember, we have a set of origs // Apply the modlist -> Remember, we have a set of origs
// and the new modified ents. // and the new modified ents.
let mut candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates let candidates: Vec<Entry<EntryInvalid, EntryCommitted>> = pre_candidates
.into_iter() .into_iter()
.map(|er| { .map(|er| {
// TODO: Deal with this properly william // TODO: Deal with this properly william
@ -630,8 +634,9 @@ impl<'a> QueryServerWriteTransaction<'a> {
audit_log!(au, "Schema Violation: {:?}", err); audit_log!(au, "Schema Violation: {:?}", err);
} }
if invalid_cand.len() > 0 { // TODO: Make this better
return Err(OperationError::SchemaViolation); for err in invalid_cand.iter() {
return Err(OperationError::SchemaViolation(err.unwrap_err()));
} }
let norm_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand let norm_cand: Vec<Entry<EntryValid, EntryCommitted>> = norm_cand
@ -865,7 +870,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
Ok(()) Ok(())
} }
pub fn commit(self, audit: &mut AuditScope) -> Result<(), ()> { pub fn commit(self, audit: &mut AuditScope) -> Result<(), OperationError> {
let QueryServerWriteTransaction { let QueryServerWriteTransaction {
committed, committed,
be_txn, be_txn,
@ -880,14 +885,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
// to perform a reload BEFORE we commit. // to perform a reload BEFORE we commit.
// Alternate, we attempt to reload during batch ops, but this seems // Alternate, we attempt to reload during batch ops, but this seems
// costly. // costly.
.map(|_| { .and_then(|_| {
// Backend Commit // Backend Commit
be_txn.commit() be_txn.commit()
}) .and_then(|_| {
.map(|_| { // Schema commit: Since validate passed and be is good, this
// Schema commit: Since validate passed and be is good, this // must now also be good.
// must now also be good. schema.commit()
schema.commit() })
}) })
// Audit done // Audit done
} }
@ -947,13 +952,13 @@ impl Handler<CreateEvent> for QueryServer {
let qs_write = self.write(); let qs_write = self.write();
match qs_write.create(&mut audit, &msg) { qs_write.create(&mut audit, &msg)
Ok(()) => { .and_then(|_| {
qs_write.commit(&mut audit); qs_write.commit(&mut audit)
Ok(OpResult {}) .map(|_| {
} OpResult {}
Err(e) => Err(e), })
} })
}); });
// At the end of the event we send it for logging. // At the end of the event we send it for logging.
self.log.do_send(audit); self.log.do_send(audit);
@ -971,13 +976,13 @@ impl Handler<ModifyEvent> for QueryServer {
let qs_write = self.write(); let qs_write = self.write();
match qs_write.modify(&mut audit, &msg) { qs_write.modify(&mut audit, &msg)
Ok(()) => { .and_then(|_| {
qs_write.commit(&mut audit); qs_write.commit(&mut audit)
Ok(OpResult {}) .map(|_| {
} OpResult {}
Err(e) => Err(e), })
} })
}); });
self.log.do_send(audit); self.log.do_send(audit);
res res
@ -994,13 +999,13 @@ impl Handler<DeleteEvent> for QueryServer {
let qs_write = self.write(); let qs_write = self.write();
match qs_write.delete(&mut audit, &msg) { qs_write.delete(&mut audit, &msg)
Ok(()) => { .and_then(|_| {
qs_write.commit(&mut audit); qs_write.commit(&mut audit)
Ok(OpResult {}) .map(|_| {
} OpResult {}
Err(e) => Err(e), })
} })
}); });
self.log.do_send(audit); self.log.do_send(audit);
res res
@ -1031,17 +1036,19 @@ impl Handler<PurgeEvent> for QueryServer {
audit_log!(audit, "Begin purge tombstone event {:?}", msg); audit_log!(audit, "Begin purge tombstone event {:?}", msg);
let qs_write = self.write(); let qs_write = self.write();
let res = match qs_write.purge_tombstones(&mut audit) { let res = qs_write.purge_tombstones(&mut audit)
Ok(()) => { .map(|_| {
qs_write.commit(&mut audit); qs_write.commit(&mut audit)
Ok(OpResult {}) .map(|_| {
} OpResult {}
Err(e) => Err(e), })
}; });
audit_log!(audit, "Purge tombstones result: {:?}", res); audit_log!(audit, "Purge tombstones result: {:?}", res);
res.expect("Invalid Server State");
}); });
// At the end of the event we send it for logging. // At the end of the event we send it for logging.
self.log.do_send(audit); self.log.do_send(audit);
res
} }
} }
@ -1668,8 +1675,6 @@ mod tests {
String::from("testperson1"), String::from("testperson1"),
)); ));
assert!(server_txn.delete(audit, &de_sin).is_ok()); assert!(server_txn.delete(audit, &de_sin).is_ok());
// After a delete -> recycle, create duplicate name etc.
// Can in be seen by special search? (external recycle search) // Can in be seen by special search? (external recycle search)
let filt_rc = ProtoFilter::Eq(String::from("class"), String::from("recycled")); let filt_rc = ProtoFilter::Eq(String::from("class"), String::from("recycled"));
let sre_rc = SearchEvent::from_rec_request(SearchRecycledRequest::new(filt_rc.clone())); let sre_rc = SearchEvent::from_rec_request(SearchRecycledRequest::new(filt_rc.clone()));
@ -1677,6 +1682,7 @@ mod tests {
assert!(r2.len() == 1); assert!(r2.len() == 1);
// Create dup uuid (rej) // Create dup uuid (rej)
// After a delete -> recycle, create duplicate name etc.
let cr = server_txn.create(audit, &ce); let cr = server_txn.create(audit, &ce);
assert!(cr.is_err()); assert!(cr.is_err());