mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 20:47:01 +01:00
Working uuid test case for simple create case
This commit is contained in:
parent
6bd5b8856b
commit
e26081dad5
|
@ -1,4 +1,3 @@
|
|||
|
||||
extern crate rsidm;
|
||||
|
||||
use rsidm::proto_v1;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
// use actix::SystemRunner;
|
||||
use actix_web::middleware::session::{self, RequestSession};
|
||||
use actix_web::{
|
||||
error, http, middleware, App, AsyncResponder, Error, FutureResponse, HttpMessage, HttpRequest,
|
||||
HttpResponse, Path, State, Result,
|
||||
HttpResponse, Path, Result, State,
|
||||
};
|
||||
use actix_web::middleware::session::{self, RequestSession};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures::{future, Future, Stream};
|
||||
|
@ -11,8 +11,7 @@ use futures::{future, Future, Stream};
|
|||
use super::config::Configuration;
|
||||
|
||||
// SearchResult
|
||||
use super::event::{CreateEvent, SearchEvent,
|
||||
};
|
||||
use super::event::{CreateEvent, SearchEvent};
|
||||
use super::filter::Filter;
|
||||
use super::log;
|
||||
use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse};
|
||||
|
@ -151,7 +150,6 @@ fn whoami(req: &HttpRequest<AppState>) -> Result<&'static str> {
|
|||
Ok("welcome!")
|
||||
}
|
||||
|
||||
|
||||
pub fn create_server_core(config: Configuration) {
|
||||
// Configure the middleware logger
|
||||
::std::env::set_var("RUST_LOG", "actix_web=info");
|
||||
|
@ -187,7 +185,7 @@ pub fn create_server_core(config: Configuration) {
|
|||
.http_only(true)
|
||||
.name("rsidm-session")
|
||||
// This forces https only
|
||||
.secure(false)
|
||||
.secure(false),
|
||||
))
|
||||
.resource("/", |r| r.f(index))
|
||||
// curl --header ...?
|
||||
|
|
|
@ -130,7 +130,8 @@ impl Entry {
|
|||
}
|
||||
|
||||
// FIXME: Should this collect from iter instead?
|
||||
pub fn add_avas(&mut self, attr: String, values: Vec<String>) {
|
||||
/// Overwrite the existing avas.
|
||||
pub fn set_avas(&mut self, attr: String, values: Vec<String>) {
|
||||
// Overwrite the existing value
|
||||
let _ = self.attrs.insert(attr, values);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ pub enum SchemaError {
|
|||
NotImplemented,
|
||||
InvalidClass,
|
||||
// FIXME: Is there a way to say what we are missing on error?
|
||||
// Yes, add a string on the enum.
|
||||
MissingMustAttribute,
|
||||
InvalidAttribute,
|
||||
InvalidAttributeSyntax,
|
||||
|
@ -14,4 +15,5 @@ pub enum OperationError {
|
|||
EmptyRequest,
|
||||
Backend,
|
||||
SchemaViolation,
|
||||
Plugin,
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ impl SearchResult {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct SearchEvent {
|
||||
pub internal: bool,
|
||||
pub filter: Filter,
|
||||
class: (), // String
|
||||
}
|
||||
|
@ -62,20 +63,24 @@ impl Message for SearchEvent {
|
|||
impl SearchEvent {
|
||||
pub fn from_request(request: SearchRequest) -> Self {
|
||||
SearchEvent {
|
||||
internal: false,
|
||||
filter: request.filter,
|
||||
class: (),
|
||||
}
|
||||
}
|
||||
// We need event -> some kind of json event string for logging
|
||||
// Could we turn the event from json back to an event for testing later?
|
||||
}
|
||||
|
||||
// Represents the decoded entries from the protocol -> internal entry representation
|
||||
// including information about the identity performing the request, and if the
|
||||
// request is internal or not.
|
||||
#[derive(Debug)]
|
||||
pub struct CreateEvent {
|
||||
// This may still actually change to handle the *raw* nature of the
|
||||
// input that we plan to parse.
|
||||
pub entries: Vec<Entry>,
|
||||
// It could be better to box this later ...
|
||||
/// Is the CreateEvent from an internal or external source?
|
||||
/// This may affect which plugins are run ...
|
||||
pub internal: bool,
|
||||
}
|
||||
|
||||
impl Message for CreateEvent {
|
||||
|
@ -89,11 +94,16 @@ impl CreateEvent {
|
|||
// From ProtoEntry -> Entry
|
||||
// What is the correct consuming iterator here? Can we
|
||||
// even do that?
|
||||
internal: false,
|
||||
entries: request.entries.iter().map(|e| Entry::from(e)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
// Is this an internal only function?
|
||||
pub fn from_vec(entries: Vec<Entry>) -> Self {
|
||||
CreateEvent { entries: entries }
|
||||
CreateEvent {
|
||||
internal: false,
|
||||
entries: entries,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
2
src/lib/identity.rs
Normal file
2
src/lib/identity.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
// Contains a structure representing the current authenticated
|
||||
// identity (or anonymous, or admin, both of which are in mem).
|
|
@ -31,9 +31,10 @@ mod audit;
|
|||
mod be;
|
||||
mod entry;
|
||||
mod event;
|
||||
mod identity;
|
||||
mod plugins;
|
||||
mod schema;
|
||||
mod server;
|
||||
mod plugins;
|
||||
|
||||
pub mod config;
|
||||
pub mod core;
|
||||
|
|
|
@ -1,3 +1,49 @@
|
|||
use audit::AuditScope;
|
||||
use be::Backend;
|
||||
use entry::Entry;
|
||||
use event::CreateEvent;
|
||||
use schema::Schema;
|
||||
use error::OperationError;
|
||||
|
||||
trait Plugin {
|
||||
fn pre_create(
|
||||
be: &mut Backend,
|
||||
au: &mut AuditScope,
|
||||
cand: &mut Vec<Entry>,
|
||||
ce: &CreateEvent,
|
||||
schema: &Schema,
|
||||
) -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_create() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_modify() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_modify() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_delete() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_delete() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn pre_search() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_search() -> Result<(), OperationError> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod uuid;
|
||||
|
||||
|
@ -5,4 +51,3 @@ mod uuid;
|
|||
|
||||
// How do we deal with plugin activation? Config?
|
||||
// What do plugins default to?
|
||||
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
use plugins::Plugin;
|
||||
use uuid::Uuid;
|
||||
|
||||
use audit::AuditScope;
|
||||
use be::Backend;
|
||||
use entry::Entry;
|
||||
use event::CreateEvent;
|
||||
use schema::Schema;
|
||||
use error::OperationError;
|
||||
|
||||
struct UUID {}
|
||||
|
||||
impl Plugin for UUID {
|
||||
// Need to be given the backend(for testing ease)
|
||||
// audit
|
||||
// the mut set of entries to create
|
||||
// the create event itself (immutable, for checking originals)
|
||||
// contains who is creating them
|
||||
// the schema of the running instance
|
||||
|
||||
fn pre_create(
|
||||
be: &mut Backend,
|
||||
au: &mut AuditScope,
|
||||
cand: &mut Vec<Entry>,
|
||||
ce: &CreateEvent,
|
||||
schema: &Schema,
|
||||
) -> Result<(), OperationError> {
|
||||
// For each candidate
|
||||
for entry in cand.iter_mut() {
|
||||
let name_uuid = String::from("uuid");
|
||||
|
||||
// if they don't have uuid, create it.
|
||||
// TODO: get_ava should have a str version for effeciency?
|
||||
let mut c_uuid = match entry.get_ava(&name_uuid) {
|
||||
Some(u) => {
|
||||
// Actually check we have a value, could be empty array ...
|
||||
let v = u.first().unwrap();
|
||||
// This could actually fail, so we probably need to handle
|
||||
// this better ....
|
||||
match Uuid::parse_str(v.as_str()) {
|
||||
Ok(up) => up,
|
||||
Err(_) => {
|
||||
return Err(
|
||||
OperationError::Plugin
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Uuid::new_v4()
|
||||
};
|
||||
|
||||
// Make it a string, so we can filter.
|
||||
println!("uuid: {}", c_uuid);
|
||||
|
||||
|
||||
// check that the uuid is unique in the be (even if one is provided
|
||||
// we especially need to check that)
|
||||
|
||||
// if not unique, generate another, and try again.
|
||||
|
||||
// If it's okay, now put it into the entry.
|
||||
// we may need to inject the base OC required for all objects in our
|
||||
// server to support this?
|
||||
let str_uuid = format!("{}", c_uuid);
|
||||
let ava_uuid: Vec<String> = vec![str_uuid];
|
||||
entry.set_avas(name_uuid, ava_uuid);
|
||||
}
|
||||
// done!
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::Plugin;
|
||||
use super::UUID;
|
||||
|
||||
use audit::AuditScope;
|
||||
use be::Backend;
|
||||
use entry::Entry;
|
||||
use event::CreateEvent;
|
||||
use schema::Schema;
|
||||
|
||||
macro_rules! run_pre_create_test {
|
||||
(
|
||||
$preload_entries:ident,
|
||||
$create_entries:ident,
|
||||
$ident:ident,
|
||||
$internal:ident,
|
||||
$test_fn:expr
|
||||
) => {{
|
||||
let mut au = AuditScope::new("run_pre_create_test");
|
||||
audit_segment!(au, || {
|
||||
// Create an in memory BE
|
||||
let mut be = Backend::new(&mut au, "");
|
||||
|
||||
// TODO: Preload entries here!
|
||||
|
||||
let ce = CreateEvent::from_vec($create_entries.clone());
|
||||
let mut schema = Schema::new();
|
||||
schema.bootstrap_core();
|
||||
|
||||
let mut au_test = AuditScope::new("pre_create_test");
|
||||
audit_segment!(au_test, || $test_fn(
|
||||
&mut be,
|
||||
&mut au_test,
|
||||
&mut $create_entries,
|
||||
&ce,
|
||||
&schema,
|
||||
));
|
||||
|
||||
au.append_scope(au_test);
|
||||
});
|
||||
// Dump the raw audit. Perhaps we should serialise this pretty?
|
||||
println!("{:?}", au);
|
||||
}};
|
||||
}
|
||||
|
||||
// Check empty create
|
||||
#[test]
|
||||
fn test_pre_create_empty() {
|
||||
// Need a macro to create all the bits here ...
|
||||
// Macro needs preload entries, the create entries
|
||||
// schema, identity for create event (later)
|
||||
let preload: Vec<Entry> = Vec::new();
|
||||
let mut create: Vec<Entry> = Vec::new();
|
||||
run_pre_create_test!(
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
false,
|
||||
|be: &mut Backend,
|
||||
au: &mut AuditScope,
|
||||
cand: &mut Vec<Entry>,
|
||||
ce: &CreateEvent,
|
||||
schema: &Schema| {
|
||||
let r = UUID::pre_create(be, au, cand, ce, schema);
|
||||
assert!(r.is_ok());
|
||||
// Nothing should have changed.
|
||||
assert!(cand.len() == 0);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// check create where no uuid
|
||||
#[test]
|
||||
fn test_pre_create_no_uuid() {
|
||||
// Need a macro to create all the bits here ...
|
||||
// Macro needs preload entries, the create entries
|
||||
// schema, identity for create event (later)
|
||||
let preload: Vec<Entry> = Vec::new();
|
||||
|
||||
let e: Entry = serde_json::from_str(
|
||||
r#"{
|
||||
"attrs": {
|
||||
"class": ["person"],
|
||||
"name": ["testperson"],
|
||||
"description": ["testperson"],
|
||||
"displayname": ["testperson"]
|
||||
}
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut create = vec![e];
|
||||
|
||||
run_pre_create_test!(
|
||||
preload,
|
||||
create,
|
||||
false,
|
||||
false,
|
||||
|be: &mut Backend,
|
||||
au: &mut AuditScope,
|
||||
cand: &mut Vec<Entry>,
|
||||
ce: &CreateEvent,
|
||||
schema: &Schema| {
|
||||
let r = UUID::pre_create(be, au, cand, ce, schema);
|
||||
assert!(r.is_ok());
|
||||
// Assert that the entry contains the attr "uuid" now.
|
||||
let ue = cand.first().unwrap();
|
||||
assert!(ue.attribute_pres("uuid"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// check unparseable uuid
|
||||
// check entry where uuid is empty list
|
||||
|
||||
// check create where provided uuid is valid. It should be unchanged.
|
||||
|
||||
// check create where uuid already exists.
|
||||
|
||||
// check create where uuid is a well-known
|
||||
// WARNING: This actually requires me to implement backend migrations and
|
||||
// creation of default objects in the DB on new() if they don't exist, and
|
||||
// to potentially support migrations of said objects.
|
||||
}
|
|
@ -749,7 +749,7 @@ impl Schema {
|
|||
None => avas.clone(),
|
||||
};
|
||||
// now push those to the new entry.
|
||||
entry_new.add_avas(attr_name_normal, avas_normal);
|
||||
entry_new.set_avas(attr_name_normal, avas_normal);
|
||||
}
|
||||
// Done!
|
||||
entry_new
|
||||
|
|
|
@ -3,14 +3,13 @@ use actix::prelude::*;
|
|||
use audit::AuditScope;
|
||||
use be::{Backend, BackendError};
|
||||
|
||||
use plugins;
|
||||
use entry::Entry;
|
||||
use error::OperationError;
|
||||
use event::{CreateEvent, OpResult, SearchEvent, SearchResult};
|
||||
use log::EventLog;
|
||||
use plugins;
|
||||
use schema::Schema;
|
||||
|
||||
|
||||
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
|
||||
let mut audit = AuditScope::new("server_start");
|
||||
audit_segment!(audit, || {
|
||||
|
@ -83,13 +82,22 @@ impl QueryServer {
|
|||
res
|
||||
}
|
||||
|
||||
// What should this take?
|
||||
// This should probably take raw encoded entries? Or sohuld they
|
||||
// be handled by fe?
|
||||
pub fn create(&mut self, au: &mut AuditScope, ce: &CreateEvent) -> Result<(), OperationError> {
|
||||
// The create event is a raw, read only representation of the request
|
||||
// that was made to us, including information about the identity
|
||||
// performing the request.
|
||||
|
||||
// Log the request
|
||||
|
||||
// TODO: Do we need limits on number of creates, or do we constraint
|
||||
// based on request size in the frontend?
|
||||
|
||||
// Copy the entries to a writeable form.
|
||||
let mut candidates: Vec<_> = ce.entries.iter().collect();
|
||||
|
||||
// Start a txn
|
||||
|
||||
// run any pre plugins
|
||||
// run any pre plugins, giving them the list of mutable candidates.
|
||||
|
||||
// Run any pre checks
|
||||
// FIXME: Normalise all entries incoming
|
||||
|
@ -117,10 +125,13 @@ impl QueryServer {
|
|||
BackendError::EmptyRequest => OperationError::EmptyRequest,
|
||||
_ => OperationError::Backend,
|
||||
});
|
||||
au.append_scope(audit_be);
|
||||
|
||||
// Run any post plugins
|
||||
|
||||
// Commit/Abort the txn
|
||||
|
||||
// We are complete, finalise logging and return
|
||||
au.append_scope(audit_be);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue