2018-11-11 01:39:11 +01:00
|
|
|
// use serde_json::{Error, Value};
|
2018-11-27 11:48:21 +01:00
|
|
|
use super::proto_v1::Entry as ProtoEntry;
|
2018-12-30 03:17:09 +01:00
|
|
|
use filter::Filter;
|
2018-11-15 04:49:08 +01:00
|
|
|
use std::collections::btree_map::{Iter as BTreeIter, IterMut as BTreeIterMut};
|
2018-10-03 13:21:21 +02:00
|
|
|
use std::collections::BTreeMap;
|
2018-11-13 08:14:26 +01:00
|
|
|
use std::slice::Iter as SliceIter;
|
2018-09-29 09:54:16 +02:00
|
|
|
|
|
|
|
// make a trait entry for everything to adhere to?
|
|
|
|
// * How to get indexs out?
|
|
|
|
// * How to track pending diffs?
|
|
|
|
|
2018-10-03 13:21:21 +02:00
|
|
|
// Entry is really similar to serde Value, but limits the possibility
|
|
|
|
// of what certain types could be.
|
|
|
|
//
|
|
|
|
// The idea of an entry is that we have
|
|
|
|
// an entry that looks like:
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// 'class': ['object', ...],
|
|
|
|
// 'attr': ['value', ...],
|
|
|
|
// 'attr': ['value', ...],
|
|
|
|
// ...
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// When we send this as a result to clients, we could embed other objects as:
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// 'attr': [
|
|
|
|
// 'value': {
|
|
|
|
// },
|
|
|
|
// ],
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
2018-11-13 08:14:26 +01:00
|
|
|
pub struct EntryClasses<'a> {
|
|
|
|
inner: Option<SliceIter<'a, String>>,
|
|
|
|
// _p: &'a PhantomData<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for EntryClasses<'a> {
|
|
|
|
type Item = &'a String;
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<(&'a String)> {
|
|
|
|
match self.inner.iter_mut().next() {
|
|
|
|
Some(i) => i.next(),
|
|
|
|
None => None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
match self.inner.iter().next() {
|
|
|
|
Some(i) => i.size_hint(),
|
|
|
|
None => (0, None),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct EntryAvas<'a> {
|
|
|
|
inner: BTreeIter<'a, String, Vec<String>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for EntryAvas<'a> {
|
|
|
|
type Item = (&'a String, &'a Vec<String>);
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<(&'a String, &'a Vec<String>)> {
|
|
|
|
self.inner.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.inner.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-15 04:49:08 +01:00
|
|
|
pub struct EntryAvasMut<'a> {
|
|
|
|
inner: BTreeIterMut<'a, String, Vec<String>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for EntryAvasMut<'a> {
|
|
|
|
type Item = (&'a String, &'a mut Vec<String>);
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn next(&mut self) -> Option<(&'a String, &'a mut Vec<String>)> {
|
|
|
|
self.inner.next()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
|
|
self.inner.size_hint()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-22 02:39:56 +01:00
|
|
|
// This is a BE concept, so move it there!
|
2018-09-29 09:54:16 +02:00
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
pub struct Entry {
|
2019-01-22 02:39:56 +01:00
|
|
|
pub id: Option<i64>,
|
2018-10-03 13:21:21 +02:00
|
|
|
attrs: BTreeMap<String, Vec<String>>,
|
|
|
|
}
|
2018-09-29 09:54:16 +02:00
|
|
|
|
2018-10-03 13:21:21 +02:00
|
|
|
impl Entry {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Entry {
|
2019-01-22 02:39:56 +01:00
|
|
|
// This means NEVER COMMITED
|
|
|
|
id: None,
|
2018-11-07 07:35:25 +01:00
|
|
|
attrs: BTreeMap::new(),
|
2018-10-03 13:21:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This should always work? It's only on validate that we'll build
|
|
|
|
// a list of syntax violations ...
|
2018-11-14 02:54:59 +01:00
|
|
|
// 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) {
|
2018-10-03 13:21:21 +02:00
|
|
|
// get_mut to access value
|
2018-11-14 02:54:59 +01:00
|
|
|
// How do we make this turn into an ok / err?
|
2018-11-07 07:35:25 +01:00
|
|
|
self.attrs
|
|
|
|
.entry(attr)
|
2018-11-14 02:54:59 +01:00
|
|
|
.and_modify(|v| {
|
|
|
|
// Here we need to actually do a check/binary search ...
|
2018-11-20 07:51:10 +01:00
|
|
|
// FIXME: Because map_err is lazy, this won't do anything on release
|
|
|
|
match v.binary_search(&value) {
|
|
|
|
Ok(_) => {}
|
|
|
|
Err(idx) => {
|
|
|
|
// This cloning is to fix a borrow issue with the or_insert below.
|
|
|
|
// Is there a better way?
|
|
|
|
v.insert(idx, value.clone())
|
|
|
|
}
|
|
|
|
}
|
2018-11-14 02:54:59 +01:00
|
|
|
})
|
2018-11-07 07:35:25 +01:00
|
|
|
.or_insert(vec![value]);
|
2018-10-03 13:21:21 +02:00
|
|
|
}
|
|
|
|
|
2018-11-15 04:49:08 +01:00
|
|
|
// FIXME: Should this collect from iter instead?
|
2018-12-29 03:29:10 +01:00
|
|
|
/// Overwrite the existing avas.
|
|
|
|
pub fn set_avas(&mut self, attr: String, values: Vec<String>) {
|
2018-11-15 04:49:08 +01:00
|
|
|
// Overwrite the existing value
|
|
|
|
let _ = self.attrs.insert(attr, values);
|
|
|
|
}
|
|
|
|
|
2018-11-13 08:14:26 +01:00
|
|
|
pub fn get_ava(&self, attr: &String) -> Option<&Vec<String>> {
|
|
|
|
self.attrs.get(attr)
|
|
|
|
}
|
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
pub fn attribute_pres(&self, attr: &str) -> bool {
|
|
|
|
// FIXME: Do we need to normalise attr name?
|
|
|
|
self.attrs.contains_key(attr)
|
2018-10-03 13:21:21 +02:00
|
|
|
}
|
2018-11-07 07:54:02 +01:00
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
pub fn attribute_equality(&self, attr: &str, value: &str) -> bool {
|
2018-12-29 10:56:03 +01:00
|
|
|
// we assume based on schema normalisation on the way in
|
2018-11-14 02:54:59 +01:00
|
|
|
// that the equality here of the raw values MUST be correct.
|
2018-12-29 10:56:03 +01:00
|
|
|
// We also normalise filters, to ensure that their values are
|
|
|
|
// syntax valid and will correctly match here with our indexes.
|
2018-11-14 02:54:59 +01:00
|
|
|
|
|
|
|
// 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 })
|
|
|
|
})
|
2018-11-07 07:54:02 +01:00
|
|
|
}
|
2018-11-13 08:14:26 +01:00
|
|
|
|
2018-12-30 03:17:09 +01:00
|
|
|
pub fn attribute_substring(&self, _attr: &str, _subvalue: &str) -> bool {
|
2018-12-29 10:56:03 +01:00
|
|
|
unimplemented!();
|
|
|
|
}
|
|
|
|
|
2018-11-13 08:14:26 +01:00
|
|
|
pub fn classes(&self) -> EntryClasses {
|
|
|
|
// Get the class vec, if any?
|
|
|
|
// How do we indicate "empty?"
|
|
|
|
// FIXME: Actually handle this error ...
|
|
|
|
let c = self.attrs.get("class").map(|c| c.iter());
|
|
|
|
EntryClasses { inner: c }
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn avas(&self) -> EntryAvas {
|
|
|
|
EntryAvas {
|
|
|
|
inner: self.attrs.iter(),
|
|
|
|
}
|
|
|
|
}
|
2018-11-15 04:49:08 +01:00
|
|
|
|
|
|
|
pub fn avas_mut(&mut self) -> EntryAvasMut {
|
|
|
|
EntryAvasMut {
|
|
|
|
inner: self.attrs.iter_mut(),
|
|
|
|
}
|
|
|
|
}
|
2018-11-27 11:48:21 +01:00
|
|
|
|
2019-01-20 00:51:22 +01:00
|
|
|
pub fn filter_from_attrs(&self, attrs: &Vec<String>) -> Option<Filter> {
|
|
|
|
// Generate a filter from the attributes requested and defined.
|
|
|
|
// Basically, this is a series of nested and's (which will be
|
|
|
|
// optimised down later: but if someone wants to solve flatten() ...)
|
|
|
|
|
|
|
|
// Take name: (a, b), name: (c, d) -> (name, a), (name, b), (name, c), (name, d)
|
|
|
|
|
|
|
|
let mut pairs: Vec<(String, String)> = Vec::new();
|
|
|
|
|
|
|
|
for attr in attrs {
|
|
|
|
match self.attrs.get(attr) {
|
|
|
|
Some(values) => {
|
|
|
|
for v in values {
|
2019-01-20 01:46:17 +01:00
|
|
|
pairs.push((attr.clone(), v.clone()))
|
2019-01-20 00:51:22 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-20 01:46:17 +01:00
|
|
|
None => return None,
|
2019-01-20 00:51:22 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now make this a filter?
|
|
|
|
|
2019-01-20 01:46:17 +01:00
|
|
|
let eq_filters = pairs
|
|
|
|
.into_iter()
|
|
|
|
.map(|(attr, value)| Filter::Eq(attr, value))
|
2019-01-20 00:51:22 +01:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
Some(Filter::And(eq_filters))
|
2018-12-30 03:17:09 +01:00
|
|
|
}
|
|
|
|
|
2018-11-27 11:48:21 +01:00
|
|
|
// FIXME: Can we consume protoentry?
|
|
|
|
pub fn from(e: &ProtoEntry) -> Self {
|
|
|
|
// Why not the trait? In the future we may want to extend
|
|
|
|
// this with server aware functions for changes of the
|
|
|
|
// incoming data.
|
|
|
|
Entry {
|
2018-12-29 13:05:54 +01:00
|
|
|
// For now, we do a straight move, and we sort the incoming data
|
|
|
|
// sets so that BST works.
|
2019-01-22 02:39:56 +01:00
|
|
|
id: None,
|
2018-12-29 13:06:15 +01:00
|
|
|
attrs: e
|
|
|
|
.attrs
|
|
|
|
.iter()
|
2018-12-29 13:05:54 +01:00
|
|
|
.map(|(k, v)| {
|
|
|
|
let mut nv = v.clone();
|
|
|
|
nv.sort_unstable();
|
2018-12-29 13:06:15 +01:00
|
|
|
(k.clone(), nv)
|
|
|
|
})
|
|
|
|
.collect(),
|
2018-11-27 11:48:21 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn into(&self) -> ProtoEntry {
|
|
|
|
// It's very likely that at this stage we'll need to apply
|
|
|
|
// access controls, dynamic attributes or more.
|
|
|
|
// As a result, this may not even be the right place
|
|
|
|
// for the conversion as algorithmically it may be
|
|
|
|
// better to do this from the outside view. This can
|
|
|
|
// of course be identified and changed ...
|
|
|
|
ProtoEntry {
|
|
|
|
attrs: self.attrs.clone(),
|
|
|
|
}
|
|
|
|
}
|
2018-09-29 09:54:16 +02:00
|
|
|
}
|
|
|
|
|
2018-11-11 01:39:11 +01:00
|
|
|
impl Clone for Entry {
|
|
|
|
fn clone(&self) -> Entry {
|
|
|
|
Entry {
|
2019-01-22 02:39:56 +01:00
|
|
|
id: self.id,
|
2018-11-11 01:39:11 +01:00
|
|
|
attrs: self.attrs.clone(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PartialEq for Entry {
|
|
|
|
fn eq(&self, rhs: &Entry) -> bool {
|
|
|
|
// FIXME: This is naive. Later it should be schema
|
|
|
|
// aware checking.
|
|
|
|
self.attrs == rhs.attrs
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-29 09:54:16 +02:00
|
|
|
// pub trait Entry {
|
2018-10-03 13:21:21 +02:00
|
|
|
//fn to_json_str(&self) -> String;
|
|
|
|
// fn to_index_diff -> ???
|
|
|
|
// from_json_str() -> Self;
|
|
|
|
//
|
|
|
|
// Does this match a filter or not?a
|
|
|
|
// fn apply_filter -> Result<bool, ()>
|
2018-09-29 09:54:16 +02:00
|
|
|
// }
|
|
|
|
|
|
|
|
//enum Credential {
|
2018-10-03 13:21:21 +02:00
|
|
|
//?
|
2018-09-29 09:54:16 +02:00
|
|
|
//}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
enum Credential {
|
|
|
|
Password {
|
|
|
|
name: String,
|
|
|
|
hash: String,
|
|
|
|
},
|
|
|
|
TOTPPassword {
|
|
|
|
name: String,
|
|
|
|
hash: String,
|
|
|
|
totp_secret: String,
|
|
|
|
},
|
|
|
|
SshPublicKey {
|
|
|
|
name: String,
|
|
|
|
data: String,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Serialize, Deserialize, Debug)]
|
|
|
|
struct User {
|
|
|
|
username: String,
|
|
|
|
// Could this be derived from self? Do we even need schema?
|
|
|
|
class: Vec<String>,
|
|
|
|
displayname: String,
|
|
|
|
legalname: Option<String>,
|
|
|
|
email: Vec<String>,
|
|
|
|
// uuid?
|
|
|
|
// need to support deref later ...
|
|
|
|
memberof: Vec<String>,
|
|
|
|
sshpublickey: Vec<String>,
|
|
|
|
|
|
|
|
credentials: Vec<Credential>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2018-11-07 07:35:25 +01:00
|
|
|
use super::{Entry, User};
|
2018-09-29 09:54:16 +02:00
|
|
|
use serde_json;
|
|
|
|
|
2018-10-03 13:21:21 +02:00
|
|
|
#[test]
|
|
|
|
fn test_entry_basic() {
|
|
|
|
let mut e: Entry = Entry::new();
|
2018-09-29 09:54:16 +02:00
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
e.add_ava(String::from("userid"), String::from("william"));
|
2018-10-03 13:21:21 +02:00
|
|
|
|
2018-11-20 07:51:10 +01:00
|
|
|
let _d = serde_json::to_string_pretty(&e).unwrap();
|
2018-11-14 02:54:59 +01:00
|
|
|
}
|
2018-10-03 13:21:21 +02:00
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
#[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)
|
2018-10-03 13:21:21 +02:00
|
|
|
}
|
2018-11-07 07:54:02 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_entry_pres() {
|
|
|
|
let mut e: Entry = Entry::new();
|
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
e.add_ava(String::from("userid"), String::from("william"));
|
|
|
|
|
|
|
|
assert!(e.attribute_pres("userid"));
|
|
|
|
assert!(!e.attribute_pres("name"));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_entry_equality() {
|
|
|
|
let mut e: Entry = Entry::new();
|
2018-11-07 07:54:02 +01:00
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
e.add_ava(String::from("userid"), String::from("william"));
|
2018-11-07 07:54:02 +01:00
|
|
|
|
2018-11-14 02:54:59 +01:00
|
|
|
assert!(e.attribute_equality("userid", "william"));
|
|
|
|
assert!(!e.attribute_equality("userid", "test"));
|
|
|
|
assert!(!e.attribute_equality("nonexist", "william"));
|
2018-11-07 07:54:02 +01:00
|
|
|
}
|
2019-01-21 03:08:56 +01:00
|
|
|
|
2018-10-03 13:21:21 +02:00
|
|
|
}
|