kanidm/src/lib/entry.rs

364 lines
9.7 KiB
Rust
Raw Normal View History

// use serde_json::{Error, Value};
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};
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?
// 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()
}
}
2018-09-29 09:54:16 +02:00
#[derive(Serialize, Deserialize, Debug)]
pub struct Entry {
attrs: BTreeMap<String, Vec<String>>,
}
2018-09-29 09:54:16 +02:00
impl Entry {
pub fn new() -> Self {
Entry {
2018-11-07 07:35:25 +01:00
attrs: BTreeMap::new(),
}
}
// 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) {
// 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-11-15 04:49:08 +01:00
// FIXME: Should this collect from iter instead?
/// 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-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(),
}
}
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
}
// 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.
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(),
}
}
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
}
impl Clone for Entry {
fn clone(&self) -> Entry {
Entry {
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 {
//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-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;
#[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-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-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-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
}
}