ntdsextract2 1.4.25

Display contents of Active Directory database files (ntds.dit)
use getset::Getters;
use libntdsextract2::{CDatabase, EntryId};

use crate::walk::{
    acl_line::AclLine, attribute_line_set::AttributeLineSet, member_line::MemberLine,
};

#[derive(Getters)]
#[getset(get = "pub")]
pub struct EntryAttributes {
    attributes: Vec<AttributeLineSet>,
    sacls: Vec<AclLine>,
    dacls: Vec<AclLine>,
    member_of: Vec<MemberLine>,
    members: Vec<MemberLine>,
}

impl EntryAttributes {
    pub fn new<'info, 'db>(database: &CDatabase<'info, 'db>, entry_id: EntryId) -> Self {
        let attributes: Vec<_>;
        let sacls;
        let dacls;
        let member_of: Vec<_>;
        let members: Vec<_>;

        if let Ok(Some(entry)) = database.entry(entry_id) {
            attributes = entry
                .all_attributes()
                .iter()
                .map(|(i, a)| AttributeLineSet::from(database, i, a))
                .collect();

            match entry.security_descriptor(database.sd_table().as_ref().unwrap()) {
                Ok(sd) => {
                    sacls = sd
                        .as_ref()
                        .and_then(|sd| {
                            sd.as_ref().sacl().as_ref().map(|acl| {
                                acl.ace_list()
                                    .iter()
                                    .enumerate()
                                    .map(|(index, ace)| AclLine::from(database, index, ace))
                                    .collect()
                            })
                        })
                        .unwrap_or_default();
                    dacls = sd
                        .and_then(|sd| {
                            sd.as_ref().dacl().as_ref().map(|acl| {
                                acl.ace_list()
                                    .iter()
                                    .enumerate()
                                    .map(|(index, ace)| AclLine::from(database, index, ace))
                                    .collect()
                            })
                        })
                        .unwrap_or_default();
                }
                Err(why) => {
                    log::error!("unable to access security descriptor of {entry_id:?}: {why}");
                    sacls = vec![];
                    dacls = vec![];
                }
            }

            if let Some(ms) = database
                .link_table()
                .member_of(&entry.ds_record_id().unwrap())
            {
                member_of = ms
                    .iter()
                    .filter_map(|ptr| MemberLine::try_from(ptr, database))
                    .collect();
            } else {
                member_of = vec![];
            }

            if let Some(ms) = database
                .link_table()
                .members(&entry.ds_record_id().unwrap())
            {
                members = ms
                    .iter()
                    .filter_map(|ptr| MemberLine::try_from(ptr, database))
                    .collect();
            } else {
                members = vec![];
            }
        } else {
            log::error!("found no database entry for id {entry_id:?}");
            attributes = vec![];
            sacls = vec![];
            dacls = vec![];
            member_of = vec![];
            members = vec![];
        };

        Self {
            attributes,
            sacls,
            dacls,
            member_of,
            members,
        }
    }

    #[allow(clippy::type_complexity)]
    pub fn explode(
        self,
    ) -> (
        Vec<AttributeLineSet>,
        Vec<AclLine>,
        Vec<AclLine>,
        Vec<MemberLine>,
        Vec<MemberLine>,
    ) {
        (self.attributes, self.dacls, self.sacls, self.member_of, self.members)
    }
}