use ldap3::{LdapError, Scope, SearchEntry, SearchResult};
use crate::{
decryption::{DecryptLapsPassword, EncryptedPasswordAttribute},
error::LapsError,
AdConnection, AdConnectionAsync, MsLapsPassword,
};
pub fn lookup_laps_info(
computer_name: &str,
con: &mut AdConnection,
search_base: &str,
scope: Scope,
) -> Result<SearchResult, LdapError> {
assert!(!con.ldap.is_closed());
let filter = format!("(&(objectClass=computer)(Name={computer_name}))");
con.ldap.search(
search_base,
scope,
&filter,
vec![
"msLAPS-Password",
"msLAPS-EncryptedPassword",
"msLAPS-PasswordExpirationTime",
],
)
}
pub async fn lookup_laps_info_async(
computer_name: &str,
con: &mut AdConnectionAsync,
search_base: &str,
scope: Scope,
) -> Result<SearchResult, LdapError> {
assert!(!con.ldap.is_closed());
let filter = format!("(&(objectClass=computer)(Name={computer_name}))");
con.ldap
.search(
search_base,
scope,
&filter,
vec![
"msLAPS-Password",
"msLAPS-EncryptedPassword",
"msLAPS-PasswordExpirationTime",
],
)
.await
}
pub fn process_ldap_search_result(
search_result: Result<SearchResult, LdapError>,
) -> Result<MsLapsPassword, LapsError> {
let (rs, _res) = search_result?.success()?;
if rs.len() != 1 {
return Err(LapsError::NotFound("Computer".into()));
}
let entry = SearchEntry::construct(
rs.first()
.expect("at least one Search result exists")
.to_owned(),
);
let ms_laps_password: Option<MsLapsPassword> = if entry.attrs.contains_key("msLAPS-Password") {
let encoded_pass_info = entry.attrs["msLAPS-Password"]
.first()
.expect("msLAPS-Password attribute should contain at least one value");
Some(
serde_json::from_str::<MsLapsPassword>(encoded_pass_info).map_err(|_| {
LapsError::ConversionError("msLAPS-Password is not a valid JSON String".into())
})?,
)
} else {
None
};
let ms_laps_encrypted_password: Option<MsLapsPassword> =
if entry.bin_attrs.contains_key("msLAPS-EncryptedPassword") {
let blob = entry.bin_attrs["msLAPS-EncryptedPassword"]
.first()
.expect("msLAPS-EncryptedPassword attribute should contain at least one value")
.as_slice();
let encrypted_password_info = EncryptedPasswordAttribute::try_from(blob)?;
Some(EncryptedPasswordAttribute::decrypt(
&encrypted_password_info,
)?)
} else {
None
};
let result = [ms_laps_password, ms_laps_encrypted_password]
.into_iter()
.flatten()
.max_by_key(|pass| pass.time);
let Some(result) = result else {
return Err(LapsError::NotFound("Laps password".into()));
};
Ok(result)
}