use hashbrown::HashMap;
use base64::prelude::*;
use crate::{
cache::{self, ColumnIndex, Record, Value, WithValue},
cli::InvalidSdAction,
win32_types::{CheckedSecurityDescriptor, SecurityDescriptor},
};
pub struct SdTable {
descriptors: HashMap<i64, Vec<u8>>,
}
impl SdTable {
pub fn new(sd_table: &cache::SdTable) -> crate::ntds::Result<Self> {
let sd_id_column = sd_table.sd_id_column();
let sd_value_column = sd_table.sd_value_column();
let descriptors: crate::ntds::Result<HashMap<i64, Vec<u8>>> = sd_table
.iter()
.map(|record| Self::descriptor_from_record(&record, sd_id_column, sd_value_column))
.collect();
Ok(Self {
descriptors: descriptors?,
})
}
fn descriptor_from_record(
record: &Record<'_, '_>,
sd_id_column: &ColumnIndex,
sd_value_column: &ColumnIndex,
) -> crate::ntds::Result<(i64, Vec<u8>)> {
Ok((
Self::sd_id_from_record(record, sd_id_column)?,
Self::sd_value_from_record(record, sd_value_column)?,
))
}
fn sd_id_from_record(
record: &Record<'_, '_>,
sd_id_column: &ColumnIndex,
) -> crate::ntds::Result<i64> {
record.with_value(*sd_id_column, |v| match v.unwrap() {
Value::I16(v) => Ok(i64::from(*v)),
Value::I32(v) => Ok(i64::from(*v)),
Value::I64(v) => Ok(*v),
Value::Currency(v) => Ok(*v),
v => unimplemented!("no support for {v} as sd_id"),
})
}
fn sd_value_from_record(
record: &Record<'_, '_>,
sd_value_column: &ColumnIndex,
) -> crate::ntds::Result<Vec<u8>> {
record.with_value(*sd_value_column, |v| match v.unwrap() {
Value::Binary(v) => Ok(v.as_ref().clone()),
v => unimplemented!("no support for {v} as sd_value"),
})
}
pub fn descriptor(
&self,
sd_id: &i64,
) -> Result<Option<SecurityDescriptor>, crate::ntds::Error> {
match self.descriptors.get(sd_id) {
Some(v) => match SecurityDescriptor::try_from(&v[..]) {
Ok(sd) => Ok(Some(sd)),
Err(why) => {
log::error!("failed descriptor was: {}", BASE64_STANDARD.encode(v));
log::error!("{why}");
Err(why)
}
},
None => Ok(None),
}
}
pub fn checked_descriptor(
&self,
sd_id: &i64,
action: &InvalidSdAction,
) -> Option<CheckedSecurityDescriptor> {
match self.descriptors.get(sd_id) {
Some(v) => match SecurityDescriptor::try_from(&v[..]) {
Ok(sd) => Some(sd.into()),
Err(why) => match action {
InvalidSdAction::Panic => panic!(
"failed descriptor was: {}\n{why}",
BASE64_STANDARD.encode(v)
),
InvalidSdAction::Hide => Some(CheckedSecurityDescriptor::Hide),
InvalidSdAction::ShowError => Some(CheckedSecurityDescriptor::ShowError(
format!("{why}"),
)),
},
},
None => None,
}
}
pub fn descriptors(
&self,
) -> impl Iterator<Item = (i64, Result<SecurityDescriptor, crate::ntds::Error>)> + '_ {
self.descriptors.iter().map(|(id, bin_sd)| {
let sd = match SecurityDescriptor::try_from(&bin_sd[..]) {
Ok(sd) => Ok(sd),
Err(why) => {
log::error!("failed descriptor was: {}", BASE64_STANDARD.encode(bin_sd));
log::error!("{why}");
Err(why)
}
};
(*id, sd)
})
}
pub fn len(&self) -> usize {
self.descriptors.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}