Skip to main content

nv_redfish/account/
item.rs

1// SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Redfish ManagerAccount — high-level wrapper
17//!
18//! Provides `Account`, an ergonomic handle over a Redfish `ManagerAccount`:
19//! - Read raw data with `raw()`
20//! - Update fields via `update()`, or use helpers `update_password()` and
21//!   `update_user_name()`
22//! - Delete the account with `delete()`; optionally disable instead of deleting
23//!   when configured
24//!
25//! Configuration:
26//! - `Config::read_patch_fn`: apply read-time JSON patches for vendor
27//!   compatibility
28//! - `Config::disable_account_on_delete`: make `delete()` disable the account
29//!   rather than remove it
30//!
31//! Note: `Account` objects are created by higher-level APIs (e.g.
32//! `AccountCollection`) and do not create accounts on the BMC by themselves.
33//! Use the collection to create new accounts.
34
35use crate::account::ManagerAccountUpdate;
36use crate::patch_support::Payload;
37use crate::patch_support::ReadPatchFn;
38use crate::patch_support::UpdateWithPatch;
39use crate::schema::redfish::manager_account::ManagerAccount;
40use crate::Error;
41use crate::NvBmc;
42use crate::Resource;
43use crate::ResourceSchema;
44use nv_redfish_core::Bmc;
45use nv_redfish_core::Deletable as _;
46use nv_redfish_core::NavProperty;
47use std::convert::identity;
48use std::sync::Arc;
49
50#[derive(Clone)]
51pub struct Config {
52    /// Function to patch input JSON when reading account structures.
53    pub read_patch_fn: Option<ReadPatchFn>,
54    /// If true, deletion disables the account instead of removing it.
55    pub disable_account_on_delete: bool,
56}
57
58/// Represents a Redfish `ManagerAccount`.
59pub struct Account<B: Bmc> {
60    config: Config,
61    bmc: NvBmc<B>,
62    data: Arc<ManagerAccount>,
63}
64
65impl<B: Bmc> UpdateWithPatch<ManagerAccount, ManagerAccountUpdate, B> for Account<B> {
66    fn entity_ref(&self) -> &ManagerAccount {
67        self.data.as_ref()
68    }
69    fn patch(&self) -> Option<&ReadPatchFn> {
70        self.config.read_patch_fn.as_ref()
71    }
72    fn bmc(&self) -> &B {
73        self.bmc.as_ref()
74    }
75}
76
77impl<B: Bmc> Account<B> {
78    /// Create a new account handle. This does not create an account on the
79    /// BMC.
80    pub(crate) async fn new(
81        bmc: &NvBmc<B>,
82        nav: &NavProperty<ManagerAccount>,
83        config: &Config,
84    ) -> Result<Self, Error<B>> {
85        if let Some(read_patch_fn) = &config.read_patch_fn {
86            Payload::get(bmc.as_ref(), nav, read_patch_fn.as_ref()).await
87        } else {
88            nav.get(bmc.as_ref()).await.map_err(Error::Bmc)
89        }
90        .map(|data| Self {
91            bmc: bmc.clone(),
92            data,
93            config: config.clone(),
94        })
95    }
96
97    /// Create from existing data.
98    pub(crate) fn from_data(bmc: NvBmc<B>, data: ManagerAccount, config: Config) -> Self {
99        Self {
100            bmc,
101            data: Arc::new(data),
102            config,
103        }
104    }
105
106    /// Raw `ManagerAccount` data.
107    #[must_use]
108    pub fn raw(&self) -> Arc<ManagerAccount> {
109        self.data.clone()
110    }
111
112    /// Account is enabled.
113    #[must_use]
114    pub fn is_enabled(&self) -> bool {
115        self.data.enabled.is_none_or(identity)
116    }
117
118    /// Update the account.
119    ///
120    /// Returns the new (updated) account.
121    ///
122    /// # Errors
123    ///
124    /// Returns an error if the server responds with an error or if the
125    /// response cannot be parsed.
126    pub async fn update(&self, update: &ManagerAccountUpdate) -> Result<Self, Error<B>> {
127        let account = self.update_with_patch(update).await?;
128        Ok(Self {
129            config: self.config.clone(),
130            bmc: self.bmc.clone(),
131            data: Arc::new(account),
132        })
133    }
134
135    /// Update the account's password.
136    ///
137    /// Returns the new (updated) account.
138    ///
139    /// # Errors
140    ///
141    /// Returns an error if the server responds with an error or if the
142    /// response cannot be parsed.
143    pub async fn update_password(&self, password: String) -> Result<Self, Error<B>> {
144        self.update(
145            &ManagerAccountUpdate::builder()
146                .with_password(password)
147                .build(),
148        )
149        .await
150    }
151
152    /// Update the account's user name.
153    ///
154    /// Returns the new (updated) account.
155    ///
156    /// # Errors
157    ///
158    /// Returns an error if the server responds with an error or if the
159    /// response cannot be parsed.
160    pub async fn update_user_name(&self, user_name: String) -> Result<Self, Error<B>> {
161        self.update(
162            &ManagerAccountUpdate::builder()
163                .with_user_name(user_name)
164                .build(),
165        )
166        .await
167    }
168
169    /// Delete the current account.
170    ///
171    /// # Errors
172    ///
173    /// Returns an error if deletion fails.
174    pub async fn delete(&self) -> Result<(), Error<B>> {
175        if self.config.disable_account_on_delete {
176            self.update(&ManagerAccountUpdate::builder().with_enabled(false).build())
177                .await
178                .map(|_| ())
179        } else {
180            self.data
181                .delete(self.bmc.as_ref())
182                .await
183                .map_err(Error::Bmc)
184                .map(|_| ())
185        }
186    }
187}
188
189impl<B: Bmc> Resource for Account<B> {
190    fn resource_ref(&self) -> &ResourceSchema {
191        &self.data.as_ref().base
192    }
193}