signstar_config/nethsm/
state.rs

1//! State representation for a [`NetHsmBackend`].
2//!
3//! Allows to create state representations of users ([`UserState`]) and keys ([`KeyState`] and
4//! [`KeyCertificateState`]) for [`NetHsm`] backends using the [`NetHsmState`] struct.
5//! It implements the [`StateHandling`] trait which allows comparison with
6//! other implementations.
7
8use std::any::Any;
9
10use log::{debug, trace};
11#[cfg(doc)]
12use nethsm::NetHsm;
13use nethsm::SystemState;
14
15use crate::{
16    NetHsmBackend,
17    config::state::{KeyState, KeyStates, UserState, UserStates},
18    state::StateType,
19};
20#[cfg(doc)]
21use crate::{
22    config::state::KeyCertificateState,
23    nethsm::admin_credentials::NetHsmAdminCredentials,
24};
25use crate::{
26    config::state::SignstarConfigNetHsmState,
27    state::{StateComparisonReport, StateHandling},
28};
29
30/// The state of a NetHSM backend.
31///
32/// Tracks a list of [`UserState`] and a list of [`KeyState`] data, which describes the overall
33/// state of the backend.
34#[derive(Debug)]
35pub struct NetHsmState {
36    /// The user states.
37    pub(crate) user_states: Vec<UserState>,
38    /// The key states.
39    pub(crate) key_states: Vec<KeyState>,
40}
41
42impl NetHsmState {
43    /// The specific [`StateType`] of this state.
44    const STATE_TYPE: StateType = StateType::NetHsm;
45}
46
47impl StateHandling for NetHsmState {
48    fn state_type(&self) -> StateType {
49        Self::STATE_TYPE
50    }
51
52    fn as_any(&self) -> &dyn Any {
53        self
54    }
55
56    fn compare(&self, other: &dyn StateHandling) -> StateComparisonReport {
57        if !self.is_comparable(other) {
58            trace!(
59                "{} is not compatible with {}",
60                self.state_type(),
61                other.state_type()
62            );
63            return StateComparisonReport::Incompatible {
64                self_state: self.state_type(),
65                other_state: other.state_type(),
66            };
67        }
68
69        let (user_failures, key_failures) = {
70            let (self_user_states, other_user_states, self_key_states, other_key_states) =
71                match other.state_type() {
72                    StateType::SignstarConfigNetHsm => {
73                        let Some(other) =
74                            other.as_any().downcast_ref::<SignstarConfigNetHsmState>()
75                        else {
76                            return StateComparisonReport::Incompatible {
77                                self_state: self.state_type(),
78                                other_state: other.state_type(),
79                            };
80                        };
81                        (
82                            UserStates {
83                                state_type: self.state_type(),
84                                users: &self.user_states,
85                            },
86                            UserStates {
87                                state_type: other.state_type(),
88                                users: &other.user_states,
89                            },
90                            KeyStates {
91                                state_type: self.state_type(),
92                                keys: &self.key_states,
93                            },
94                            KeyStates {
95                                state_type: other.state_type(),
96                                keys: &other.key_states,
97                            },
98                        )
99                    }
100                    StateType::NetHsm => {
101                        let Some(other) = other.as_any().downcast_ref::<NetHsmState>() else {
102                            return StateComparisonReport::Incompatible {
103                                self_state: self.state_type(),
104                                other_state: other.state_type(),
105                            };
106                        };
107                        (
108                            UserStates {
109                                state_type: self.state_type(),
110                                users: &self.user_states,
111                            },
112                            UserStates {
113                                state_type: other.state_type(),
114                                users: &other.user_states,
115                            },
116                            KeyStates {
117                                state_type: self.state_type(),
118                                keys: &self.key_states,
119                            },
120                            KeyStates {
121                                state_type: other.state_type(),
122                                keys: &other.key_states,
123                            },
124                        )
125                    }
126                    StateType::SignstarConfigYubiHsm2 | StateType::YubiHsm2 => {
127                        return StateComparisonReport::Incompatible {
128                            self_state: self.state_type(),
129                            other_state: other.state_type(),
130                        };
131                    }
132                };
133
134            let user_failures = self_user_states.compare(&other_user_states);
135            let key_failures = self_key_states.compare(&other_key_states);
136
137            (user_failures, key_failures)
138        };
139
140        let failures = {
141            let mut failures: Vec<String> = Vec::new();
142
143            for user_failure in user_failures.iter() {
144                failures.push(user_failure.to_string());
145            }
146            for key_failure in key_failures.iter() {
147                failures.push(key_failure.to_string());
148            }
149
150            failures
151        };
152
153        if !failures.is_empty() {
154            return StateComparisonReport::Failure(failures);
155        }
156
157        StateComparisonReport::Success
158    }
159}
160
161impl<'a, 'b> TryFrom<&NetHsmBackend<'a, 'b>> for NetHsmState {
162    type Error = crate::Error;
163
164    /// Creates a new [`NetHsmState`] from a [`NetHsmBackend`].
165    ///
166    /// # Note
167    ///
168    /// Uses the [`NetHsm`] backend with the [default
169    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`], but may switch to a
170    /// namespace-specific _N-Administrator_ for individual operations.
171    /// If this function succeeds, the `nethsm` is guaranteed to use the [default
172    /// _R-Administrator_][`NetHsmAdminCredentials::get_default_administrator`] again.
173    /// If this function fails, the `nethsm` may still use a namespace-specific _N-Administrator_.
174    ///
175    /// # Errors
176    ///
177    /// Returns an error if
178    ///
179    /// - retrieving the system state of the [`NetHsm`] backend fails,
180    /// - unlocking a locked [`NetHsm`] backend fails,
181    /// - or retrieving the state of users or keys on the tracked [`NetHsm`] backend fails.
182    fn try_from(value: &NetHsmBackend) -> Result<Self, Self::Error> {
183        debug!(
184            "Retrieve state of the NetHSM backend at {}",
185            value.nethsm().get_url()
186        );
187
188        let (user_states, key_states) = match value.nethsm().state()? {
189            SystemState::Unprovisioned => {
190                debug!(
191                    "Unprovisioned NetHSM backend detected at {}.\nSync should be run!",
192                    value.nethsm().get_url()
193                );
194
195                (Vec::new(), Vec::new())
196            }
197            SystemState::Locked => {
198                debug!(
199                    "Locked NetHSM backend detected at {}",
200                    value.nethsm().get_url()
201                );
202
203                value.unlock_nethsm()?;
204
205                let user_states = value.user_states()?;
206                let key_states = value.key_states()?;
207
208                (user_states, key_states)
209            }
210            SystemState::Operational => {
211                debug!(
212                    "Operational NetHSM backend detected at {}",
213                    value.nethsm().get_url()
214                );
215
216                let user_states = value.user_states()?;
217                let key_states = value.key_states()?;
218
219                (user_states, key_states)
220            }
221        };
222
223        Ok(Self {
224            user_states,
225            key_states,
226        })
227    }
228}