//! An `event` is a self contained module of data, that contains all of the //! required information for any operation to proceed. While there are many //! types of potential events, they all eventually lower to one of: //! //! * AuthEvent //! * SearchEvent //! * ExistsEvent //! * ModifyEvent //! * CreateEvent //! * DeleteEvent //! //! An "event" is generally then passed to the `QueryServer` for processing. //! By making these fully self contained units, it means that we can assert //! at event creation time we have all the correct data required to proceed //! with the operation, and a clear path to know how to transform events between //! various types. use std::collections::BTreeSet; #[cfg(test)] use std::sync::Arc; use kanidm_proto::v1::{ CreateRequest, DeleteRequest, Entry as ProtoEntry, ModifyList as ProtoModifyList, ModifyRequest, OperationError, SearchRequest, SearchResponse, WhoamiResponse, }; use ldap3_proto::simple::LdapFilter; use uuid::Uuid; use crate::entry::{Entry, EntryCommitted, EntryInit, EntryNew, EntryReduced}; use crate::filter::{Filter, FilterInvalid, FilterValid}; use crate::modify::{ModifyInvalid, ModifyList, ModifyValid}; use crate::prelude::*; use crate::schema::SchemaTransaction; use crate::value::PartialValue; #[derive(Debug)] pub struct SearchResult { entries: Vec, } impl SearchResult { pub fn new( qs: &mut QueryServerReadTransaction, entries: &[Entry], ) -> Result { let entries: Result<_, _> = entries .iter() .map(|e| { // All the needed transforms for this result are done // in search_ext. This is just an entry -> protoentry // step. e.to_pe(qs) }) .collect(); Ok(SearchResult { entries: entries? }) } // Consume self into a search response pub fn response(self) -> SearchResponse { SearchResponse { entries: self.entries, } } // Consume into the array of entries, used in the json proto pub fn into_proto_array(self) -> Vec { self.entries } } #[derive(Debug)] pub struct SearchEvent { pub ident: Identity, // This is the filter as we apply and process it. pub filter: Filter, // This is the original filter, for the purpose of ACI checking. pub filter_orig: Filter, pub attrs: Option>, } impl SearchEvent { pub fn from_message( ident: Identity, req: &SearchRequest, qs: &mut QueryServerReadTransaction, ) -> Result { let f = Filter::from_ro(&ident, &req.filter, qs)?; // We do need to do this twice to account for the ignore_hidden // changes. let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(SearchEvent { ident, filter, filter_orig, // We can't get this from the SearchMessage because it's annoying with the // current macro design. attrs: None, }) } pub fn from_internal_message( ident: Identity, filter: &Filter, attrs: Option<&[String]>, qs: &mut QueryServerReadTransaction, ) -> Result { let r_attrs: Option> = attrs.map(|vs| { vs.iter() .filter_map(|a| qs.get_schema().normalise_attr_if_exists(a.as_str())) .collect() }); if let Some(s) = &r_attrs { if s.is_empty() { request_error!("EmptyRequest for attributes"); return Err(OperationError::EmptyRequest); } } let filter_orig = filter.validate(qs.get_schema()).map_err(|e| { request_error!(?e, "filter schema violation"); OperationError::SchemaViolation(e) })?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(SearchEvent { ident, filter, filter_orig, attrs: r_attrs, }) } pub fn from_internal_recycle_message( ident: Identity, filter: &Filter, attrs: Option<&[String]>, qs: &QueryServerReadTransaction, ) -> Result { let r_attrs: Option> = attrs.map(|vs| { vs.iter() .filter_map(|a| qs.get_schema().normalise_attr_if_exists(a.as_str())) .collect() }); if let Some(s) = &r_attrs { if s.is_empty() { return Err(OperationError::EmptyRequest); } } let filter_orig = filter .validate(qs.get_schema()) .map(|f| f.into_recycled()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone(); Ok(SearchEvent { ident, filter, filter_orig, attrs: r_attrs, }) } pub fn from_whoami_request( ident: Identity, qs: &QueryServerReadTransaction, ) -> Result { let filter_orig = filter_all!(f_self()) .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(SearchEvent { ident, filter, filter_orig, attrs: None, }) } pub fn from_target_uuid_request( ident: Identity, target_uuid: Uuid, qs: &QueryServerReadTransaction, ) -> Result { let filter_orig = filter_all!(f_eq("uuid", PartialValue::Uuid(target_uuid))) .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(SearchEvent { ident, filter, filter_orig, attrs: None, }) } // Just impersonate the account with no filter changes. #[cfg(test)] pub unsafe fn new_impersonate_entry_ser(e: &str, filter: Filter) -> Self { let ei: Entry = Entry::unsafe_from_entry_str(e); SearchEvent { ident: Identity::from_impersonate_entry_readonly(Arc::new(ei.into_sealed_committed())), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), attrs: None, } } #[cfg(test)] pub unsafe fn new_impersonate_entry( e: Arc>, filter: Filter, ) -> Self { SearchEvent { ident: Identity::from_impersonate_entry_readonly(e), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), attrs: None, } } #[cfg(test)] pub unsafe fn new_impersonate_identity(ident: Identity, filter: Filter) -> Self { SearchEvent { ident, filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), attrs: None, } } pub fn new_impersonate( ident: &Identity, filter: Filter, filter_orig: Filter, ) -> Self { SearchEvent { ident: Identity::from_impersonate(ident), filter, filter_orig, attrs: None, } } #[cfg(test)] /* Impersonate a request for recycled objects */ pub unsafe fn new_rec_impersonate_entry( e: Arc>, filter: Filter, ) -> Self { let filter_orig = filter.into_valid(); let filter = filter_orig.clone().into_recycled(); SearchEvent { ident: Identity::from_impersonate_entry_readonly(e), filter, filter_orig, attrs: None, } } #[cfg(test)] /* Impersonate an external request AKA filter ts + recycle */ pub unsafe fn new_ext_impersonate_entry( e: Arc>, filter: Filter, ) -> Self { SearchEvent { ident: Identity::from_impersonate_entry_readonly(e), filter: filter.clone().into_valid().into_ignore_hidden(), filter_orig: filter.into_valid(), attrs: None, } } pub(crate) fn new_ext_impersonate_uuid( qs: &mut QueryServerReadTransaction, ident: Identity, lf: &LdapFilter, attrs: Option>, ) -> Result { // Kanidm Filter from LdapFilter let f = Filter::from_ldap_ro(&ident, lf, qs)?; let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(SearchEvent { ident, filter, filter_orig, attrs, }) } #[cfg(test)] pub unsafe fn new_internal_invalid(filter: Filter) -> Self { SearchEvent { ident: Identity::from_internal(), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), attrs: None, } } pub fn new_internal(filter: Filter) -> Self { SearchEvent { ident: Identity::from_internal(), filter: filter.clone(), filter_orig: filter, attrs: None, } } pub(crate) fn get_limits(&self) -> &Limits { &self.ident.limits } } // 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 { pub ident: Identity, // This may still actually change to handle the *raw* nature of the // input that we plan to parse. pub entries: Vec>, // Is the CreateEvent from an internal or external source? // This may affect which plugins are run ... } impl CreateEvent { pub fn from_message( ident: Identity, req: &CreateRequest, qs: &mut QueryServerWriteTransaction, ) -> Result { let rentries: Result, _> = req .entries .iter() .map(|e| Entry::from_proto_entry(e, qs)) .collect(); // From ProtoEntry -> Entry // What is the correct consuming iterator here? Can we // even do that? match rentries { Ok(entries) => Ok(CreateEvent { ident, entries }), Err(e) => Err(e), } } #[cfg(test)] pub unsafe fn new_impersonate_entry_ser( e: &str, entries: Vec>, ) -> Self { let ei: Entry = Entry::unsafe_from_entry_str(e); CreateEvent { ident: Identity::from_impersonate_entry_readwrite(Arc::new(ei.into_sealed_committed())), entries, } } #[cfg(test)] pub fn new_impersonate_identity( ident: Identity, entries: Vec>, ) -> Self { CreateEvent { ident, entries } } pub fn new_internal(entries: Vec>) -> Self { CreateEvent { ident: Identity::from_internal(), entries, } } } #[derive(Debug)] pub struct ExistsEvent { pub ident: Identity, // This is the filter, as it will be processed. pub filter: Filter, // This is the original filter, for the purpose of ACI checking. pub filter_orig: Filter, } impl ExistsEvent { pub fn new_internal(filter: Filter) -> Self { ExistsEvent { ident: Identity::from_internal(), filter: filter.clone(), filter_orig: filter, } } #[cfg(test)] #[allow(dead_code)] pub unsafe fn new_internal_invalid(filter: Filter) -> Self { ExistsEvent { ident: Identity::from_internal(), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), } } pub(crate) fn get_limits(&self) -> &Limits { &self.ident.limits } } #[derive(Debug)] pub struct DeleteEvent { pub ident: Identity, // This is the filter, as it will be processed. pub filter: Filter, // This is the original filter, for the purpose of ACI checking. pub filter_orig: Filter, } impl DeleteEvent { pub fn from_message( ident: Identity, req: &DeleteRequest, qs: &mut QueryServerWriteTransaction, ) -> Result { let f = Filter::from_rw(&ident, &req.filter, qs)?; let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(DeleteEvent { ident, filter, filter_orig, }) } pub fn from_parts( ident: Identity, f: &Filter, qs: &mut QueryServerWriteTransaction, ) -> Result { let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); Ok(DeleteEvent { ident, filter, filter_orig, }) } #[cfg(test)] pub unsafe fn new_impersonate_entry( e: Arc>, filter: Filter, ) -> Self { DeleteEvent { ident: Identity::from_impersonate_entry_readwrite(e), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), } } #[cfg(test)] pub fn new_impersonate_identity(ident: Identity, filter: Filter) -> Self { unsafe { DeleteEvent { ident, filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), } } } #[cfg(test)] pub unsafe fn new_impersonate_entry_ser(e: &str, filter: Filter) -> Self { let ei: Entry = Entry::unsafe_from_entry_str(e); DeleteEvent { ident: Identity::from_impersonate_entry_readwrite(Arc::new(ei.into_sealed_committed())), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), } } #[cfg(test)] pub unsafe fn new_internal_invalid(filter: Filter) -> Self { DeleteEvent { ident: Identity::from_internal(), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), } } pub fn new_internal(filter: Filter) -> Self { DeleteEvent { ident: Identity::from_internal(), filter: filter.clone(), filter_orig: filter, } } } #[derive(Debug)] pub struct ModifyEvent { pub ident: Identity, // This is the filter, as it will be processed. pub filter: Filter, // This is the original filter, for the purpose of ACI checking. pub filter_orig: Filter, pub modlist: ModifyList, } impl ModifyEvent { pub fn from_message( ident: Identity, req: &ModifyRequest, qs: &mut QueryServerWriteTransaction, ) -> Result { let f = Filter::from_rw(&ident, &req.filter, qs)?; let m = ModifyList::from(&req.modlist, qs)?; let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); let modlist = m .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; Ok(ModifyEvent { ident, filter, filter_orig, modlist, }) } pub fn from_parts( ident: Identity, target_uuid: Uuid, proto_ml: &ProtoModifyList, filter: Filter, qs: &mut QueryServerWriteTransaction, ) -> Result { let f_uuid = filter_all!(f_eq("uuid", PartialValue::Uuid(target_uuid))); // Add any supplemental conditions we have. let f = Filter::join_parts_and(f_uuid, filter); let m = ModifyList::from(proto_ml, qs)?; let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); let modlist = m .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; Ok(ModifyEvent { ident, filter, filter_orig, modlist, }) } pub fn from_internal_parts( ident: Identity, ml: &ModifyList, filter: &Filter, qs: &QueryServerWriteTransaction, ) -> Result { let filter_orig = filter .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); let modlist = ml .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; Ok(ModifyEvent { ident, filter, filter_orig, modlist, }) } pub fn from_target_uuid_attr_purge( ident: Identity, target_uuid: Uuid, attr: &str, filter: Filter, qs: &QueryServerWriteTransaction, ) -> Result { let ml = ModifyList::new_purge(attr); let f_uuid = filter_all!(f_eq("uuid", PartialValue::Uuid(target_uuid))); // Add any supplemental conditions we have. let f = Filter::join_parts_and(f_uuid, filter); let filter_orig = f .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; let filter = filter_orig.clone().into_ignore_hidden(); let modlist = ml .validate(qs.get_schema()) .map_err(OperationError::SchemaViolation)?; Ok(ModifyEvent { ident, filter, filter_orig, modlist, }) } pub fn new_internal(filter: Filter, modlist: ModifyList) -> Self { ModifyEvent { ident: Identity::from_internal(), filter: filter.clone(), filter_orig: filter, modlist, } } #[cfg(test)] pub unsafe fn new_internal_invalid( filter: Filter, modlist: ModifyList, ) -> Self { ModifyEvent { ident: Identity::from_internal(), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), modlist: modlist.into_valid(), } } #[cfg(test)] pub unsafe fn new_impersonate_entry_ser( e: &str, filter: Filter, modlist: ModifyList, ) -> Self { let ei: Entry = Entry::unsafe_from_entry_str(e); ModifyEvent { ident: Identity::from_impersonate_entry_readwrite(Arc::new(ei.into_sealed_committed())), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), modlist: modlist.into_valid(), } } #[cfg(test)] pub unsafe fn new_impersonate_identity( ident: Identity, filter: Filter, modlist: ModifyList, ) -> Self { ModifyEvent { ident, filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), modlist: modlist.into_valid(), } } #[cfg(test)] pub unsafe fn new_impersonate_entry( e: Arc>, filter: Filter, modlist: ModifyList, ) -> Self { ModifyEvent { ident: Identity::from_impersonate_entry_readwrite(e), filter: filter.clone().into_valid(), filter_orig: filter.into_valid(), modlist: modlist.into_valid(), } } pub fn new_impersonate( ident: &Identity, filter: Filter, filter_orig: Filter, modlist: ModifyList, ) -> Self { ModifyEvent { ident: Identity::from_impersonate(ident), filter, filter_orig, modlist, } } } pub struct WhoamiResult { youare: ProtoEntry, } impl WhoamiResult { pub fn new( qs: &mut QueryServerReadTransaction, e: &Entry, ) -> Result { Ok(WhoamiResult { youare: e.to_pe(qs)?, }) } pub fn response(self) -> WhoamiResponse { WhoamiResponse { youare: self.youare, } } } #[derive(Debug)] pub struct PurgeTombstoneEvent { pub ident: Identity, pub eventid: Uuid, } impl Default for PurgeTombstoneEvent { fn default() -> Self { Self::new() } } impl PurgeTombstoneEvent { pub fn new() -> Self { PurgeTombstoneEvent { ident: Identity::from_internal(), eventid: Uuid::new_v4(), } } } #[derive(Debug)] pub struct PurgeRecycledEvent { pub ident: Identity, pub eventid: Uuid, } impl Default for PurgeRecycledEvent { fn default() -> Self { Self::new() } } impl PurgeRecycledEvent { pub fn new() -> Self { PurgeRecycledEvent { ident: Identity::from_internal(), eventid: Uuid::new_v4(), } } } #[derive(Debug)] pub struct OnlineBackupEvent { pub ident: Identity, pub eventid: Uuid, } impl Default for OnlineBackupEvent { fn default() -> Self { Self::new() } } impl OnlineBackupEvent { pub fn new() -> Self { OnlineBackupEvent { ident: Identity::from_internal(), eventid: Uuid::new_v4(), } } } #[derive(Debug)] pub struct ReviveRecycledEvent { pub ident: Identity, // This is the filter, as it will be processed. pub filter: Filter, // Unlike the others, because of how this works, we don't need the orig filter // to be retained, because the filter is the orig filter for this check. // // It will be duplicated into the modify ident as it exists. } impl ReviveRecycledEvent { pub fn from_parts( ident: Identity, filter: &Filter, qs: &QueryServerWriteTransaction, ) -> Result { let filter = filter .validate(qs.get_schema()) .map(|f| f.into_recycled()) .map_err(OperationError::SchemaViolation)?; Ok(ReviveRecycledEvent { ident, filter }) } #[cfg(test)] pub unsafe fn new_impersonate_entry( e: Arc>, filter: Filter, ) -> Self { ReviveRecycledEvent { ident: Identity::from_impersonate_entry_readwrite(e), filter: filter.into_valid(), } } }