mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Schema aware create now works!
This commit is contained in:
parent
56264b5b7b
commit
dddd04898c
|
@ -274,9 +274,7 @@ mod tests {
|
|||
assert_eq!(empty_result, Err(BackendError::EmptyRequest));
|
||||
|
||||
let mut e: Entry = Entry::new();
|
||||
e.add_ava(String::from("userid"), String::from("william"))
|
||||
.unwrap();
|
||||
assert!(e.validate());
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
|
||||
let single_result = be.create(audit, &vec![e]);
|
||||
|
||||
|
|
86
src/entry.rs
86
src/entry.rs
|
@ -88,27 +88,48 @@ impl Entry {
|
|||
|
||||
// This should always work? It's only on validate that we'll build
|
||||
// a list of syntax violations ...
|
||||
pub fn add_ava(&mut self, attr: String, value: String) -> Result<(), ()> {
|
||||
// If this already exists, we silently drop the event? Is that an
|
||||
// acceptable interface?
|
||||
// Should value here actually be a &str?
|
||||
pub fn add_ava(&mut self, attr: String, value: String) {
|
||||
// get_mut to access value
|
||||
// How do we make this turn into an ok / err?
|
||||
self.attrs
|
||||
.entry(attr)
|
||||
.and_modify(|v| v.push(value.clone()))
|
||||
.and_modify(|v| {
|
||||
// Here we need to actually do a check/binary search ...
|
||||
v.binary_search(&value).map_err(|idx| {
|
||||
// This cloning is to fix a borrow issue ...
|
||||
v.insert(idx, value.clone())
|
||||
});
|
||||
})
|
||||
.or_insert(vec![value]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_ava(&self, attr: &String) -> Option<&Vec<String>> {
|
||||
self.attrs.get(attr)
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> bool {
|
||||
// We need access to the current system schema here now ...
|
||||
true
|
||||
pub fn attribute_pres(&self, attr: &str) -> bool {
|
||||
// FIXME: Do we need to normalise attr name?
|
||||
self.attrs.contains_key(attr)
|
||||
}
|
||||
|
||||
pub fn pres(&self, attr: &str) -> bool {
|
||||
self.attrs.contains_key(attr)
|
||||
pub fn attribute_equality(&self, attr: &str, value: &str) -> bool {
|
||||
// Do a schema aware equality?
|
||||
// Either we get schema passed in.
|
||||
// OR we assume based on schema normalisation on the way in
|
||||
// that the equality here of the raw values MUST be correct.
|
||||
// If we do this, we likely need a DB normalise function ...
|
||||
// The other issue is we have to normalise what's in the filter
|
||||
// but that could be done *before* we get here?
|
||||
|
||||
// FIXME: Make this binary_search
|
||||
|
||||
self.attrs.get(attr).map_or(false, |v| {
|
||||
v.iter()
|
||||
.fold(false, |acc, av| if acc { acc } else { value == av })
|
||||
})
|
||||
}
|
||||
|
||||
pub fn classes(&self) -> EntryClasses {
|
||||
|
@ -229,42 +250,55 @@ mod tests {
|
|||
#[test]
|
||||
fn test_user_basic() {
|
||||
let u: User = User::new("william", "William Brown");
|
||||
|
||||
println!("u: {:?}", u);
|
||||
|
||||
let d = serde_json::to_string_pretty(&u).unwrap();
|
||||
|
||||
println!("d: {}", d.as_str());
|
||||
|
||||
let u2: User = serde_json::from_str(d.as_str()).unwrap();
|
||||
|
||||
println!("u2: {:?}", u2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entry_basic() {
|
||||
let mut e: Entry = Entry::new();
|
||||
|
||||
e.add_ava(String::from("userid"), String::from("william"))
|
||||
.unwrap();
|
||||
|
||||
assert!(e.validate());
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
|
||||
let d = serde_json::to_string_pretty(&e).unwrap();
|
||||
}
|
||||
|
||||
println!("d: {}", d.as_str());
|
||||
#[test]
|
||||
fn test_entry_dup_value() {
|
||||
// Schema doesn't matter here because we are duplicating a value
|
||||
// it should fail!
|
||||
|
||||
// We still probably need schema here anyway to validate what we
|
||||
// are adding ... Or do we validate after the changes are made in
|
||||
// total?
|
||||
let mut e: Entry = Entry::new();
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
|
||||
let values = e.get_ava(&String::from("userid")).unwrap();
|
||||
// Should only be one value!
|
||||
assert_eq!(values.len(), 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_entry_pres() {
|
||||
let mut e: Entry = Entry::new();
|
||||
|
||||
e.add_ava(String::from("userid"), String::from("william"))
|
||||
.unwrap();
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
|
||||
assert!(e.validate());
|
||||
assert!(e.attribute_pres("userid"));
|
||||
assert!(!e.attribute_pres("name"));
|
||||
}
|
||||
|
||||
assert!(e.pres("userid"));
|
||||
assert!(!e.pres("name"));
|
||||
#[test]
|
||||
fn test_entry_equality() {
|
||||
let mut e: Entry = Entry::new();
|
||||
|
||||
e.add_ava(String::from("userid"), String::from("william"));
|
||||
|
||||
assert!(e.attribute_equality("userid", "william"));
|
||||
assert!(!e.attribute_equality("userid", "test"));
|
||||
assert!(!e.attribute_equality("nonexist", "william"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ pub enum SchemaError {
|
|||
MISSING_MUST_ATTRIBUTE,
|
||||
INVALID_ATTRIBUTE,
|
||||
INVALID_ATTRIBUTE_SYNTAX,
|
||||
EMPTY_FILTER,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// entry to assert it matches.
|
||||
|
||||
use super::entry::Entry;
|
||||
use super::schema::Schema;
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
|
||||
// Perhaps make these json serialisable. Certainly would make parsing
|
||||
|
@ -20,7 +21,9 @@ pub enum Filter {
|
|||
}
|
||||
|
||||
impl Filter {
|
||||
fn optimise(mut self) -> Self {
|
||||
// Does this need mut self? Aren't we returning
|
||||
// a new copied filter?
|
||||
fn optimise(&self) -> Self {
|
||||
// Apply optimisations to the filter
|
||||
// An easy way would be imple partialOrd
|
||||
// then do sort on the or/and/not
|
||||
|
@ -33,12 +36,7 @@ impl Filter {
|
|||
// If an or/not/and condition has no items, remove it
|
||||
//
|
||||
// If its the root item?
|
||||
self
|
||||
}
|
||||
|
||||
// In the future this will probably be used with schema ...
|
||||
fn validate(&self) -> Result<(), ()> {
|
||||
Ok(())
|
||||
self.clone()
|
||||
}
|
||||
|
||||
// This is probably not safe, so it's for internal test cases
|
||||
|
@ -62,7 +60,7 @@ impl Filter {
|
|||
Filter::Sub(_, _) => false,
|
||||
Filter::Pres(attr) => {
|
||||
// Given attr, is is present in the entry?
|
||||
e.pres(attr.as_str())
|
||||
e.attribute_pres(attr.as_str())
|
||||
}
|
||||
Filter::Or(_) => false,
|
||||
Filter::And(_) => false,
|
||||
|
@ -130,6 +128,7 @@ impl PartialOrd for Filter {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::schema::Schema;
|
||||
use super::Filter;
|
||||
use serde_json;
|
||||
use std::cmp::{Ordering, PartialOrd};
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
#![feature(try_from)]
|
||||
|
||||
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
#[macro_use]
|
||||
|
|
789
src/schema.rs
789
src/schema.rs
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@ use be::Backend;
|
|||
use entry::Entry;
|
||||
use event::{CreateEvent, EventResult, SearchEvent};
|
||||
use log::EventLog;
|
||||
use schema::Schema;
|
||||
|
||||
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
|
||||
let mut audit = AuditEvent::new();
|
||||
|
@ -12,12 +13,16 @@ pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::A
|
|||
// Create the BE connection
|
||||
// probably need a config type soon ....
|
||||
let be = Backend::new(&mut audit, path);
|
||||
let mut schema = Schema::new();
|
||||
schema.bootstrap_core();
|
||||
// now we clone it out in the startup I think
|
||||
// Should the be need a log clone ref? or pass it around?
|
||||
// it probably needs it ...
|
||||
audit.end_event("server_new");
|
||||
log.do_send(audit);
|
||||
SyncArbiter::start(threads, move || QueryServer::new(log.clone(), be.clone()))
|
||||
SyncArbiter::start(threads, move || {
|
||||
QueryServer::new(log.clone(), be.clone(), schema.clone())
|
||||
})
|
||||
}
|
||||
|
||||
// This is the core of the server. It implements all
|
||||
|
@ -35,12 +40,17 @@ pub struct QueryServer {
|
|||
// I think the BE is build, configured and cloned? Maybe Backend
|
||||
// is a wrapper type to Arc<BackendInner> or something.
|
||||
be: Backend,
|
||||
schema: Schema,
|
||||
}
|
||||
|
||||
impl QueryServer {
|
||||
pub fn new(log: actix::Addr<EventLog>, be: Backend) -> Self {
|
||||
pub fn new(log: actix::Addr<EventLog>, be: Backend, schema: Schema) -> Self {
|
||||
log_event!(log, "Starting query worker ...");
|
||||
QueryServer { log: log, be: be }
|
||||
QueryServer {
|
||||
log: log,
|
||||
be: be,
|
||||
schema: schema,
|
||||
}
|
||||
}
|
||||
|
||||
// Actually conduct a search request
|
||||
|
@ -59,6 +69,18 @@ impl QueryServer {
|
|||
pub fn create(&mut self, au: &mut AuditEvent, ce: &CreateEvent) -> Result<(), ()> {
|
||||
// Start a txn
|
||||
// Run any pre checks
|
||||
|
||||
let r = ce.entries.iter().fold(Ok(()), |acc, e| {
|
||||
if acc.is_ok() {
|
||||
self.schema.validate_entry(e).map_err(|_| ())
|
||||
} else {
|
||||
acc
|
||||
}
|
||||
});
|
||||
if r.is_err() {
|
||||
return r;
|
||||
}
|
||||
|
||||
// We may change from ce.entries later to something else?
|
||||
match self.be.create(au, &ce.entries) {
|
||||
Ok(_) => Ok(()),
|
||||
|
@ -146,6 +168,7 @@ mod tests {
|
|||
use super::super::event::{CreateEvent, SearchEvent};
|
||||
use super::super::filter::Filter;
|
||||
use super::super::log;
|
||||
use super::super::schema::Schema;
|
||||
use super::super::server::QueryServer;
|
||||
|
||||
macro_rules! run_test {
|
||||
|
@ -155,7 +178,9 @@ mod tests {
|
|||
let test_log = log::start();
|
||||
|
||||
let be = Backend::new(&mut audit, "");
|
||||
let test_server = QueryServer::new(test_log.clone(), be);
|
||||
let mut schema = Schema::new();
|
||||
schema.bootstrap_core();
|
||||
let test_server = QueryServer::new(test_log.clone(), be, schema);
|
||||
|
||||
// Could wrap another future here for the future::ok bit...
|
||||
let fut = $test_fn(test_log.clone(), test_server, &mut audit);
|
||||
|
@ -174,13 +199,21 @@ mod tests {
|
|||
#[test]
|
||||
fn test_be_create_user() {
|
||||
run_test!(|_log, mut server: QueryServer, audit: &mut AuditEvent| {
|
||||
let filt = Filter::Pres(String::from("userid"));
|
||||
let filt = Filter::Pres(String::from("name"));
|
||||
|
||||
let se1 = SearchEvent::new(filt.clone());
|
||||
let se2 = SearchEvent::new(filt);
|
||||
|
||||
let mut e: Entry = Entry::new();
|
||||
e.add_ava(String::from("userid"), String::from("william"))
|
||||
let e: Entry = serde_json::from_str(
|
||||
r#"{
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"name": ["testperson"],
|
||||
"description": ["testperson"],
|
||||
"displayname": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let expected = vec![e];
|
||||
|
@ -194,6 +227,7 @@ mod tests {
|
|||
assert!(cr.is_ok());
|
||||
|
||||
let r2 = server.search(audit, &se2).unwrap();
|
||||
println!("--> {:?}", r2);
|
||||
assert!(r2.len() == 1);
|
||||
|
||||
assert_eq!(r2, expected);
|
||||
|
|
Loading…
Reference in a new issue