use cursive_table_view::TableViewItem;
use libntdsextract2::CDatabase;
use sddl::{
ace::header::{Flag, Flags},
AccessMask, AccessMaskFlag, Ace, Contains,
};
use strum::IntoStaticStr;
use crate::walk::acl_column::AclColumn;
#[derive(Clone, Eq, PartialEq)]
pub(crate) struct AclLine {
index: usize,
subject_name: String,
operation: AclOperation,
access_mask: AccessMask,
extended_right: Option<String>,
flags: Flags,
}
#[derive(Clone, Eq, PartialEq, IntoStaticStr)]
pub enum AclOperation {
Allowed,
Denied,
Audit,
MandatoryLabel,
ResourceAttribute,
ScopedPolicy,
}
impl AclLine {
pub fn from<'info, 'db>(database: &CDatabase<'info, 'db>, index: usize, ace: &Ace) -> Self {
let sid = ace.sid().sid();
let mut subject_name = sid.to_string();
if let Some(well_known_name) = ace.sid().well_known_name() {
subject_name = well_known_name.to_string();
} else if let Some(entry) = database
.data_table()
.data_table()
.metadata()
.entries_with_rid(*sid.get_rid())
.next()
{
if let Some(sam_account_name) = entry.sam_account_name() {
subject_name = sam_account_name.clone();
} else {
log::error!("{subject_name} has no samAccountName attribute");
}
} else {
}
let access_mask = *ace.header().mask();
let operation = match ace {
Ace::ACCESS_ALLOWED_ACE { .. } => AclOperation::Allowed,
Ace::ACCESS_ALLOWED_OBJECT_ACE { .. } => AclOperation::Allowed,
Ace::ACCESS_DENIED_ACE { .. } => AclOperation::Denied,
Ace::ACCESS_DENIED_OBJECT_ACE { .. } => AclOperation::Denied,
Ace::ACCESS_ALLOWED_CALLBACK_ACE { .. } => AclOperation::Allowed,
Ace::ACCESS_DENIED_CALLBACK_ACE { .. } => AclOperation::Denied,
Ace::ACCESS_ALLOWED_CALLBACK_OBJECT_ACE { .. } => AclOperation::Allowed,
Ace::ACCESS_DENIED_CALLBACK_OBJECT_ACE { .. } => AclOperation::Denied,
Ace::SYSTEM_AUDIT_ACE { .. } => AclOperation::Audit,
Ace::SYSTEM_AUDIT_OBJECT_ACE { .. } => AclOperation::Audit,
Ace::SYSTEM_AUDIT_CALLBACK_ACE { .. } => AclOperation::Audit,
Ace::SYSTEM_MANDATORY_LABEL_ACE { .. } => AclOperation::MandatoryLabel,
Ace::SYSTEM_AUDIT_CALLBACK_OBJECT_ACE { .. } => AclOperation::Audit,
Ace::SYSTEM_RESOURCE_ATTRIBUTE_ACE { .. } => AclOperation::ResourceAttribute,
Ace::SYSTEM_SCOPED_POLICY_ID_ACE { .. } => AclOperation::ScopedPolicy,
};
let mut extended_right = None;
if ace.header().mask().contains(AccessMaskFlag::CONTROL_ACCESS) {
if let Some(guid) = ace.object_type() {
if let Some(er) = database.data_table().extended_rights().get(guid) {
extended_right = Some(er.to_owned());
} else {
log::error!("unable to find extended right with GUID {guid}");
extended_right = Some(guid.to_string());
}
}
}
let flags = *ace.header().ace_flags();
Self {
index,
subject_name,
operation,
access_mask,
extended_right,
flags,
}
}
}
impl TableViewItem<AclColumn> for AclLine {
fn to_column(&self, column: AclColumn) -> String {
match column {
AclColumn::SubjectName => self.subject_name.clone(),
AclColumn::Operation => {
let operation: &str = (&self.operation).into();
operation.to_owned()
}
AclColumn::AccessMask => self.access_mask.to_string(),
AclColumn::ExtendedRight => self
.extended_right
.as_ref()
.map_or("".to_string(), |s| s.to_owned()),
AclColumn::ObjectInherit => if self.flags.contains(Flag::OBJECT_INHERIT_ACE) {
"✓"
} else {
""
}
.to_owned(),
AclColumn::ContainerInherit => if self.flags.contains(Flag::CONTAINER_INHERIT_ACE) {
"✓"
} else {
""
}
.to_owned(),
AclColumn::NoPropagate => if self.flags.contains(Flag::NO_PROPAGATE_INHERIT_ACE) {
"✓"
} else {
""
}
.to_owned(),
AclColumn::InheritOnly => if self.flags.contains(Flag::INHERIT_ONLY_ACE) {
"✓"
} else {
""
}
.to_owned(),
AclColumn::Inherited => if self.flags.contains(Flag::INHERITED_ACE) {
"✓"
} else {
""
}
.to_owned(),
}
}
fn cmp(&self, other: &Self, _column: AclColumn) -> std::cmp::Ordering
where
Self: Sized,
{
self.index.cmp(&other.index)
}
}