SolarEnergy/central_backend/src/devices/devices_list.rs

150 lines
4.5 KiB
Rust
Raw Normal View History

2024-07-01 19:10:45 +00:00
use crate::app_config::AppConfig;
2024-07-03 19:32:32 +00:00
use crate::crypto::pki;
2024-07-02 20:55:51 +00:00
use crate::devices::device::{Device, DeviceId, DeviceInfo};
use crate::utils::time_utils::time_secs;
use openssl::x509::X509Req;
2024-07-01 19:10:45 +00:00
use std::collections::HashMap;
2024-07-02 20:55:51 +00:00
#[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,
2024-07-03 19:32:32 +00:00
#[error("Validated device failed: the device does not exists!")]
ValidateDeviceFailedDeviceNotFound,
#[error("Validated device failed: the device is already validated!")]
ValidateDeviceFailedDeviceAlreadyValidated,
2024-07-02 20:55:51 +00:00
}
2024-07-01 19:10:45 +00:00
pub struct DevicesList(HashMap<DeviceId, Device>);
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<Self> {
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)
}
2024-07-02 20:55:51 +00:00
/// 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(DevicesListError::PersistFailedDeviceNotFound)?;
2024-07-02 20:55:51 +00:00
std::fs::write(
AppConfig::get().device_config_path(id),
serde_json::to_string_pretty(dev)?,
)?;
Ok(())
}
/// Get a copy of the full list of devices
pub fn full_list(&self) -> Vec<Device> {
self.0.clone().into_values().collect()
}
2024-07-03 19:10:15 +00:00
2024-07-03 19:32:32 +00:00
/// Validate a device
pub fn validate(&mut self, id: &DeviceId) -> anyhow::Result<()> {
let dev = self
.0
.get_mut(id)
.ok_or(DevicesListError::ValidateDeviceFailedDeviceNotFound)?;
if dev.validated {
return Err(DevicesListError::ValidateDeviceFailedDeviceAlreadyValidated.into());
}
// Issue certificate
let csr = X509Req::from_pem(&std::fs::read(AppConfig::get().device_csr_path(id))?)?;
let cert = pki::gen_certificate_for_device(&csr)?;
std::fs::write(AppConfig::get().device_cert_path(id), cert)?;
// Mark device as validated
dev.validated = true;
self.persist_dev_config(id)?;
Ok(())
}
2024-07-03 19:10:15 +00:00
/// Delete a device
pub fn delete(&mut self, id: &DeviceId) -> anyhow::Result<()> {
let crt_path = AppConfig::get().device_cert_path(id);
if crt_path.is_file() {
// TODO : implement
unimplemented!("Certificate revocation not implemented yet!");
}
let csr_path = AppConfig::get().device_csr_path(id);
if csr_path.is_file() {
std::fs::remove_file(&csr_path)?;
}
let conf_path = AppConfig::get().device_config_path(id);
if conf_path.is_file() {
std::fs::remove_file(&conf_path)?;
}
self.0.remove(id);
Ok(())
}
2024-07-01 19:10:45 +00:00
}