122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
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<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)
|
|
}
|
|
|
|
/// 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)?;
|
|
|
|
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()
|
|
}
|
|
|
|
/// 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(())
|
|
}
|
|
}
|