use super::profiles::{
AccessControlDeleteResolved, AccessControlReceiverCondition, AccessControlTargetCondition,
};
use crate::prelude::*;
use std::sync::Arc;
pub(super) enum DeleteResult {
Denied,
Grant,
}
enum IResult {
Denied,
Grant,
Ignore,
}
pub(super) fn apply_delete_access<'a>(
ident: &Identity,
related_acp: &'a [AccessControlDeleteResolved],
entry: &'a Arc<EntrySealedCommitted>,
) -> DeleteResult {
let mut denied = false;
let mut grant = false;
match protected_filter_entry(ident, entry) {
IResult::Denied => denied = true,
IResult::Grant | IResult::Ignore => {}
}
match delete_filter_entry(ident, related_acp, entry) {
IResult::Denied => denied = true,
IResult::Grant => grant = true,
IResult::Ignore => {}
}
if denied {
// Something explicitly said no.
DeleteResult::Denied
} else if grant {
// Something said yes
DeleteResult::Grant
} else {
// Nothing said yes.
DeleteResult::Denied
}
}
fn delete_filter_entry<'a>(
ident: &Identity,
related_acp: &'a [AccessControlDeleteResolved],
entry: &'a Arc<EntrySealedCommitted>,
) -> IResult {
match &ident.origin {
IdentType::Internal => {
trace!("Internal operation, bypassing access check");
// No need to check ACS
return IResult::Grant;
}
IdentType::Synch(_) => {
security_critical!("Blocking sync check");
return IResult::Denied;
}
IdentType::User(_) => {}
};
debug!(event = %ident, "Access check for delete event");
match ident.access_scope() {
AccessScope::ReadOnly | AccessScope::Synchronise => {
security_access!("denied ❌ - identity access scope is not permitted to delete");
return IResult::Denied;
}
AccessScope::ReadWrite => {
// As you were
}
};
let ident_memberof = ident.get_memberof();
let ident_uuid = ident.get_uuid();
let allow = related_acp.iter().any(|acd| {
// Assert that the receiver condition applies.
match &acd.receiver_condition {
AccessControlReceiverCondition::GroupChecked => {
// The groups were already checked during filter resolution. Trust
// that result, and continue.
}
AccessControlReceiverCondition::EntryManager => {
// This condition relies on the entry we are looking at to have a back-ref
// to our uuid or a group we are in as an entry manager.
// Note, while schema has this as single value, we currently
// fetch it as a multivalue btreeset for future incase we allow
// multiple entry manager by in future.
if let Some(entry_manager_uuids) = entry.get_ava_refer(Attribute::EntryManagedBy) {
let group_check = ident_memberof
// Have at least one group allowed.
.map(|imo| imo.intersection(entry_manager_uuids).next().is_some())
.unwrap_or_default();
let user_check = ident_uuid
.map(|u| entry_manager_uuids.contains(&u))
.unwrap_or_default();
if !(group_check || user_check) {
// Not the entry manager
return false;
}
} else {
// Can not satisfy.
return false;
}
}
};
match &acd.target_condition {
AccessControlTargetCondition::Scope(f_res) => {
if !entry.entry_match_no_index(f_res) {
trace!(
"entry {:?} DOES NOT match acs {}",
entry.get_uuid(),
acd.acp.acp.name
);
// Does not match, fail.
return false;
}
}
};
let entry_name = entry.get_display_id();
security_access!(
%entry_name,
acs = %acd.acp.acp.name,
"entry matches acs"
);
true
}); // any related_acp
if allow {
IResult::Grant
} else {
IResult::Ignore
}
}
fn protected_filter_entry(ident: &Identity, entry: &Arc<EntrySealedCommitted>) -> IResult {
match &ident.origin {
IdentType::Internal => {
trace!("Internal operation, protected rules do not apply.");
IResult::Ignore
}
IdentType::Synch(_) => {
security_access!("sync agreements may not directly delete entities");
IResult::Denied
}
IdentType::User(_) => {
// Now check things ...
// For now we just block create on sync object
if let Some(classes) = entry.get_ava_set(Attribute::Class) {
if classes.contains(&EntryClass::SyncObject.into()) {
// Block the mod
security_access!("attempt to delete with protected class type");
return IResult::Denied;
}
};
// Prevent deletion of entries that exist in the system controlled entry range.
if entry.get_uuid() <= UUID_ANONYMOUS {
security_access!("attempt to delete system builtin entry");
return IResult::Denied;
}
// Checks exhausted, no more input from us
IResult::Ignore
}
}
}