diff --git a/kanidmd/lib/src/access.rs b/kanidmd/lib/src/access.rs index 20f6cf92b..23c71bad5 100644 --- a/kanidmd/lib/src/access.rs +++ b/kanidmd/lib/src/access.rs @@ -75,16 +75,16 @@ impl AccessControlSearch { #[cfg(test)] unsafe fn from_raw( name: &str, - uuid: &str, - receiver: Filter, + uuid: Uuid, + receiver: Uuid, targetscope: Filter, attrs: &str, ) -> Self { AccessControlSearch { acp: AccessControlProfile { name: name.to_string(), - uuid: Uuid::parse_str(uuid).unwrap(), - receiver, + uuid, + receiver: Some(receiver), targetscope, }, attrs: attrs @@ -120,15 +120,15 @@ impl AccessControlDelete { #[cfg(test)] unsafe fn from_raw( name: &str, - uuid: &str, - receiver: Filter, + uuid: Uuid, + receiver: Uuid, targetscope: Filter, ) -> Self { AccessControlDelete { acp: AccessControlProfile { name: name.to_string(), - uuid: Uuid::parse_str(uuid).unwrap(), - receiver, + uuid, + receiver: Some(receiver), targetscope, }, } @@ -174,8 +174,8 @@ impl AccessControlCreate { #[cfg(test)] unsafe fn from_raw( name: &str, - uuid: &str, - receiver: Filter, + uuid: Uuid, + receiver: Uuid, targetscope: Filter, classes: &str, attrs: &str, @@ -183,8 +183,8 @@ impl AccessControlCreate { AccessControlCreate { acp: AccessControlProfile { name: name.to_string(), - uuid: Uuid::parse_str(uuid).unwrap(), - receiver, + uuid, + receiver: Some(receiver), targetscope, }, classes: classes.split_whitespace().map(AttrString::from).collect(), @@ -239,8 +239,8 @@ impl AccessControlModify { #[cfg(test)] unsafe fn from_raw( name: &str, - uuid: &str, - receiver: Filter, + uuid: Uuid, + receiver: Uuid, targetscope: Filter, presattrs: &str, remattrs: &str, @@ -249,8 +249,8 @@ impl AccessControlModify { AccessControlModify { acp: AccessControlProfile { name: name.to_string(), - uuid: Uuid::parse_str(uuid).unwrap(), - receiver, + uuid, + receiver: Some(receiver), targetscope, }, classes: classes @@ -278,7 +278,13 @@ struct AccessControlProfile { uuid: Uuid, // Must be // Group - receiver: Filter, + // === ⚠️ WARNING!!! ⚠️ === + // This is OPTION to allow migration from 10 -> 11. We have to do this because ACP is reloaded + // so early in the boot phase that we can't have migrated the content of the receiver yet! As a + // result we MUST be able to withstand some failure in the parse process. The INTENT is that + // during early boot this will be None, and will NEVER match. Once started, the migration + // will occur, and this will flip to Some. In a future version we can remove this! + receiver: Option, // or // Filter // Group @@ -313,14 +319,17 @@ impl AccessControlProfile { // copy uuid let uuid = value.get_uuid(); // receiver, and turn to real filter - let receiver_f: ProtoFilter = value - .get_ava_single_protofilter("acp_receiver") - // .map(|pf| pf.clone()) - .cloned() - .ok_or_else(|| { - admin_error!("Missing acp_receiver"); - OperationError::InvalidAcpState("Missing acp_receiver".to_string()) - })?; + + // === ⚠️ WARNING!!! ⚠️ === + // See struct ACP for details. + let receiver = value.get_ava_single_refer("acp_receiver_group"); + /* + .ok_or_else(|| { + admin_error!("Missing acp_receiver_group"); + OperationError::InvalidAcpState("Missing acp_receiver_group".to_string()) + })?; + */ + // targetscope, and turn to real filter let targetscope_f: ProtoFilter = value .get_ava_single_protofilter("acp_targetscope") @@ -333,15 +342,6 @@ impl AccessControlProfile { let ident = Identity::from_internal(); - let receiver_i = Filter::from_rw(&ident, &receiver_f, qs).map_err(|e| { - admin_error!("Receiver validation failed {:?}", e); - e - })?; - let receiver = receiver_i.validate(qs.get_schema()).map_err(|e| { - admin_error!("acp_receiver Schema Violation {:?}", e); - OperationError::SchemaViolation(e) - })?; - let targetscope_i = Filter::from_rw(&ident, &targetscope_f, qs).map_err(|e| { admin_error!("Targetscope validation failed {:?}", e); e @@ -405,7 +405,6 @@ pub trait AccessControlsTransaction<'a> { #[instrument(level = "debug", name = "access::search_related_acp", skip_all)] fn search_related_acp<'b>( &'b self, - rec_entry: &Entry, ident: &Identity, ) -> Vec<(&'b AccessControlSearch, Filter)> { let search_state = self.get_search(); @@ -456,35 +455,27 @@ pub trait AccessControlsTransaction<'a> { // A possible solution is to change the filter resolve function // such that it takes an entry, rather than an event, but that // would create issues in search. - match acs - .acp - .receiver - .resolve(ident, None, Some(acp_resolve_filter_cache)) - { - Ok(f_res) => { - if rec_entry.entry_match_no_index(&f_res) { - // Now, for each of the acp's that apply to our receiver, resolve their - // related target filters. - acs.acp - .targetscope - .resolve(ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - ?e, - "A internal filter/event was passed for resolution!?!?" - ); - e - }) - .ok() - .map(|f_res| (acs, f_res)) - } else { - None - } - } - Err(e) => { - admin_error!(?e, "A internal filter/event was passed for resolution!?!?"); + if let Some(receiver) = acs.acp.receiver { + if ident.is_memberof(receiver) { + // Now, for each of the acp's that apply to our receiver, resolve their + // related target filters. + acs.acp + .targetscope + .resolve(ident, None, Some(acp_resolve_filter_cache)) + .map_err(|e| { + admin_error!( + ?e, + "A internal filter/event was passed for resolution!?!?" + ); + e + }) + .ok() + .map(|f_res| (acs, f_res)) + } else { None } + } else { + None } }) .collect(); @@ -508,7 +499,7 @@ pub trait AccessControlsTransaction<'a> { entries: Vec>, ) -> Result>, OperationError> { // If this is an internal search, return our working set. - let rec_entry: &Entry = match &se.ident.origin { + match &se.ident.origin { IdentType::Internal => { trace!("Internal operation, bypassing access check"); // No need to check ACS @@ -518,7 +509,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; info!(event = %se.ident, "Access check for search (filter) event"); @@ -533,8 +524,7 @@ pub trait AccessControlsTransaction<'a> { }; // First get the set of acps that apply to this receiver - let related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, &se.ident); + let related_acp: Vec<(&AccessControlSearch, _)> = self.search_related_acp(&se.ident); /* related_acp.iter().for_each(|racp| { @@ -604,7 +594,7 @@ pub trait AccessControlsTransaction<'a> { entries: Vec>, ) -> Result>, OperationError> { // If this is an internal search, do nothing. This can occur in some test cases ONLY - let rec_entry: &Entry = match &se.ident.origin { + match &se.ident.origin { IdentType::Internal => { if cfg!(test) { trace!("TEST: Internal search in external interface - allowing due to cfg test ..."); @@ -625,7 +615,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; /* @@ -639,8 +629,7 @@ pub trait AccessControlsTransaction<'a> { info!(event = %se.ident, "Access check for search (reduce) event"); // Get the relevant acps for this receiver. - let related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, &se.ident); + let related_acp: Vec<(&AccessControlSearch, _)> = self.search_related_acp(&se.ident); let related_acp: Vec<(&AccessControlSearch, _)> = if let Some(r_attrs) = se.attrs.as_ref() { related_acp .into_iter() @@ -724,7 +713,6 @@ pub trait AccessControlsTransaction<'a> { #[instrument(level = "debug", name = "access::modify_related_acp", skip_all)] fn modify_related_acp<'b>( &'b self, - rec_entry: &Entry, ident: &Identity, ) -> Vec<(&'b AccessControlModify, Filter)> { // Some useful references we'll use for the remainder of the operation @@ -733,38 +721,31 @@ pub trait AccessControlsTransaction<'a> { // Find the acps that relate to the caller, and compile their related // target filters. - let related_acp: Vec<(&AccessControlModify, _)> = - modify_state - .iter() - .filter_map(|acs| { - match acs.acp.receiver.resolve(ident, None, Some(acp_resolve_filter_cache)) { - Ok(f_res) => { - if rec_entry.entry_match_no_index(&f_res) { - acs.acp.targetscope - .resolve(ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", - e - ); - e - }) - .ok() - .map(|f_res| (acs, f_res)) - } else { - None - } - } - Err(e) => { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", + let related_acp: Vec<(&AccessControlModify, _)> = modify_state + .iter() + .filter_map(|acs| { + if let Some(receiver) = acs.acp.receiver { + if ident.is_memberof(receiver) { + acs.acp + .targetscope + .resolve(ident, None, Some(acp_resolve_filter_cache)) + .map_err(|e| { + admin_error!( + "A internal filter/event was passed for resolution!?!? {:?}", + e + ); e - ); - None - } + }) + .ok() + .map(|f_res| (acs, f_res)) + } else { + None } - }) - .collect(); + } else { + None + } + }) + .collect(); related_acp } @@ -776,7 +757,7 @@ pub trait AccessControlsTransaction<'a> { me: &ModifyEvent, entries: &[Arc], ) -> Result { - let rec_entry: &Entry = match &me.ident.origin { + match &me.ident.origin { IdentType::Internal => { trace!("Internal operation, bypassing access check"); // No need to check ACS @@ -786,7 +767,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; info!(event = %me.ident, "Access check for modify event"); @@ -813,8 +794,7 @@ pub trait AccessControlsTransaction<'a> { // Find the acps that relate to the caller, and compile their related // target filters. - let related_acp: Vec<(&AccessControlModify, _)> = - self.modify_related_acp(rec_entry, &me.ident); + let related_acp: Vec<(&AccessControlModify, _)> = self.modify_related_acp(&me.ident); related_acp.iter().for_each(|racp| { trace!("Related acs -> {:?}", racp.0.acp.name); @@ -951,7 +931,7 @@ pub trait AccessControlsTransaction<'a> { ce: &CreateEvent, entries: &[Entry], ) -> Result { - let rec_entry: &Entry = match &ce.ident.origin { + match &ce.ident.origin { IdentType::Internal => { trace!("Internal operation, bypassing access check"); // No need to check ACS @@ -961,7 +941,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; info!(event = %ce.ident, "Access check for create event"); @@ -983,32 +963,25 @@ pub trait AccessControlsTransaction<'a> { let related_acp: Vec<(&AccessControlCreate, _)> = create_state .iter() .filter_map(|acs| { - match acs - .acp - .receiver - .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) - { - Ok(f_res) if rec_entry.entry_match_no_index(&f_res) => acs - .acp - .targetscope - .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", + if let Some(receiver) = acs.acp.receiver { + if ce.ident.is_memberof(receiver) { + acs.acp + .targetscope + .resolve(&ce.ident, None, Some(acp_resolve_filter_cache)) + .map_err(|e| { + admin_error!( + "A internal filter/event was passed for resolution!?!? {:?}", + e + ); e - ); - e - }) - .ok() - .map(|f_res| (acs, f_res)), - Ok(_) => None, - Err(e) => { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", - e - ); + }) + .ok() + .map(|f_res| (acs, f_res)) + } else { None } + } else { + None } }) .collect(); @@ -1096,7 +1069,7 @@ pub trait AccessControlsTransaction<'a> { de: &DeleteEvent, entries: &[Arc], ) -> Result { - let rec_entry: &Entry = match &de.ident.origin { + match &de.ident.origin { IdentType::Internal => { trace!("Internal operation, bypassing access check"); // No need to check ACS @@ -1106,7 +1079,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; info!(event = %de.ident, "Access check for delete event"); @@ -1126,38 +1099,30 @@ pub trait AccessControlsTransaction<'a> { // Find the acps that relate to the caller. let related_acp: Vec<(&AccessControlDelete, _)> = delete_state - .iter() - .filter_map(|acs| { - match acs.acp.receiver.resolve(&de.ident, None, Some(acp_resolve_filter_cache)) { - Ok(f_res) => { - if rec_entry.entry_match_no_index(&f_res) { - acs.acp.targetscope - .resolve(&de.ident, None, Some(acp_resolve_filter_cache)) - .map_err(|e| { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", - e - ); - e - }) - .ok() - .map(|f_res| - (acs, f_res) - ) - } else { - None - } - } - Err(e) => { - admin_error!( - "A internal filter/event was passed for resolution!?!? {:?}", + .iter() + .filter_map(|acs| { + if let Some(receiver) = acs.acp.receiver { + if de.ident.is_memberof(receiver) { + acs.acp + .targetscope + .resolve(&de.ident, None, Some(acp_resolve_filter_cache)) + .map_err(|e| { + admin_error!( + "A internal filter/event was passed for resolution!?!? {:?}", + e + ); e - ); - None - } + }) + .ok() + .map(|f_res| (acs, f_res)) + } else { + None } - }) - .collect(); + } else { + None + } + }) + .collect(); /* related_acp.iter().for_each(|racp| { @@ -1211,7 +1176,7 @@ pub trait AccessControlsTransaction<'a> { // have an entry template. I think james was right about the create being // a template copy op ... - let rec_entry: &Entry = match &ident.origin { + match &ident.origin { IdentType::Internal => { // In production we can't risk leaking data here, so we return // empty sets. @@ -1223,7 +1188,7 @@ pub trait AccessControlsTransaction<'a> { security_critical!("Blocking sync check"); return Err(OperationError::InvalidState); } - IdentType::User(u) => &u.entry, + IdentType::User(_) => {} }; trace!(ident = %ident, "Effective permission check"); @@ -1231,8 +1196,7 @@ pub trait AccessControlsTransaction<'a> { // == search == // Get the relevant acps for this receiver. - let search_related_acp: Vec<(&AccessControlSearch, _)> = - self.search_related_acp(rec_entry, ident); + let search_related_acp: Vec<(&AccessControlSearch, _)> = self.search_related_acp(ident); let search_related_acp: Vec<(&AccessControlSearch, _)> = if let Some(r_attrs) = attrs.as_ref() { search_related_acp @@ -1251,8 +1215,7 @@ pub trait AccessControlsTransaction<'a> { // == modify == - let modify_related_acp: Vec<(&AccessControlModify, _)> = - self.modify_related_acp(rec_entry, ident); + let modify_related_acp: Vec<(&AccessControlModify, _)> = self.modify_related_acp(ident); /* modify_related_acp.iter().for_each(|(racp, _)| { @@ -1572,6 +1535,32 @@ mod tests { use crate::event::{CreateEvent, DeleteEvent, ModifyEvent, SearchEvent}; use crate::prelude::*; + const UUID_TEST_ACCOUNT_1: Uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"); + const UUID_TEST_ACCOUNT_2: Uuid = uuid::uuid!("cec0852a-abdf-4ea6-9dae-d3157cb33d3a"); + const UUID_TEST_GROUP_1: Uuid = uuid::uuid!("81ec1640-3637-4a2f-8a52-874fa3c3c92f"); + const UUID_TEST_GROUP_2: Uuid = uuid::uuid!("acae81d6-5ea7-4bd8-8f7f-fcec4c0dd647"); + + lazy_static! { + pub static ref E_TEST_ACCOUNT_1: Arc = Arc::new(unsafe { + entry_init!( + ("class", Value::new_class("object")), + ("name", Value::new_iname("test_account_1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)), + ("memberof", Value::new_refer(UUID_TEST_GROUP_1)) + ) + .into_sealed_committed() + }); + pub static ref E_TEST_ACCOUNT_2: Arc = Arc::new(unsafe { + entry_init!( + ("class", Value::new_class("object")), + ("name", Value::new_iname("test_account_1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_2)), + ("memberof", Value::new_refer(UUID_TEST_GROUP_2)) + ) + .into_sealed_committed() + }); + } + macro_rules! acp_from_entry_err { ( $qs:expr, @@ -1643,7 +1632,7 @@ mod tests { "class": ["object", "access_control_profile"], "name": ["acp_invalid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [""], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [""] } }"#, @@ -1659,11 +1648,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::new_uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::new_refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1685,9 +1674,7 @@ mod tests { "class": ["object", "access_control_profile"], "name": ["acp_valid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ] @@ -1705,11 +1692,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1733,9 +1720,7 @@ mod tests { "class": ["object", "access_control_search"], "name": ["acp_invalid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ], @@ -1753,9 +1738,7 @@ mod tests { "class": ["object", "access_control_profile"], "name": ["acp_invalid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ], @@ -1773,9 +1756,7 @@ mod tests { "class": ["object", "access_control_profile", "access_control_search"], "name": ["acp_invalid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ] @@ -1794,11 +1775,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1823,9 +1804,7 @@ mod tests { "class": ["object", "access_control_profile"], "name": ["acp_valid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ], @@ -1849,8 +1828,8 @@ mod tests { Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1869,11 +1848,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1899,9 +1878,7 @@ mod tests { "class": ["object", "access_control_profile"], "name": ["acp_valid"], "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], - "acp_receiver": [ - "{\"eq\":[\"name\",\"a\"]}" - ], + "acp_receiver_group": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"], "acp_targetscope": [ "{\"eq\":[\"name\",\"a\"]}" ], @@ -1921,11 +1898,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1944,11 +1921,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -1979,11 +1956,11 @@ mod tests { ("name", Value::new_iname("acp_valid")), ( "uuid", - Value::new_uuids("cc8e95b4-c24f-4d68-ba54-8bed76f63930").expect("uuid") + Value::Uuid(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( - "acp_receiver", - Value::new_json_filter_s("{\"eq\":[\"name\",\"a\"]}").expect("filter") + "acp_receiver_group", + Value::Refer(uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930")) ), ( "acp_targetscope", @@ -2064,19 +2041,8 @@ mod tests { // Test that an internal search bypasses ACS let se = unsafe { SearchEvent::new_internal_invalid(filter!(f_pres("class"))) }; - let e1: Entry = Entry::unsafe_from_entry_str( - r#"{ - "attrs": { - "class": ["object"], - "name": ["testperson1"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } - }"#, - ); - let ev1 = unsafe { e1.into_sealed_committed() }; - - let expect = vec![Arc::new(ev1.clone())]; - let entries = vec![Arc::new(ev1)]; + let expect = vec![E_TEST_ACCOUNT_1.clone()]; + let entries = vec![E_TEST_ACCOUNT_1.clone()]; // This acp basically is "allow access to stuff, but not this". test_acp_search!( @@ -2084,10 +2050,10 @@ mod tests { vec![unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", - filter_valid!(f_pres("class")), // apply to all people + Uuid::new_v4(), + UUID_TEST_GROUP_1, filter_valid!(f_pres("nomatchy")), // apply to none - ie no allowed results - "name", // allow to this attr, but we don't eval this. + "name", // allow to this attr, but we don't eval this. ) }], entries, @@ -2103,22 +2069,28 @@ mod tests { let r_set = vec![Arc::new(ev1.clone()), Arc::new(ev2.clone())]; - let se_admin = unsafe { - SearchEvent::new_impersonate_entry_ser(JSON_ADMIN_V1, filter_all!(f_pres("name"))) + let se_a = unsafe { + SearchEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), + filter_all!(f_pres("name")), + ) }; - let ex_admin = vec![Arc::new(ev1.clone())]; + let ex_a = vec![Arc::new(ev1.clone())]; - let se_anon = unsafe { - SearchEvent::new_impersonate_entry_ser(JSON_ANONYMOUS_V1, filter_all!(f_pres("name"))) + let se_b = unsafe { + SearchEvent::new_impersonate_entry( + E_TEST_ACCOUNT_2.clone(), + filter_all!(f_pres("name")), + ) }; - let ex_anon = vec![]; + let ex_b = vec![]; let acp = unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to admin only - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // Allow admin to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // In that read, admin may only view the "name" attribute, or query on @@ -2128,10 +2100,10 @@ mod tests { }; // Check the admin search event - test_acp_search!(&se_admin, vec![acp.clone()], r_set.clone(), ex_admin); + test_acp_search!(&se_a, vec![acp.clone()], r_set.clone(), ex_a); // Check the anonymous - test_acp_search!(&se_anon, vec![acp], r_set, ex_anon); + test_acp_search!(&se_b, vec![acp], r_set, ex_b); } #[test] @@ -2140,34 +2112,28 @@ mod tests { // Test that identities are bound by their access scope. let ev1 = unsafe { E_TESTPERSON_1.clone().into_sealed_committed() }; - let ex_admin_some = vec![Arc::new(ev1.clone())]; - let ex_admin_none = vec![]; + let ex_some = vec![Arc::new(ev1.clone())]; + let ex_none = vec![]; let r_set = vec![Arc::new(ev1)]; - let se_admin_io = unsafe { + let se_io = unsafe { SearchEvent::new_impersonate_identity( - Identity::from_impersonate_entry_identityonly(Arc::new( - E_ADMIN_V1.clone().into_sealed_committed(), - )), + Identity::from_impersonate_entry_identityonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_pres("name")), ) }; - let se_admin_ro = unsafe { + let se_ro = unsafe { SearchEvent::new_impersonate_identity( - Identity::from_impersonate_entry_readonly(Arc::new( - E_ADMIN_V1.clone().into_sealed_committed(), - )), + Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_pres("name")), ) }; - let se_admin_rw = unsafe { + let se_rw = unsafe { SearchEvent::new_impersonate_identity( - Identity::from_impersonate_entry_readwrite(Arc::new( - E_ADMIN_V1.clone().into_sealed_committed(), - )), + Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()), filter_all!(f_pres("name")), ) }; @@ -2175,9 +2141,9 @@ mod tests { let acp = unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to admin only - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // Allow admin to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // In that read, admin may only view the "name" attribute, or query on @@ -2187,26 +2153,11 @@ mod tests { }; // Check the admin search event - test_acp_search!( - &se_admin_io, - vec![acp.clone()], - r_set.clone(), - ex_admin_none - ); + test_acp_search!(&se_io, vec![acp.clone()], r_set.clone(), ex_none); - test_acp_search!( - &se_admin_ro, - vec![acp.clone()], - r_set.clone(), - ex_admin_some - ); + test_acp_search!(&se_ro, vec![acp.clone()], r_set.clone(), ex_some); - test_acp_search!( - &se_admin_rw, - vec![acp.clone()], - r_set.clone(), - ex_admin_some - ); + test_acp_search!(&se_rw, vec![acp.clone()], r_set.clone(), ex_some); } #[test] @@ -2224,18 +2175,14 @@ mod tests { let se_anon_io = unsafe { SearchEvent::new_impersonate_identity( - Identity::from_impersonate_entry_identityonly(Arc::new( - E_ANONYMOUS_V1.clone().into_sealed_committed(), - )), + Identity::from_impersonate_entry_identityonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_pres("name")), ) }; let se_anon_ro = unsafe { SearchEvent::new_impersonate_identity( - Identity::from_impersonate_entry_readonly(Arc::new( - E_ANONYMOUS_V1.clone().into_sealed_committed(), - )), + Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_pres("name")), ) }; @@ -2243,9 +2190,9 @@ mod tests { let acp = unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", - // apply to anonymous only - filter_valid!(f_eq("name", PartialValue::new_iname("anonymous"))), + Uuid::new_v4(), + // apply to all accounts. + UUID_TEST_GROUP_1, // Allow anonymous to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // In that read, admin may only view the "name" attribute, or query on @@ -2277,8 +2224,8 @@ mod tests { let ex_anon = vec![exv1.clone()]; let se_anon = unsafe { - SearchEvent::new_impersonate_entry_ser( - JSON_ANONYMOUS_V1, + SearchEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), ) }; @@ -2286,9 +2233,9 @@ mod tests { let acp = unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to anonymous only - filter_valid!(f_eq("name", PartialValue::new_iname("anonymous"))), + UUID_TEST_GROUP_1, // Allow anonymous to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // In that read, admin may only view the "name" attribute, or query on @@ -2314,8 +2261,8 @@ mod tests { let ex_anon = vec![exv1.clone()]; let mut se_anon = unsafe { - SearchEvent::new_impersonate_entry_ser( - JSON_ANONYMOUS_V1, + SearchEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), ) }; @@ -2325,9 +2272,9 @@ mod tests { let acp = unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to anonymous only - filter_valid!(f_eq("name", PartialValue::new_iname("anonymous"))), + UUID_TEST_GROUP_1, // Allow anonymous to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // In that read, admin may only view the "name" attribute, or query on @@ -2370,24 +2317,24 @@ mod tests { // Name present let me_pres = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_pres("name", &Value::new_iname("value"))]), ) }; // Name rem let me_rem = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_remove("name", &PartialValue::new_iname("value"))]), ) }; // Name purge let me_purge = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_purge("name")]), ) @@ -2395,24 +2342,24 @@ mod tests { // Class account pres let me_pres_class = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_pres("class", &Value::new_class("account"))]), ) }; // Class account rem let me_rem_class = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_remove("class", &PartialValue::new_class("account"))]), ) }; // Class purge let me_purge_class = unsafe { - ModifyEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + ModifyEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_purge("class")]), ) @@ -2422,9 +2369,9 @@ mod tests { let acp_allow = unsafe { AccessControlModify::from_raw( "test_modify_allow", - "87bfe9b8-7600-431e-a492-1dde64bbc455", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To modify testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // Allow pres name and class @@ -2439,9 +2386,9 @@ mod tests { let acp_deny = unsafe { AccessControlModify::from_raw( "test_modify_deny", - "87bfe9b8-7600-431e-a492-1dde64bbc456", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To modify testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // Allow pres name and class @@ -2456,9 +2403,9 @@ mod tests { let acp_no_class = unsafe { AccessControlModify::from_raw( "test_modify_no_class", - "87bfe9b8-7600-431e-a492-1dde64bbc457", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To modify testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // Allow pres name and class @@ -2506,12 +2453,10 @@ mod tests { let ev1 = unsafe { E_TESTPERSON_1.clone().into_sealed_committed() }; let r_set = vec![Arc::new(ev1.clone())]; - let admin = Arc::new(unsafe { E_ADMIN_V1.clone().into_sealed_committed() }); - // Name present let me_pres_io = unsafe { ModifyEvent::new_impersonate_identity( - Identity::from_impersonate_entry_identityonly(admin.clone()), + Identity::from_impersonate_entry_identityonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_pres("name", &Value::new_iname("value"))]), ) @@ -2520,7 +2465,7 @@ mod tests { // Name present let me_pres_ro = unsafe { ModifyEvent::new_impersonate_identity( - Identity::from_impersonate_entry_readonly(admin.clone()), + Identity::from_impersonate_entry_readonly(E_TEST_ACCOUNT_1.clone()), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_pres("name", &Value::new_iname("value"))]), ) @@ -2529,7 +2474,7 @@ mod tests { // Name present let me_pres_rw = unsafe { ModifyEvent::new_impersonate_identity( - Identity::from_impersonate_entry_readwrite(admin.clone()), + Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), modlist!([m_pres("name", &Value::new_iname("value"))]), ) @@ -2538,9 +2483,9 @@ mod tests { let acp_allow = unsafe { AccessControlModify::from_raw( "test_modify_allow", - "87bfe9b8-7600-431e-a492-1dde64bbc455", - // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + Uuid::new_v4(), + // apply to admin only + UUID_TEST_GROUP_1, // To modify testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // Allow pres name and class @@ -2582,51 +2527,38 @@ mod tests { }}; } - const JSON_TEST_CREATE_AC1: &'static str = r#"{ - "attrs": { - "class": ["account"], - "name": ["testperson1"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } - }"#; - - const JSON_TEST_CREATE_AC2: &'static str = r#"{ - "attrs": { - "class": ["account"], - "name": ["testperson1"], - "notallowed": ["not allowed!"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } - }"#; - - const JSON_TEST_CREATE_AC3: &'static str = r#"{ - "attrs": { - "class": ["account", "notallowed"], - "name": ["testperson1"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } - }"#; - - const JSON_TEST_CREATE_AC4: &'static str = r#"{ - "attrs": { - "class": ["account", "group"], - "name": ["testperson1"], - "uuid": ["cc8e95b4-c24f-4d68-ba54-8bed76f63930"] - } - }"#; - #[test] fn test_access_enforce_create() { - let ev1: Entry = Entry::unsafe_from_entry_str(JSON_TEST_CREATE_AC1); + let ev1 = entry_init!( + ("class", Value::new_class("account")), + ("name", Value::new_iname("testperson1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)) + ); let r1_set = vec![ev1.clone()]; - let ev2: Entry = Entry::unsafe_from_entry_str(JSON_TEST_CREATE_AC2); + let ev2 = entry_init!( + ("class", Value::new_class("account")), + ("notallowed", Value::new_class("notallowed")), + ("name", Value::new_iname("testperson1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)) + ); + let r2_set = vec![ev2.clone()]; - let ev3: Entry = Entry::unsafe_from_entry_str(JSON_TEST_CREATE_AC3); + let ev3 = entry_init!( + ("class", Value::new_class("account")), + ("class", Value::new_class("notallowed")), + ("name", Value::new_iname("testperson1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)) + ); let r3_set = vec![ev3.clone()]; - let ev4: Entry = Entry::unsafe_from_entry_str(JSON_TEST_CREATE_AC4); + let ev4 = entry_init!( + ("class", Value::new_class("account")), + ("class", Value::new_class("group")), + ("name", Value::new_iname("testperson1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)) + ); let r4_set = vec![ev4.clone()]; // In this case, we can make the create event with an empty entry @@ -2634,14 +2566,18 @@ mod tests { // // In the realy server code, the entry set is derived from and checked // against the create event, so we have some level of trust in it. - let ce_admin = unsafe { CreateEvent::new_impersonate_entry_ser(JSON_ADMIN_V1, vec![]) }; + + let ce_admin = CreateEvent::new_impersonate_identity( + Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()), + vec![], + ); let acp = unsafe { AccessControlCreate::from_raw( "test_create", - "87bfe9b8-7600-431e-a492-1dde64bbc453", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To create matching filter testperson // Can this be empty? filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), @@ -2655,9 +2591,9 @@ mod tests { let acp2 = unsafe { AccessControlCreate::from_raw( "test_create_2", - "87bfe9b8-7600-431e-a492-1dde64bbc454", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To create matching filter testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // classes @@ -2679,10 +2615,14 @@ mod tests { #[test] fn test_access_enforce_scope_create() { - let ev1: Entry = Entry::unsafe_from_entry_str(JSON_TEST_CREATE_AC1); + let ev1 = entry_init!( + ("class", Value::new_class("account")), + ("name", Value::new_iname("testperson1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT_1)) + ); let r1_set = vec![ev1.clone()]; - let admin = Arc::new(unsafe { E_ADMIN_V1.clone().into_sealed_committed() }); + let admin = E_TEST_ACCOUNT_1.clone(); let ce_admin_io = CreateEvent::new_impersonate_identity( Identity::from_impersonate_entry_identityonly(admin.clone()), @@ -2702,9 +2642,9 @@ mod tests { let acp = unsafe { AccessControlCreate::from_raw( "test_create", - "87bfe9b8-7600-431e-a492-1dde64bbc453", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To create matching filter testperson // Can this be empty? filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), @@ -2751,15 +2691,15 @@ mod tests { let r_set = vec![Arc::new(ev1.clone())]; let de_admin = unsafe { - DeleteEvent::new_impersonate_entry_ser( - JSON_ADMIN_V1, + DeleteEvent::new_impersonate_entry( + E_TEST_ACCOUNT_1.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), ) }; let de_anon = unsafe { - DeleteEvent::new_impersonate_entry_ser( - JSON_ANONYMOUS_V1, + DeleteEvent::new_impersonate_entry( + E_TEST_ACCOUNT_2.clone(), filter_all!(f_eq("name", PartialValue::new_iname("testperson1"))), ) }; @@ -2767,9 +2707,9 @@ mod tests { let acp = unsafe { AccessControlDelete::from_raw( "test_delete", - "87bfe9b8-7600-431e-a492-1dde64bbc453", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To delete testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), ) @@ -2786,7 +2726,7 @@ mod tests { let ev1 = unsafe { E_TESTPERSON_1.clone().into_sealed_committed() }; let r_set = vec![Arc::new(ev1.clone())]; - let admin = Arc::new(unsafe { E_ADMIN_V1.clone().into_sealed_committed() }); + let admin = E_TEST_ACCOUNT_1.clone(); let de_admin_io = DeleteEvent::new_impersonate_identity( Identity::from_impersonate_entry_identityonly(admin.clone()), @@ -2806,9 +2746,9 @@ mod tests { let acp = unsafe { AccessControlDelete::from_raw( "test_delete", - "87bfe9b8-7600-431e-a492-1dde64bbc453", + Uuid::new_v4(), // Apply to admin - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // To delete testperson filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), ) @@ -2853,11 +2793,7 @@ mod tests { fn test_access_effective_permission_check_1() { let _ = sketching::test_init(); - let admin = unsafe { - Identity::from_impersonate_entry_readwrite(Arc::new( - E_ADMIN_V1.clone().into_sealed_committed(), - )) - }; + let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()); let e1: Entry = Entry::unsafe_from_entry_str(JSON_TESTPERSON1); let ev1 = unsafe { e1.into_sealed_committed() }; @@ -2870,9 +2806,9 @@ mod tests { vec![unsafe { AccessControlSearch::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to admin only - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // Allow admin to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // They can read "name". @@ -2895,11 +2831,7 @@ mod tests { fn test_access_effective_permission_check_2() { let _ = sketching::test_init(); - let admin = unsafe { - Identity::from_impersonate_entry_readwrite(Arc::new( - E_ADMIN_V1.clone().into_sealed_committed(), - )) - }; + let admin = Identity::from_impersonate_entry_readwrite(E_TEST_ACCOUNT_1.clone()); let e1: Entry = Entry::unsafe_from_entry_str(JSON_TESTPERSON1); let ev1 = unsafe { e1.into_sealed_committed() }; @@ -2913,9 +2845,9 @@ mod tests { vec![unsafe { AccessControlModify::from_raw( "test_acp", - "d38640c4-0254-49f9-99b7-8ba7d0233f3d", + Uuid::new_v4(), // apply to admin only - filter_valid!(f_eq("name", PartialValue::new_iname("admin"))), + UUID_TEST_GROUP_1, // Allow admin to read only testperson1 filter_valid!(f_eq("name", PartialValue::new_iname("testperson1"))), // They can read "name". diff --git a/kanidmd/lib/src/be/idl_sqlite.rs b/kanidmd/lib/src/be/idl_sqlite.rs index dfc2b7189..bce077576 100644 --- a/kanidmd/lib/src/be/idl_sqlite.rs +++ b/kanidmd/lib/src/be/idl_sqlite.rs @@ -229,10 +229,9 @@ pub trait IdlSqliteTransaction { idx_key: &str, ) -> Result, OperationError> { if !(self.exists_idx(attr, itype)?) { - filter_error!( + debug!( "IdlSqliteTransaction: Index {:?} {:?} not found", - itype, - attr + itype, attr ); return Ok(None); } diff --git a/kanidmd/lib/src/be/mod.rs b/kanidmd/lib/src/be/mod.rs index 0059697b9..0251330e5 100644 --- a/kanidmd/lib/src/be/mod.rs +++ b/kanidmd/lib/src/be/mod.rs @@ -1264,7 +1264,7 @@ impl<'a> BackendWriteTransaction<'a> { idlayer.write_idl(attr, itype, &idx_key, &idl) } None => { - admin_error!( + warn!( "WARNING: index {:?} {:?} was not found. YOU MUST REINDEX YOUR DATABASE", attr, itype ); @@ -1280,7 +1280,7 @@ impl<'a> BackendWriteTransaction<'a> { idlayer.write_idl(attr, itype, &idx_key, &idl) } None => { - admin_error!( + warn!( "WARNING: index {:?} {:?} was not found. YOU MUST REINDEX YOUR DATABASE", attr, itype ); diff --git a/kanidmd/lib/src/constants/acp.rs b/kanidmd/lib/src/constants/acp.rs index 2f370da2b..4ab34a4f7 100644 --- a/kanidmd/lib/src/constants/acp.rs +++ b/kanidmd/lib/src/constants/acp.rs @@ -48,9 +48,8 @@ pub const JSON_IDM_ADMINS_ACP_RECYCLE_SEARCH_V1: &str = r#"{ "name": ["idm_admins_acp_recycle_search"], "uuid": ["00000000-0000-0000-0000-ffffff000002"], "description": ["Builtin IDM admin recycle bin search permission."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000019\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000019"], "acp_targetscope": [ "{\"eq\": [\"class\", \"recycled\"]}" ], @@ -64,9 +63,8 @@ pub const JSON_IDM_ADMINS_ACP_REVIVE_V1: &str = r#"{ "name": ["idm_admins_acp_revive"], "uuid": ["00000000-0000-0000-0000-ffffff000003"], "description": ["Builtin IDM Administrators Access Controls."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000019\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000019"], "acp_targetscope": [ "{\"eq\":[\"class\",\"recycled\"]}" ], @@ -81,9 +79,8 @@ pub const JSON_IDM_SELF_ACP_READ_V1: &str = r#"{ "name": ["idm_self_acp_read"], "uuid": ["00000000-0000-0000-0000-ffffff000004"], "description": ["Builtin IDM Control for self read - required for whoami and many other functions."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000036\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000036"], "acp_targetscope": [ "\"self\"" ], @@ -114,9 +111,8 @@ pub const JSON_IDM_SELF_ACP_WRITE_V1: &str = r#"{ "name": ["idm_self_acp_write"], "uuid": ["00000000-0000-0000-0000-ffffff000021"], "description": ["Builtin IDM Control for self write - required for people to update their own identities and credentials in line with best practices."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000035\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000035"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"class\",\"account\"]}, \"self\"]}" ], @@ -135,9 +131,8 @@ pub const JSON_IDM_PEOPLE_SELF_ACP_WRITE_MAIL_PRIV_V1: &str = r#"{ "name": ["idm_people_self_acp_write_mail"], "uuid": ["00000000-0000-0000-0000-ffffff000041"], "description": ["Builtin IDM Control for self write of mail for people accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000033\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000033"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"class\",\"account\"]}, \"self\"]}" ], @@ -156,9 +151,8 @@ pub const JSON_IDM_ALL_ACP_READ_V1: &str = r#"{ "name": ["idm_all_acp_read"], "uuid": ["00000000-0000-0000-0000-ffffff000006"], "description": ["Builtin IDM Control for all read - IE anonymous and all authenticated accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000036\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000036"], "acp_targetscope": [ "{\"and\": [{\"pres\": \"class\"}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -188,9 +182,8 @@ pub const JSON_IDM_ACP_PEOPLE_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_people_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000007"], "description": ["Builtin IDM Control for reading personal sensitive data."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000002\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000002"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -210,9 +203,8 @@ pub const JSON_IDM_ACP_PEOPLE_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_people_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000008"], "description": ["Builtin IDM Control for managing personal and sensitive data."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000003\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000003"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -236,9 +228,8 @@ pub const JSON_IDM_ACP_PEOPLE_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_people_manage"], "uuid": ["00000000-0000-0000-0000-ffffff000013"], "description": ["Builtin IDM Control for creating person (user) accounts"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000013\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000013"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"eq\": [\"class\",\"person\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -273,9 +264,8 @@ pub const JSON_IDM_ACP_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1: &str = r#"{ "name": ["idm_acp_people_account_password_import_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000031"], "description": ["Builtin IDM Control for allowing imports of passwords to people+account types."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000023\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000023"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -299,9 +289,8 @@ pub const JSON_IDM_ACP_PEOPLE_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_people_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000032"], "description": ["Builtin IDM Control for allowing person class extension"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000024\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000024"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -326,9 +315,8 @@ pub const JSON_IDM_ACP_HP_PEOPLE_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_people_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000036"], "description": ["Builtin IDM Control for reading high privilege personal sensitive data."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000028\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000028"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -348,9 +336,8 @@ pub const JSON_IDM_ACP_HP_PEOPLE_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_people_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000037"], "description": ["Builtin IDM Control for managing privilege personal and sensitive data."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000029\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000029"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"person\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -373,9 +360,8 @@ pub const JSON_IDM_ACP_HP_PEOPLE_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_people_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000038"], "description": ["Builtin IDM Control for allowing privilege person class extension"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000030\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000030"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -403,9 +389,8 @@ pub const JSON_IDM_ACP_GROUP_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_group_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000009"], "description": ["Builtin IDM Control for managing groups"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000004\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000004"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -431,9 +416,8 @@ pub const JSON_IDM_ACP_ACCOUNT_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_account_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000010"], "description": ["Builtin IDM Control for accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000005\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000005"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -453,9 +437,8 @@ pub const JSON_IDM_ACP_ACCOUNT_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_account_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000011"], "description": ["Builtin IDM Control for managing all accounts (both person and service)."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000006\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000006"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -479,9 +462,8 @@ pub const JSON_IDM_ACP_ACCOUNT_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_account_manage"], "uuid": ["00000000-0000-0000-0000-ffffff000012"], "description": ["Builtin IDM Control for creating and deleting (service) accounts"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000014\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000014"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -518,9 +500,8 @@ pub const JSON_IDM_ACP_RADIUS_SECRET_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_radius_secret_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000039"], "description": ["Builtin IDM Control for reading radius secrets of accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000032\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000032"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -540,9 +521,8 @@ pub const JSON_IDM_ACP_RADIUS_SECRET_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_radius_secret_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000040"], "description": ["Builtin IDM Control allowing writes to user radius secrets."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000031\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000031"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -565,9 +545,8 @@ pub const JSON_IDM_ACP_RADIUS_SERVERS_V1: &str = r#"{ "name": ["idm_acp_radius_servers"], "uuid": ["00000000-0000-0000-0000-ffffff000014"], "description": ["Builtin IDM Control for RADIUS servers to read credentials and other needed details."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000007\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000007"], "acp_targetscope": [ "{\"and\": [{\"pres\": \"class\"}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -588,9 +567,8 @@ pub const JSON_IDM_ACP_HP_ACCOUNT_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_account_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000015"], "description": ["Builtin IDM Control for reading high privilege accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000009\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000009"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -610,9 +588,8 @@ pub const JSON_IDM_ACP_HP_ACCOUNT_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_account_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000016"], "description": ["Builtin IDM Control for managing high privilege accounts (both person and service)."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000009\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000009"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -637,9 +614,8 @@ pub const JSON_IDM_ACP_HP_GROUP_WRITE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_group_write_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000017"], "description": ["Builtin IDM Control for managing high privilege groups"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000012\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000012"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -668,9 +644,8 @@ pub const JSON_IDM_ACP_SCHEMA_WRITE_ATTRS_PRIV_V1: &str = r#"{ "name": ["idm_acp_schema_write_attrs_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000018"], "description": ["Builtin IDM Control for management of schema attributes."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000010\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000010"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"attributetype\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -729,9 +704,8 @@ pub const JSON_IDM_ACP_ACP_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_acp_manage_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000019"], "description": ["Builtin IDM Control for access profiles management."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000011\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000011"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -740,7 +714,7 @@ pub const JSON_IDM_ACP_ACP_MANAGE_PRIV_V1: &str = r#"{ "class", "description", "acp_enable", - "acp_receiver", + "acp_receiver_group", "acp_targetscope", "acp_search_attr", "acp_modify_removedattr", @@ -754,7 +728,7 @@ pub const JSON_IDM_ACP_ACP_MANAGE_PRIV_V1: &str = r#"{ "class", "description", "acp_enable", - "acp_receiver", + "acp_receiver_group", "acp_targetscope", "acp_search_attr", "acp_modify_removedattr", @@ -768,7 +742,7 @@ pub const JSON_IDM_ACP_ACP_MANAGE_PRIV_V1: &str = r#"{ "class", "description", "acp_enable", - "acp_receiver", + "acp_receiver_group", "acp_targetscope", "acp_search_attr", "acp_modify_removedattr", @@ -789,7 +763,7 @@ pub const JSON_IDM_ACP_ACP_MANAGE_PRIV_V1: &str = r#"{ "class", "description", "acp_enable", - "acp_receiver", + "acp_receiver_group", "acp_targetscope", "acp_search_attr", "acp_modify_removedattr", @@ -820,9 +794,8 @@ pub const JSON_IDM_ACP_SCHEMA_WRITE_CLASSES_PRIV_V1: &str = r#"{ "name": ["idm_acp_schema_write_classes_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000020"], "description": ["Builtin IDM Control for management of schema classes."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000010\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000010"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"classtype\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -877,9 +850,8 @@ pub const JSON_IDM_ACP_GROUP_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_group_manage"], "uuid": ["00000000-0000-0000-0000-ffffff000022"], "description": ["Builtin IDM Control for creating and deleting groups in the directory"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000015\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000015"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -907,9 +879,8 @@ pub const JSON_IDM_ACP_HP_ACCOUNT_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_account_manage"], "uuid": ["00000000-0000-0000-0000-ffffff000023"], "description": ["Builtin IDM Control for creating and deleting hp and regular (service) accounts"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000016\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000016"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -943,9 +914,8 @@ pub const JSON_IDM_ACP_HP_GROUP_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_group_manage"], "uuid": ["00000000-0000-0000-0000-ffffff000024"], "description": ["Builtin IDM Control for creating and deleting hp and regular groups in the directory"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000017\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000017"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -973,9 +943,8 @@ pub const JSON_IDM_ACP_DOMAIN_ADMIN_PRIV_V1: &str = r#"{ "name": ["idm_acp_domain_admin_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000026"], "description": ["Builtin IDM Control for granting domain info administration locally"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000020\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000020"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"uuid\",\"00000000-0000-0000-0000-ffffff000025\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1014,9 +983,8 @@ pub const JSON_IDM_ACP_SYSTEM_CONFIG_PRIV_V1: &str = r#"{ "name": ["idm_acp_system_config_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000028"], "description": ["Builtin IDM Control for granting system configuration rights"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000019\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000019"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"uuid\",\"00000000-0000-0000-0000-ffffff000027\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1047,9 +1015,8 @@ pub const JSON_IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_account_unix_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000029"], "description": ["Builtin IDM Control for managing accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000021\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000021"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1077,9 +1044,8 @@ pub const JSON_IDM_ACP_GROUP_UNIX_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_group_unix_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000030"], "description": ["Builtin IDM Control for managing and extending unix groups"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000022\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000022"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1108,9 +1074,8 @@ pub const JSON_IDM_HP_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_account_unix_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000033"], "description": ["Builtin IDM Control for managing and extending unix high privilege accounts."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000025\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000025"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1138,9 +1103,8 @@ pub const JSON_IDM_HP_ACP_GROUP_UNIX_EXTEND_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_group_unix_extend_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000034"], "description": ["Builtin IDM Control for managing and extending unix high privilege groups"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000026\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000026"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"group\"]}, {\"eq\": [\"memberof\",\"00000000-0000-0000-0000-000000001000\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1171,9 +1135,8 @@ pub const JSON_IDM_HP_ACP_OAUTH2_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_oauth2_manage_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000035"], "description": ["Builtin IDM Control for managing oauth2 resource server integrations."], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000027\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000027"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"oauth2_resource_server\"]},{\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1247,9 +1210,8 @@ pub const JSON_IDM_HP_ACP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_V1: &str = r#"{ "name": ["idm_hp_acp_service_account_into_person_migrate"], "uuid": ["00000000-0000-0000-0000-ffffff000042"], "description": ["Builtin IDM Control allowing service accounts to be migrated into persons"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000034\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000034"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"account\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1276,9 +1238,8 @@ pub const JSON_IDM_ACP_OAUTH2_READ_PRIV_V1: &str = r#"{ "name": ["idm_acp_oauth2_read_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000043"], "description": ["Builtin IDM Control allowing persons to view oauth2 applications they can access"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000035\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000035"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"oauth2_resource_server\"]},{\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], @@ -1304,9 +1265,8 @@ pub const JSON_IDM_HP_ACP_SYNC_ACCOUNT_MANAGE_PRIV_V1: &str = r#"{ "name": ["idm_acp_hp_sync_account_manage_priv"], "uuid": ["00000000-0000-0000-0000-ffffff000044"], "description": ["Builtin IDM Control for managing IDM synchronisation accounts / connections"], - "acp_receiver": [ - "{\"eq\":[\"memberof\",\"00000000-0000-0000-0000-000000000037\"]}" - ], + "acp_receiver": [], + "acp_receiver_group": ["00000000-0000-0000-0000-000000000037"], "acp_targetscope": [ "{\"and\": [{\"eq\": [\"class\",\"sync_account\"]},{\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}" ], diff --git a/kanidmd/lib/src/constants/uuids.rs b/kanidmd/lib/src/constants/uuids.rs index 945c1a8ae..4a7663b88 100644 --- a/kanidmd/lib/src/constants/uuids.rs +++ b/kanidmd/lib/src/constants/uuids.rs @@ -23,6 +23,8 @@ pub const _UUID_IDM_GROUP_MANAGE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000 pub const _UUID_IDM_HP_ACCOUNT_MANAGE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000016"); pub const _UUID_IDM_HP_GROUP_MANAGE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000017"); pub const UUID_IDM_ADMIN: Uuid = uuid!("00000000-0000-0000-0000-000000000018"); + +pub const STR_UUID_SYSTEM_ADMINS: &str = "00000000-0000-0000-0000-000000000000"; pub const UUID_SYSTEM_ADMINS: Uuid = uuid!("00000000-0000-0000-0000-000000000019"); pub const UUID_DOMAIN_ADMINS: Uuid = uuid!("00000000-0000-0000-0000-000000000020"); pub const _UUID_IDM_ACCOUNT_UNIX_EXTEND_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000021"); @@ -48,6 +50,7 @@ pub const _UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000034"); pub const UUID_IDM_ALL_PERSONS: Uuid = uuid!("00000000-0000-0000-0000-000000000035"); +pub const STR_UUID_IDM_ALL_ACCOUNTS: &str = "00000000-0000-0000-0000-000000000036"; pub const UUID_IDM_ALL_ACCOUNTS: Uuid = uuid!("00000000-0000-0000-0000-000000000036"); pub const _UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000037"); @@ -202,6 +205,7 @@ pub const _UUID_SCHEMA_ATTR_SYNC_TOKEN_SESSION: Uuid = uuid!("00000000-0000-0000-0000-ffff00000115"); pub const _UUID_SCHEMA_ATTR_SYNC_COOKIE: Uuid = uuid!("00000000-0000-0000-0000-ffff00000116"); pub const _UUID_SCHEMA_ATTR_OAUTH2_SESSION: Uuid = uuid!("00000000-0000-0000-0000-ffff00000117"); +pub const UUID_SCHEMA_ATTR_ACP_RECEIVER_GROUP: Uuid = uuid!("00000000-0000-0000-0000-ffff00000118"); // System and domain infos // I'd like to strongly criticise william of the past for making poor choices about these allocations. diff --git a/kanidmd/lib/src/entry.rs b/kanidmd/lib/src/entry.rs index 5ca22d921..4373182d3 100644 --- a/kanidmd/lib/src/entry.rs +++ b/kanidmd/lib/src/entry.rs @@ -390,7 +390,7 @@ impl Entry { ) ) } - "member" | "memberof" | "directmemberof" => { + "member" | "memberof" | "directmemberof" | "acp_receiver_group" => { valueset::from_value_iter( vs.into_iter().map(|v| Value::new_refer_s(v.as_str()).unwrap() ) ) diff --git a/kanidmd/lib/src/idm/oauth2.rs b/kanidmd/lib/src/idm/oauth2.rs index 003194d29..80af6df04 100644 --- a/kanidmd/lib/src/idm/oauth2.rs +++ b/kanidmd/lib/src/idm/oauth2.rs @@ -2385,9 +2385,7 @@ mod tests { // Process it to ensure the record exists. let mut idms_prox_write = task::block_on(idms.proxy_write(ct)); - assert!(idms_prox_write - .process_oauth2sessionrecord(&osr) - .is_ok()); + assert!(idms_prox_write.process_oauth2sessionrecord(&osr).is_ok()); assert!(idms_prox_write.commit().is_ok()); } @@ -2561,9 +2559,7 @@ mod tests { // Assert that the session creation was submitted let session_id = match idms_delayed.async_rx.blocking_recv() { Some(DelayedAction::Oauth2SessionRecord(osr)) => { - assert!(idms_prox_write - .process_oauth2sessionrecord(&osr) - .is_ok()); + assert!(idms_prox_write.process_oauth2sessionrecord(&osr).is_ok()); osr.session_id } _ => { diff --git a/kanidmd/lib/src/idm/server.rs b/kanidmd/lib/src/idm/server.rs index 8ac1774ed..feb8bcaa1 100644 --- a/kanidmd/lib/src/idm/server.rs +++ b/kanidmd/lib/src/idm/server.rs @@ -2205,10 +2205,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> { info!(session_id = %osr.session_id, "Persisting auth session"); // modify the account to put the session onto it. - let modlist = ModifyList::new_append( - "oauth2_session", - session, - ); + let modlist = ModifyList::new_append("oauth2_session", session); self.qs_write .internal_modify( diff --git a/kanidmd/lib/src/macros.rs b/kanidmd/lib/src/macros.rs index 96aa1281f..7f7b0c533 100644 --- a/kanidmd/lib/src/macros.rs +++ b/kanidmd/lib/src/macros.rs @@ -135,9 +135,10 @@ macro_rules! run_create_test { let ce = match $internal { None => CreateEvent::new_internal($create_entries.clone()), - Some(e_str) => unsafe { - CreateEvent::new_impersonate_entry_ser(e_str, $create_entries.clone()) - }, + Some(ent) => CreateEvent::new_impersonate_identity( + Identity::from_impersonate_entry_readwrite(ent), + $create_entries.clone(), + ), }; { @@ -190,8 +191,8 @@ macro_rules! run_modify_test { let me = match $internal { None => unsafe { ModifyEvent::new_internal_invalid($modify_filter, $modify_list) }, - Some(e_str) => unsafe { - ModifyEvent::new_impersonate_entry_ser(e_str, $modify_filter, $modify_list) + Some(ent) => unsafe { + ModifyEvent::new_impersonate_entry(ent, $modify_filter, $modify_list) }, }; @@ -237,9 +238,7 @@ macro_rules! run_delete_test { let qs = setup_test!($preload_entries); let de = match $internal { - Some(e_str) => unsafe { - DeleteEvent::new_impersonate_entry_ser(e_str, $delete_filter.clone()) - }, + Some(ent) => unsafe { DeleteEvent::new_impersonate_entry(ent, $delete_filter.clone()) }, None => unsafe { DeleteEvent::new_internal_invalid($delete_filter.clone()) }, }; diff --git a/kanidmd/lib/src/plugins/access.rs b/kanidmd/lib/src/plugins/access.rs new file mode 100644 index 000000000..f22fd1871 --- /dev/null +++ b/kanidmd/lib/src/plugins/access.rs @@ -0,0 +1,63 @@ + +// == ⚠️ Template, not used yet. + + +//! This plugin is responsible for pre-extraction of access related elements onto +//! entries. This is a "trade" where we sacrifice time in the write path to pre-calculate +//! a number of access related elements, and we benefit in read/write paths due to +//! optimised application of access controls. +//! +//! Additionally, this also extracts and applies a number of access adjacent elements +//! to accounts - An example being UI hints that are tied in with the ability to +//! perform an action in the webui. + + +pub struct AccessExtract {} + +impl Plugin for AccessExtract { + fn id() -> &'static str { + "plugin_session_consistency" + } + + #[instrument( + level = "debug", + name = "accessextract_pre_create_transform", + skip_all + )] + fn pre_create_transform( + _qs: &mut QueryServerWriteTransaction, + _cand: &mut Vec>, + _ce: &CreateEvent, + ) -> Result<(), OperationError> { + } + + #[instrument(level = "debug", name = "accessextract_pre_modify", skip(_qs, cand, _me))] + fn pre_modify( + _qs: &mut QueryServerWriteTransaction, + _cand: &mut Vec>, + _me: &ModifyEvent, + ) -> Result<(), OperationError> { + } + + #[instrument(level = "debug", name = "accessextract_pre_delete", skip(_qs, cand, de))] + fn pre_delete( + _qs: &mut QueryServerWriteTransaction, + // Should these be EntrySealed + _cand: &mut Vec>, + _de: &DeleteEvent, + ) -> Result<(), OperationError> { + // Clear all extracted values. + } +} + +// This is outside the normal plugin interface, but when access controls are reloaded, we +// re-run to update the needed attributes on entries. +impl AccessExtract { +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + +} + diff --git a/kanidmd/lib/src/plugins/base.rs b/kanidmd/lib/src/plugins/base.rs index 5bb8c8ea3..6acb638b6 100644 --- a/kanidmd/lib/src/plugins/base.rs +++ b/kanidmd/lib/src/plugins/base.rs @@ -208,38 +208,70 @@ impl Plugin for Base { #[cfg(test)] mod tests { - use kanidm_proto::v1::PluginError; - use crate::prelude::*; + use kanidm_proto::v1::PluginError; + use std::sync::Arc; - const JSON_ADMIN_ALLOW_ALL: &'static str = r#"{ - "attrs": { - "class": [ - "object", - "access_control_profile", - "access_control_modify", - "access_control_create", - "access_control_delete", - "access_control_search" - ], - "name": ["idm_admins_acp_allow_all_test"], - "uuid": ["bb18f746-a409-497d-928c-5455d4aef4f7"], - "description": ["Builtin IDM Administrators Access Controls."], - "acp_enable": ["true"], - "acp_receiver": [ - "{\"eq\":[\"uuid\",\"00000000-0000-0000-0000-000000000000\"]}" - ], - "acp_targetscope": [ - "{\"pres\":\"class\"}" - ], - "acp_search_attr": ["name", "class", "uuid"], - "acp_modify_class": ["system"], - "acp_modify_removedattr": ["class", "displayname", "may", "must"], - "acp_modify_presentattr": ["class", "displayname", "may", "must"], - "acp_create_class": ["object", "person", "system"], - "acp_create_attr": ["name", "class", "description", "displayname", "uuid"] - } - }"#; + const UUID_TEST_ACCOUNT: Uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"); + const UUID_TEST_GROUP: Uuid = uuid::uuid!("81ec1640-3637-4a2f-8a52-874fa3c3c92f"); + const UUID_TEST_ACP: Uuid = uuid::uuid!("acae81d6-5ea7-4bd8-8f7f-fcec4c0dd647"); + + lazy_static! { + pub static ref TEST_ACCOUNT: EntryInitNew = entry_init!( + ("class", Value::new_class("account")), + ("class", Value::new_class("service_account")), + ("class", Value::new_class("memberof")), + ("name", Value::new_iname("test_account_1")), + ("displayname", Value::new_utf8s("test_account_1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT)), + ("memberof", Value::new_refer(UUID_TEST_GROUP)) + ); + pub static ref TEST_GROUP: EntryInitNew = entry_init!( + ("class", Value::new_class("group")), + ("name", Value::new_iname("test_group_a")), + ("uuid", Value::new_uuid(UUID_TEST_GROUP)), + ("member", Value::new_refer(UUID_TEST_ACCOUNT)) + ); + pub static ref ALLOW_ALL: EntryInitNew = entry_init!( + ("class", Value::new_class("object")), + ("class", Value::new_class("access_control_profile")), + ("class", Value::new_class("access_control_modify")), + ("class", Value::new_class("access_control_create")), + ("class", Value::new_class("access_control_delete")), + ("class", Value::new_class("access_control_search")), + ("name", Value::new_iname("idm_admins_acp_allow_all_test")), + ("uuid", Value::new_uuid(UUID_TEST_ACP)), + ("acp_receiver_group", Value::Refer(UUID_TEST_GROUP)), + ( + "acp_targetscope", + Value::new_json_filter_s("{\"pres\":\"class\"}").expect("filter") + ), + ("acp_search_attr", Value::new_iutf8("name")), + ("acp_search_attr", Value::new_iutf8("class")), + ("acp_search_attr", Value::new_iutf8("uuid")), + ("acp_modify_class", Value::new_iutf8("system")), + ("acp_modify_removedattr", Value::new_iutf8("class")), + ("acp_modify_removedattr", Value::new_iutf8("displayname")), + ("acp_modify_removedattr", Value::new_iutf8("may")), + ("acp_modify_removedattr", Value::new_iutf8("must")), + ("acp_modify_presentattr", Value::new_iutf8("class")), + ("acp_modify_presentattr", Value::new_iutf8("displayname")), + ("acp_modify_presentattr", Value::new_iutf8("may")), + ("acp_modify_presentattr", Value::new_iutf8("must")), + ("acp_create_class", Value::new_iutf8("object")), + ("acp_create_class", Value::new_iutf8("person")), + ("acp_create_class", Value::new_iutf8("system")), + ("acp_create_attr", Value::new_iutf8("name")), + ("acp_create_attr", Value::new_iutf8("class")), + ("acp_create_attr", Value::new_iutf8("description")), + ("acp_create_attr", Value::new_iutf8("displayname")), + ("acp_create_attr", Value::new_iutf8("uuid")) + ); + pub static ref PRELOAD: Vec = + vec![TEST_ACCOUNT.clone(), TEST_GROUP.clone(), ALLOW_ALL.clone()]; + pub static ref E_TEST_ACCOUNT: Arc = + Arc::new(unsafe { TEST_ACCOUNT.clone().into_sealed_committed() }); + } // check create where no uuid #[test] @@ -572,9 +604,7 @@ mod tests { // Test an external create, it should fail. // Testing internal create is not super needed, due to migrations at start // up testing this every time we run :P - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); - - let preload = vec![acp]; + let preload = PRELOAD.clone(); let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -596,7 +626,7 @@ mod tests { ))), preload, create, - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } @@ -606,9 +636,7 @@ mod tests { // Test an external create, it should fail. // Testing internal create is not super needed, due to migrations at start // up testing this every time we run :P - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); - - let preload = vec![acp]; + let preload = PRELOAD.clone(); let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -630,7 +658,7 @@ mod tests { ))), preload, create, - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } diff --git a/kanidmd/lib/src/plugins/protected.rs b/kanidmd/lib/src/plugins/protected.rs index 6e6233371..e3b4577d4 100644 --- a/kanidmd/lib/src/plugins/protected.rs +++ b/kanidmd/lib/src/plugins/protected.rs @@ -168,49 +168,116 @@ impl Plugin for Protected { #[cfg(test)] mod tests { use crate::prelude::*; + use std::sync::Arc; - const JSON_ADMIN_ALLOW_ALL: &'static str = r#"{ - "attrs": { - "class": [ - "object", - "access_control_profile", - "access_control_modify", - "access_control_create", - "access_control_delete", - "access_control_search" - ], - "name": ["idm_admins_acp_allow_all_test"], - "uuid": ["bb18f746-a409-497d-928c-5455d4aef4f7"], - "description": ["Builtin IDM Administrators Access Controls for TESTING."], - "acp_enable": ["true"], - "acp_receiver": [ - "{\"eq\":[\"uuid\",\"00000000-0000-0000-0000-000000000000\"]}" - ], - "acp_targetscope": [ - "{\"pres\":\"class\"}" - ], - "acp_search_attr": ["name", "class", "uuid", "classname", "attributename"], - "acp_modify_class": ["system", "domain_info"], - "acp_modify_removedattr": [ - "class", "displayname", "may", "must", "domain_name", "domain_display_name", "domain_uuid", "domain_ssid", "fernet_private_key_str", "es256_private_key_der" - ], - "acp_modify_presentattr": [ - "class", "displayname", "may", "must", "domain_name", "domain_display_name", "domain_uuid", "domain_ssid", "fernet_private_key_str", "es256_private_key_der" - ], - "acp_create_class": ["object", "person", "system", "domain_info"], - "acp_create_attr": [ - "name", "class", "description", "displayname", "domain_name", "domain_display_name", "domain_uuid", "domain_ssid", "uuid", "fernet_private_key_str", "es256_private_key_der", "version" - ] - } - }"#; + const UUID_TEST_ACCOUNT: Uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"); + const UUID_TEST_GROUP: Uuid = uuid::uuid!("81ec1640-3637-4a2f-8a52-874fa3c3c92f"); + const UUID_TEST_ACP: Uuid = uuid::uuid!("acae81d6-5ea7-4bd8-8f7f-fcec4c0dd647"); + + lazy_static! { + pub static ref TEST_ACCOUNT: EntryInitNew = entry_init!( + ("class", Value::new_class("account")), + ("class", Value::new_class("service_account")), + ("class", Value::new_class("memberof")), + ("name", Value::new_iname("test_account_1")), + ("displayname", Value::new_utf8s("test_account_1")), + ("uuid", Value::new_uuid(UUID_TEST_ACCOUNT)), + ("memberof", Value::new_refer(UUID_TEST_GROUP)) + ); + pub static ref TEST_GROUP: EntryInitNew = entry_init!( + ("class", Value::new_class("group")), + ("name", Value::new_iname("test_group_a")), + ("uuid", Value::new_uuid(UUID_TEST_GROUP)), + ("member", Value::new_refer(UUID_TEST_ACCOUNT)) + ); + pub static ref ALLOW_ALL: EntryInitNew = entry_init!( + ("class", Value::new_class("object")), + ("class", Value::new_class("access_control_profile")), + ("class", Value::new_class("access_control_modify")), + ("class", Value::new_class("access_control_create")), + ("class", Value::new_class("access_control_delete")), + ("class", Value::new_class("access_control_search")), + ("name", Value::new_iname("idm_admins_acp_allow_all_test")), + ("uuid", Value::new_uuid(UUID_TEST_ACP)), + ("acp_receiver_group", Value::Refer(UUID_TEST_GROUP)), + ( + "acp_targetscope", + Value::new_json_filter_s("{\"pres\":\"class\"}").expect("filter") + ), + ("acp_search_attr", Value::new_iutf8("name")), + ("acp_search_attr", Value::new_iutf8("class")), + ("acp_search_attr", Value::new_iutf8("uuid")), + ("acp_search_attr", Value::new_iutf8("classname")), + ("acp_search_attr", Value::new_iutf8("attributename")), + ("acp_modify_class", Value::new_iutf8("system")), + ("acp_modify_class", Value::new_iutf8("domain_info")), + ("acp_modify_removedattr", Value::new_iutf8("class")), + ("acp_modify_removedattr", Value::new_iutf8("displayname")), + ("acp_modify_removedattr", Value::new_iutf8("may")), + ("acp_modify_removedattr", Value::new_iutf8("must")), + ("acp_modify_removedattr", Value::new_iutf8("domain_name")), + ( + "acp_modify_removedattr", + Value::new_iutf8("domain_display_name") + ), + ("acp_modify_removedattr", Value::new_iutf8("domain_uuid")), + ("acp_modify_removedattr", Value::new_iutf8("domain_ssid")), + ( + "acp_modify_removedattr", + Value::new_iutf8("fernet_private_key_str") + ), + ( + "acp_modify_removedattr", + Value::new_iutf8("es256_private_key_der") + ), + ("acp_modify_presentattr", Value::new_iutf8("class")), + ("acp_modify_presentattr", Value::new_iutf8("displayname")), + ("acp_modify_presentattr", Value::new_iutf8("may")), + ("acp_modify_presentattr", Value::new_iutf8("must")), + ("acp_modify_presentattr", Value::new_iutf8("domain_name")), + ( + "acp_modify_presentattr", + Value::new_iutf8("domain_display_name") + ), + ("acp_modify_presentattr", Value::new_iutf8("domain_uuid")), + ("acp_modify_presentattr", Value::new_iutf8("domain_ssid")), + ( + "acp_modify_presentattr", + Value::new_iutf8("fernet_private_key_str") + ), + ( + "acp_modify_presentattr", + Value::new_iutf8("es256_private_key_der") + ), + ("acp_create_class", Value::new_iutf8("object")), + ("acp_create_class", Value::new_iutf8("person")), + ("acp_create_class", Value::new_iutf8("system")), + ("acp_create_class", Value::new_iutf8("domain_info")), + ("acp_create_attr", Value::new_iutf8("name")), + ("acp_create_attr", Value::new_iutf8("class")), + ("acp_create_attr", Value::new_iutf8("description")), + ("acp_create_attr", Value::new_iutf8("displayname")), + ("acp_create_attr", Value::new_iutf8("domain_name")), + ("acp_create_attr", Value::new_iutf8("domain_display_name")), + ("acp_create_attr", Value::new_iutf8("domain_uuid")), + ("acp_create_attr", Value::new_iutf8("domain_ssid")), + ("acp_create_attr", Value::new_iutf8("uuid")), + ( + "acp_create_attr", + Value::new_iutf8("fernet_private_key_str") + ), + ("acp_create_attr", Value::new_iutf8("es256_private_key_der")), + ("acp_create_attr", Value::new_iutf8("version")) + ); + pub static ref PRELOAD: Vec = + vec![TEST_ACCOUNT.clone(), TEST_GROUP.clone(), ALLOW_ALL.clone()]; + pub static ref E_TEST_ACCOUNT: Arc = + Arc::new(unsafe { TEST_ACCOUNT.clone().into_sealed_committed() }); + } #[test] fn test_pre_create_deny() { // Test creating with class: system is rejected. - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); - - let preload = vec![acp]; - let e: Entry = Entry::unsafe_from_entry_str( r#"{ "attrs": { @@ -223,19 +290,19 @@ mod tests { ); let create = vec![e.clone()]; + let preload = PRELOAD.clone(); run_create_test!( Err(OperationError::SystemProtectedObject), preload, create, - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } #[test] fn test_pre_modify_system_deny() { - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); // Test modify of class to a system is denied let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -248,7 +315,8 @@ mod tests { }"#, ); - let preload = vec![acp, e.clone()]; + let mut preload = PRELOAD.clone(); + preload.push(e.clone()); run_modify_test!( Err(OperationError::SystemProtectedObject), @@ -258,7 +326,7 @@ mod tests { m_purge("displayname"), m_pres("displayname", &Value::new_utf8s("system test")), ]), - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {}, |_| {} ); @@ -266,7 +334,6 @@ mod tests { #[test] fn test_pre_modify_class_add_deny() { - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); // Show that adding a system class is denied let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -279,7 +346,8 @@ mod tests { }"#, ); - let preload = vec![acp, e.clone()]; + let mut preload = PRELOAD.clone(); + preload.push(e.clone()); run_modify_test!( Ok(()), @@ -289,7 +357,7 @@ mod tests { m_pres("may", &Value::new_iutf8("name")), m_pres("must", &Value::new_iutf8("name")), ]), - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {}, |_| {} ); @@ -297,7 +365,6 @@ mod tests { #[test] fn test_pre_delete_deny() { - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); // Test deleting with class: system is rejected. let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -310,13 +377,14 @@ mod tests { }"#, ); - let preload = vec![acp, e.clone()]; + let mut preload = PRELOAD.clone(); + preload.push(e.clone()); run_delete_test!( Err(OperationError::SystemProtectedObject), preload, filter!(f_eq("name", PartialValue::new_iname("testperson"))), - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } @@ -324,7 +392,6 @@ mod tests { #[test] fn test_modify_domain() { // Can edit *my* domain_ssid and domain_name - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); // Show that adding a system class is denied let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -344,7 +411,8 @@ mod tests { }"#, ); - let preload = vec![acp, e.clone()]; + let mut preload = PRELOAD.clone(); + preload.push(e.clone()); run_modify_test!( Ok(()), @@ -357,7 +425,7 @@ mod tests { m_purge("domain_ssid"), m_pres("domain_ssid", &Value::new_utf8s("NewExampleWifi")), ]), - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {}, |_| {} ); @@ -366,8 +434,7 @@ mod tests { #[test] fn test_ext_create_domain() { // can not add a domain_info type - note the lack of class: system - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); - let preload = vec![acp]; + let e: Entry = Entry::unsafe_from_entry_str( r#"{ "attrs": { @@ -386,19 +453,19 @@ mod tests { }"#, ); let create = vec![e]; + let preload = PRELOAD.clone(); run_create_test!( Err(OperationError::SystemProtectedObject), preload, create, - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } #[test] fn test_delete_domain() { - let acp: Entry = Entry::unsafe_from_entry_str(JSON_ADMIN_ALLOW_ALL); // On the real thing we have a class: system, but to prove the point ... let e: Entry = Entry::unsafe_from_entry_str( r#"{ @@ -418,7 +485,8 @@ mod tests { }"#, ); - let preload = vec![acp, e.clone()]; + let mut preload = PRELOAD.clone(); + preload.push(e.clone()); run_delete_test!( Err(OperationError::SystemProtectedObject), @@ -427,7 +495,7 @@ mod tests { "name", PartialValue::new_iname("domain_example.net.au") )), - Some(JSON_ADMIN_V1), + Some(E_TEST_ACCOUNT.clone()), |_| {} ); } diff --git a/kanidmd/lib/src/schema.rs b/kanidmd/lib/src/schema.rs index 70ed7a30a..f10d189c0 100644 --- a/kanidmd/lib/src/schema.rs +++ b/kanidmd/lib/src/schema.rs @@ -975,6 +975,22 @@ impl<'a> SchemaWriteTransaction<'a> { syntax: SyntaxType::JsonFilter, }, ); + self.attributes.insert( + AttrString::from("acp_receiver_group"), + SchemaAttribute { + name: AttrString::from("acp_receiver_group"), + uuid: UUID_SCHEMA_ATTR_ACP_RECEIVER_GROUP, + description: String::from( + "The group that recieves this access control to allow access", + ), + multivalue: false, + unique: false, + phantom: false, + index: vec![IndexType::Equality], + syntax: SyntaxType::ReferenceUuid, + }, + ); + self.attributes.insert( AttrString::from("acp_targetscope"), SchemaAttribute { @@ -1454,9 +1470,10 @@ impl<'a> SchemaWriteTransaction<'a> { systemmay: vec![ AttrString::from("acp_enable"), AttrString::from("description"), + AttrString::from("acp_receiver"), ], systemmust: vec![ - AttrString::from("acp_receiver"), + AttrString::from("acp_receiver_group"), AttrString::from("acp_targetscope"), AttrString::from("name"), ], diff --git a/kanidmd/lib/src/server.rs b/kanidmd/lib/src/server.rs index d6efb95ab..32d5ad7d8 100644 --- a/kanidmd/lib/src/server.rs +++ b/kanidmd/lib/src/server.rs @@ -1063,6 +1063,7 @@ impl QueryServer { } } + #[instrument(level = "info", name = "system_initialisation", skip_all)] pub async fn initialise_helper(&self, ts: Duration) -> Result<(), OperationError> { // Check our database version - attempt to do an initial indexing // based on the in memory configuration @@ -2611,6 +2612,7 @@ impl<'a> QueryServerWriteTransaction<'a> { } */ + #[instrument(level = "info", skip_all)] pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> { admin_debug!("initialise_schema_core -> start ..."); // Load in all the "core" schema, that we already have in "memory". @@ -2633,6 +2635,7 @@ impl<'a> QueryServerWriteTransaction<'a> { r } + #[instrument(level = "info", skip_all)] pub fn initialise_schema_idm(&mut self) -> Result<(), OperationError> { admin_debug!("initialise_schema_idm -> start ..."); // List of IDM schemas to init. @@ -2710,6 +2713,7 @@ impl<'a> QueryServerWriteTransaction<'a> { } // This function is idempotent + #[instrument(level = "info", skip_all)] pub fn initialise_idm(&mut self) -> Result<(), OperationError> { // First, check the system_info object. This stores some server information // and details. It's a pretty const thing. Also check anonymous, important to many @@ -3129,6 +3133,7 @@ impl<'a> QueryServerWriteTransaction<'a> { self.changed_schema.set(true); } + #[instrument(level = "info", skip_all)] pub(crate) fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> { self.be_txn.upgrade_reindex(v) } @@ -3149,6 +3154,7 @@ impl<'a> QueryServerWriteTransaction<'a> { *self.phase = phase } + #[instrument(level = "info", skip_all)] pub fn commit(mut self) -> Result<(), OperationError> { // This could be faster if we cache the set of classes changed // in an operation so we can check if we need to do the reload or not diff --git a/kanidmd/testkit/tests/default_entries.rs b/kanidmd/testkit/tests/default_entries.rs index 47fe30ab8..483b1025f 100644 --- a/kanidmd/testkit/tests/default_entries.rs +++ b/kanidmd/testkit/tests/default_entries.rs @@ -115,7 +115,7 @@ async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) -> Opti ), entry => { let new_value = match entry { - "acp_receiver" => r#"{"eq":["memberof","00000000-0000-0000-0000-000000000011"]}"#.to_string(), + "acp_receiver_group" => "00000000-0000-0000-0000-000000000011".to_string(), "acp_targetscope" => "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}".to_string(), _ => id.to_string(), }; @@ -418,7 +418,12 @@ async fn test_default_entries_rbac_admins_access_control_entries(rsclient: Kanid .await .unwrap(); - static ACP_COMMON_ATTRS: [&str; 4] = ["name", "description", "acp_receiver", "acp_targetscope"]; + static ACP_COMMON_ATTRS: [&str; 4] = [ + "name", + "description", + "acp_receiver_group", + "acp_targetscope", + ]; static ACP_ENTRIES: [&str; 28] = [ "idm_admins_acp_recycle_search", "idm_admins_acp_revive", @@ -513,7 +518,7 @@ async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient) "acp_modify_class", "acp_modify_presentattr", "acp_modify_removedattr", - "acp_receiver", + "acp_receiver_group", "acp_search_attr", "acp_targetscope", "attributename",