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;
|
extern crate rsidm;
|
||||||
|
|
||||||
use rsidm::proto_v1;
|
use rsidm::proto_v1;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// use actix::SystemRunner;
|
// use actix::SystemRunner;
|
||||||
|
use actix_web::middleware::session::{self, RequestSession};
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
error, http, middleware, App, AsyncResponder, Error, FutureResponse, HttpMessage, HttpRequest,
|
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 bytes::BytesMut;
|
||||||
use futures::{future, Future, Stream};
|
use futures::{future, Future, Stream};
|
||||||
|
@ -11,8 +11,7 @@ use futures::{future, Future, Stream};
|
||||||
use super::config::Configuration;
|
use super::config::Configuration;
|
||||||
|
|
||||||
// SearchResult
|
// SearchResult
|
||||||
use super::event::{CreateEvent, SearchEvent,
|
use super::event::{CreateEvent, SearchEvent};
|
||||||
};
|
|
||||||
use super::filter::Filter;
|
use super::filter::Filter;
|
||||||
use super::log;
|
use super::log;
|
||||||
use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse};
|
use super::proto_v1::{CreateRequest, Response, SearchRequest, SearchResponse};
|
||||||
|
@ -151,7 +150,6 @@ fn whoami(req: &HttpRequest<AppState>) -> Result<&'static str> {
|
||||||
Ok("welcome!")
|
Ok("welcome!")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn create_server_core(config: Configuration) {
|
pub fn create_server_core(config: Configuration) {
|
||||||
// Configure the middleware logger
|
// Configure the middleware logger
|
||||||
::std::env::set_var("RUST_LOG", "actix_web=info");
|
::std::env::set_var("RUST_LOG", "actix_web=info");
|
||||||
|
@ -179,15 +177,15 @@ pub fn create_server_core(config: Configuration) {
|
||||||
// Signed prevents tampering. this 32 byte key MUST
|
// Signed prevents tampering. this 32 byte key MUST
|
||||||
// be generated (probably stored in DB for cross-host access)
|
// be generated (probably stored in DB for cross-host access)
|
||||||
session::CookieSessionBackend::signed(&[0; 32])
|
session::CookieSessionBackend::signed(&[0; 32])
|
||||||
.path("/")
|
.path("/")
|
||||||
//.max_age() duration of the token life
|
//.max_age() duration of the token life
|
||||||
// .domain()
|
// .domain()
|
||||||
//.same_site() constraunt to the domain
|
//.same_site() constraunt to the domain
|
||||||
// Disallow from js
|
// Disallow from js
|
||||||
.http_only(true)
|
.http_only(true)
|
||||||
.name("rsidm-session")
|
.name("rsidm-session")
|
||||||
// This forces https only
|
// This forces https only
|
||||||
.secure(false)
|
.secure(false),
|
||||||
))
|
))
|
||||||
.resource("/", |r| r.f(index))
|
.resource("/", |r| r.f(index))
|
||||||
// curl --header ...?
|
// curl --header ...?
|
||||||
|
|
|
@ -130,7 +130,8 @@ impl Entry {
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Should this collect from iter instead?
|
// 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
|
// Overwrite the existing value
|
||||||
let _ = self.attrs.insert(attr, values);
|
let _ = self.attrs.insert(attr, values);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ pub enum SchemaError {
|
||||||
NotImplemented,
|
NotImplemented,
|
||||||
InvalidClass,
|
InvalidClass,
|
||||||
// FIXME: Is there a way to say what we are missing on error?
|
// FIXME: Is there a way to say what we are missing on error?
|
||||||
|
// Yes, add a string on the enum.
|
||||||
MissingMustAttribute,
|
MissingMustAttribute,
|
||||||
InvalidAttribute,
|
InvalidAttribute,
|
||||||
InvalidAttributeSyntax,
|
InvalidAttributeSyntax,
|
||||||
|
@ -14,4 +15,5 @@ pub enum OperationError {
|
||||||
EmptyRequest,
|
EmptyRequest,
|
||||||
Backend,
|
Backend,
|
||||||
SchemaViolation,
|
SchemaViolation,
|
||||||
|
Plugin,
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ impl SearchResult {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SearchEvent {
|
pub struct SearchEvent {
|
||||||
|
pub internal: bool,
|
||||||
pub filter: Filter,
|
pub filter: Filter,
|
||||||
class: (), // String
|
class: (), // String
|
||||||
}
|
}
|
||||||
|
@ -62,20 +63,24 @@ impl Message for SearchEvent {
|
||||||
impl SearchEvent {
|
impl SearchEvent {
|
||||||
pub fn from_request(request: SearchRequest) -> Self {
|
pub fn from_request(request: SearchRequest) -> Self {
|
||||||
SearchEvent {
|
SearchEvent {
|
||||||
|
internal: false,
|
||||||
filter: request.filter,
|
filter: request.filter,
|
||||||
class: (),
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct CreateEvent {
|
pub struct CreateEvent {
|
||||||
// This may still actually change to handle the *raw* nature of the
|
// This may still actually change to handle the *raw* nature of the
|
||||||
// input that we plan to parse.
|
// input that we plan to parse.
|
||||||
pub entries: Vec<Entry>,
|
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 {
|
impl Message for CreateEvent {
|
||||||
|
@ -89,11 +94,16 @@ impl CreateEvent {
|
||||||
// From ProtoEntry -> Entry
|
// From ProtoEntry -> Entry
|
||||||
// What is the correct consuming iterator here? Can we
|
// What is the correct consuming iterator here? Can we
|
||||||
// even do that?
|
// even do that?
|
||||||
|
internal: false,
|
||||||
entries: request.entries.iter().map(|e| Entry::from(e)).collect(),
|
entries: request.entries.iter().map(|e| Entry::from(e)).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Is this an internal only function?
|
||||||
pub fn from_vec(entries: Vec<Entry>) -> Self {
|
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 be;
|
||||||
mod entry;
|
mod entry;
|
||||||
mod event;
|
mod event;
|
||||||
|
mod identity;
|
||||||
|
mod plugins;
|
||||||
mod schema;
|
mod schema;
|
||||||
mod server;
|
mod server;
|
||||||
mod plugins;
|
|
||||||
|
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod core;
|
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;
|
mod uuid;
|
||||||
|
|
||||||
|
@ -5,4 +51,3 @@ mod uuid;
|
||||||
|
|
||||||
// How do we deal with plugin activation? Config?
|
// How do we deal with plugin activation? Config?
|
||||||
// What do plugins default to?
|
// 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(),
|
None => avas.clone(),
|
||||||
};
|
};
|
||||||
// now push those to the new entry.
|
// 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!
|
// Done!
|
||||||
entry_new
|
entry_new
|
||||||
|
|
|
@ -3,14 +3,13 @@ use actix::prelude::*;
|
||||||
use audit::AuditScope;
|
use audit::AuditScope;
|
||||||
use be::{Backend, BackendError};
|
use be::{Backend, BackendError};
|
||||||
|
|
||||||
use plugins;
|
|
||||||
use entry::Entry;
|
use entry::Entry;
|
||||||
use error::OperationError;
|
use error::OperationError;
|
||||||
use event::{CreateEvent, OpResult, SearchEvent, SearchResult};
|
use event::{CreateEvent, OpResult, SearchEvent, SearchResult};
|
||||||
use log::EventLog;
|
use log::EventLog;
|
||||||
|
use plugins;
|
||||||
use schema::Schema;
|
use schema::Schema;
|
||||||
|
|
||||||
|
|
||||||
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
|
pub fn start(log: actix::Addr<EventLog>, path: &str, threads: usize) -> actix::Addr<QueryServer> {
|
||||||
let mut audit = AuditScope::new("server_start");
|
let mut audit = AuditScope::new("server_start");
|
||||||
audit_segment!(audit, || {
|
audit_segment!(audit, || {
|
||||||
|
@ -83,13 +82,22 @@ impl QueryServer {
|
||||||
res
|
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> {
|
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
|
// Start a txn
|
||||||
|
|
||||||
// run any pre plugins
|
// run any pre plugins, giving them the list of mutable candidates.
|
||||||
|
|
||||||
// Run any pre checks
|
// Run any pre checks
|
||||||
// FIXME: Normalise all entries incoming
|
// FIXME: Normalise all entries incoming
|
||||||
|
@ -117,10 +125,13 @@ impl QueryServer {
|
||||||
BackendError::EmptyRequest => OperationError::EmptyRequest,
|
BackendError::EmptyRequest => OperationError::EmptyRequest,
|
||||||
_ => OperationError::Backend,
|
_ => OperationError::Backend,
|
||||||
});
|
});
|
||||||
au.append_scope(audit_be);
|
|
||||||
|
|
||||||
// Run any post plugins
|
// Run any post plugins
|
||||||
|
|
||||||
// Commit/Abort the txn
|
// Commit/Abort the txn
|
||||||
|
|
||||||
|
// We are complete, finalise logging and return
|
||||||
|
au.append_scope(audit_be);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue