use crate::account::Account;
use crate::account::AccountConfig;
use crate::account::ManagerAccountCreate;
use crate::account::ManagerAccountUpdate;
use crate::patch_support::CollectionWithPatch;
use crate::patch_support::CreateWithPatch;
use crate::patch_support::ReadPatchFn;
use crate::schema::manager_account::ManagerAccount;
use crate::schema::manager_account_collection::ManagerAccountCollection;
use crate::schema::resource::ResourceCollection;
use crate::Error;
use crate::NvBmc;
use nv_redfish_core::Bmc;
use nv_redfish_core::EntityTypeRef as _;
use nv_redfish_core::NavProperty;
use nv_redfish_core::ODataId;
use std::sync::Arc;
#[derive(Clone)]
pub struct SlotDefinedConfig {
pub min_slot: Option<u32>,
pub hide_disabled: bool,
pub disable_account_on_delete: bool,
}
#[derive(Clone)]
pub struct Config {
pub account: AccountConfig,
pub slot_defined_user_accounts: Option<SlotDefinedConfig>,
}
pub struct AccountCollection<B: Bmc> {
config: Config,
bmc: NvBmc<B>,
collection: Arc<ManagerAccountCollection>,
}
impl<B: Bmc> CollectionWithPatch<ManagerAccountCollection, ManagerAccount, B>
for AccountCollection<B>
{
fn convert_patched(
base: ResourceCollection,
members: Vec<NavProperty<ManagerAccount>>,
) -> ManagerAccountCollection {
ManagerAccountCollection { base, members }
}
}
impl<B: Bmc> CreateWithPatch<ManagerAccountCollection, ManagerAccount, ManagerAccountCreate, B>
for AccountCollection<B>
{
fn entity_ref(&self) -> &ManagerAccountCollection {
self.collection.as_ref()
}
fn patch(&self) -> Option<&ReadPatchFn> {
self.config.account.read_patch_fn.as_ref()
}
fn bmc(&self) -> &B {
self.bmc.as_ref()
}
}
impl<B: Bmc> AccountCollection<B> {
pub(crate) async fn new(
bmc: NvBmc<B>,
collection_ref: &NavProperty<ManagerAccountCollection>,
config: Config,
) -> Result<Self, Error<B>> {
let collection = Self::expand_collection(
&bmc,
collection_ref,
config.account.read_patch_fn.as_ref(),
None,
)
.await?;
Ok(Self {
config,
bmc,
collection,
})
}
#[must_use]
pub fn odata_id(&self) -> &ODataId {
self.collection.as_ref().odata_id()
}
pub async fn create_account(
&self,
create: ManagerAccountCreate,
) -> Result<Option<Account<B>>, Error<B>> {
if let Some(cfg) = &self.config.slot_defined_user_accounts {
for nav in &self.collection.members {
let Ok(account) = Account::new(&self.bmc, nav, &self.config.account).await else {
continue;
};
if let Some(min) = cfg.min_slot {
let Ok(id) = account.raw().base.id.parse::<u32>() else {
continue;
};
if id < min {
continue;
}
}
if account.is_enabled() {
continue;
}
let update = ManagerAccountUpdate {
base: None,
user_name: Some(create.user_name),
password: Some(create.password),
role_id: Some(create.role_id),
enabled: Some(true),
account_expiration: create.account_expiration,
account_types: create.account_types,
email_address: create.email_address,
locked: create.locked,
oem_account_types: create.oem_account_types,
one_time_passcode_delivery_address: create.one_time_passcode_delivery_address,
password_change_required: create.password_change_required,
password_expiration: create.password_expiration,
phone_number: create.phone_number,
snmp: create.snmp,
strict_account_types: create.strict_account_types,
mfa_bypass: create.mfa_bypass,
links: None,
};
return account.update(&update).await;
}
Err(Error::AccountSlotNotAvailable)
} else {
let outcome = self.create_with_patch(&create).await?;
Ok(match outcome {
nv_redfish_core::ModificationResponse::Entity(account) => Some(Account::from_data(
self.bmc.clone(),
account,
self.config.account.clone(),
)),
nv_redfish_core::ModificationResponse::Task(_)
| nv_redfish_core::ModificationResponse::Empty => None,
})
}
}
pub async fn all_accounts_data(&self) -> Result<Vec<Account<B>>, Error<B>> {
let mut result = Vec::with_capacity(self.collection.members.len());
if let Some(cfg) = &self.config.slot_defined_user_accounts {
for m in &self.collection.members {
let account = Account::new(&self.bmc, m, &self.config.account).await?;
if !cfg.hide_disabled || account.is_enabled() {
result.push(account);
}
}
} else {
for m in &self.collection.members {
result.push(Account::new(&self.bmc, m, &self.config.account).await?);
}
}
Ok(result)
}
}