use crate::app_config::AppConfig; use crate::devices::device::{Device, DeviceId, DeviceInfo}; use crate::utils::time_utils::time_secs; use openssl::x509::X509Req; use std::collections::HashMap; #[derive(thiserror::Error, Debug)] pub enum DevicesListError { #[error("Enrollment failed: a device with the same ID was already registered!")] EnrollFailedDeviceAlreadyExists, #[error("Persist device config failed: the configuration of the device was not found!")] PersistFailedDeviceNotFound, } pub struct DevicesList(HashMap); impl DevicesList { /// Load the list of devices. This method should be called only once during the whole execution /// of the program pub fn load() -> anyhow::Result { let mut list = Self(HashMap::new()); for f in std::fs::read_dir(AppConfig::get().devices_config_path())? { let f = f?.file_name(); let f = f.to_string_lossy(); let dev_id = match f.strip_suffix(".conf") { Some(s) => DeviceId(s.to_string()), // This is not a device configuration file None => continue, }; let device_conf = std::fs::read(AppConfig::get().device_config_path(&dev_id))?; list.0.insert(dev_id, serde_json::from_slice(&device_conf)?); } Ok(list) } /// Check if a device with a given id exists or not pub fn exists(&self, id: &DeviceId) -> bool { self.0.contains_key(id) } /// Enroll a new device pub fn enroll( &mut self, id: &DeviceId, info: &DeviceInfo, csr: &X509Req, ) -> anyhow::Result<()> { if self.exists(id) { return Err(DevicesListError::EnrollFailedDeviceAlreadyExists.into()); } let device = Device { id: id.clone(), info: info.clone(), time_create: time_secs(), time_update: time_secs(), name: id.0.to_string(), description: "".to_string(), validated: false, enabled: false, relays: vec![], }; // First, write CSR std::fs::write(AppConfig::get().device_csr_path(id), csr.to_pem()?)?; self.0.insert(id.clone(), device); self.persist_dev_config(id)?; Ok(()) } /// Persist a device configuration on the filesystem fn persist_dev_config(&self, id: &DeviceId) -> anyhow::Result<()> { let dev = self .0 .get(id) .ok_or_else(|| DevicesListError::PersistFailedDeviceNotFound)?; std::fs::write( AppConfig::get().device_config_path(id), serde_json::to_string_pretty(dev)?, )?; Ok(()) } }