kanidmd_lib/server/access/
delete.rsuse 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 {
DeleteResult::Denied
} else if grant {
DeleteResult::Grant
} else {
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");
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 => {
}
};
let ident_memberof = ident.get_memberof();
let ident_uuid = ident.get_uuid();
let allow = related_acp.iter().any(|acd| {
match &acd.receiver_condition {
AccessControlReceiverCondition::GroupChecked => {
}
AccessControlReceiverCondition::EntryManager => {
if let Some(entry_manager_uuids) = entry.get_ava_refer(Attribute::EntryManagedBy) {
let group_check = ident_memberof
.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) {
return false;
}
} else {
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
);
return false;
}
}
};
let entry_name = entry.get_display_id();
security_access!(
%entry_name,
acs = %acd.acp.acp.name,
"entry matches acs"
);
true
}); 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(_) => {
if let Some(classes) = entry.get_ava_set(Attribute::Class) {
if classes.contains(&EntryClass::SyncObject.into()) {
security_access!("attempt to delete with protected class type");
return IResult::Denied;
}
};
if entry.get_uuid() <= UUID_ANONYMOUS {
security_access!("attempt to delete system builtin entry");
return IResult::Denied;
}
IResult::Ignore
}
}
}