kanidm/src/entry.rs

338 lines
8.7 KiB
Rust
Raw Normal View History

// use serde_json::{Error, Value};
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?
pub fn add_avas(&mut self, attr: String, values: Vec<String>) {
// 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 {
// 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 })
})
2018-11-07 07:54:02 +01:00
}
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-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>,
}
impl User {
pub fn new(username: &str, displayname: &str) -> Self {
// Build a blank value
User {
username: String::from(username),
class: Vec::new(),
displayname: String::from(displayname),
legalname: None,
email: Vec::new(),
memberof: Vec::new(),
sshpublickey: Vec::new(),
credentials: Vec::new(),
}
}
// We need a way to "diff" two User objects
// as on a modification we want to track the set of changes
// that is occuring -- needed for indexing to function.
// Basically we just need to check if it changed, remove
// the "former" and add the "newer" value.
// We have to sort vecs ...
// Is there a way to call this on serialise?
2018-11-13 08:14:26 +01:00
fn validate(&self) -> Result<(), ()> {
// Given a schema, validate our object is sane.
Ok(())
2018-09-29 09:54:16 +02:00
}
}
#[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_user_basic() {
let u: User = User::new("william", "William Brown");
let d = serde_json::to_string_pretty(&u).unwrap();
2018-09-29 09:54:16 +02:00
2018-11-20 07:51:10 +01:00
let _u2: User = serde_json::from_str(d.as_str()).unwrap();
2018-09-29 09:54:16 +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-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
}
}