Can update device general information

This commit is contained in:
2024-07-22 22:19:48 +02:00
parent baf341d505
commit 4d5ba939d1
18 changed files with 546 additions and 147 deletions

View File

@ -18,3 +18,40 @@ pub const MAX_SESSION_DURATION: u64 = 3600 * 24;
/// List of routes that do not require authentication
pub const ROUTES_WITHOUT_AUTH: [&str; 2] =
["/web_api/server/config", "/web_api/auth/password_auth"];
#[derive(serde::Serialize)]
pub struct SizeConstraint {
/// Minimal string length
min: usize,
/// Maximal string length
max: usize,
}
impl SizeConstraint {
pub fn new(min: usize, max: usize) -> Self {
Self { min, max }
}
pub fn validate(&self, val: &str) -> bool {
let len = val.trim().len();
len >= self.min && len <= self.max
}
}
/// Backend static constraints
#[derive(serde::Serialize)]
pub struct StaticConstraints {
/// Device name constraint
pub dev_name_len: SizeConstraint,
/// Device description constraint
pub dev_description_len: SizeConstraint,
}
impl Default for StaticConstraints {
fn default() -> Self {
Self {
dev_name_len: SizeConstraint::new(1, 50),
dev_description_len: SizeConstraint::new(0, 100),
}
}
}

View File

@ -1,5 +1,7 @@
//! # Devices entities definition
use crate::constants::StaticConstraints;
/// Device information provided directly by the device during syncrhonisation.
///
/// It should not be editable fro the Web UI
@ -100,3 +102,29 @@ pub struct DeviceRelay {
/// Specify relays that must be turned off before this relay can be started
conflicts_with: Vec<DeviceRelayID>,
}
/// Device general information
///
/// This structure is used to update device information
#[derive(serde::Deserialize, Debug, Clone)]
pub struct DeviceGeneralInfo {
pub name: String,
pub description: String,
pub enabled: bool,
}
impl DeviceGeneralInfo {
/// Check for errors in the structure
pub fn error(&self) -> Option<&'static str> {
let constraints = StaticConstraints::default();
if !constraints.dev_name_len.validate(&self.name) {
return Some("Invalid device name length!");
}
if !constraints.dev_description_len.validate(&self.description) {
return Some("Invalid device description length!");
}
None
}
}

View File

@ -1,6 +1,6 @@
use crate::app_config::AppConfig;
use crate::crypto::pki;
use crate::devices::device::{Device, DeviceId, DeviceInfo};
use crate::devices::device::{Device, DeviceGeneralInfo, DeviceId, DeviceInfo};
use crate::utils::time_utils::time_secs;
use openssl::x509::{X509Req, X509};
use std::collections::HashMap;
@ -15,6 +15,8 @@ pub enum DevicesListError {
ValidateDeviceFailedDeviceNotFound,
#[error("Validated device failed: the device is already validated!")]
ValidateDeviceFailedDeviceAlreadyValidated,
#[error("Update device failed: the device does not exists!")]
UpdateDeviceFailedDeviceNotFound,
#[error("Requested device was not found")]
DeviceNotFound,
#[error("Requested device is not validated")]
@ -133,6 +135,27 @@ impl DevicesList {
Ok(())
}
/// Update a device general information
pub fn update_general_info(
&mut self,
id: &DeviceId,
general_info: DeviceGeneralInfo,
) -> anyhow::Result<()> {
let dev = self
.0
.get_mut(id)
.ok_or(DevicesListError::UpdateDeviceFailedDeviceNotFound)?;
dev.name = general_info.name;
dev.description = general_info.description;
dev.enabled = general_info.enabled;
dev.time_update = time_secs();
self.persist_dev_config(id)?;
Ok(())
}
/// Get single certificate information
fn get_cert(&self, id: &DeviceId) -> anyhow::Result<X509> {
let dev = self

View File

@ -1,5 +1,5 @@
use crate::constants;
use crate::devices::device::{Device, DeviceId, DeviceInfo};
use crate::devices::device::{Device, DeviceGeneralInfo, DeviceId, DeviceInfo};
use crate::devices::devices_list::DevicesList;
use crate::energy::consumption;
use crate::energy::consumption::EnergyConsumption;
@ -109,6 +109,27 @@ impl Handler<ValidateDevice> for EnergyActor {
}
}
/// Update a device general information
#[derive(Message)]
#[rtype(result = "anyhow::Result<()>")]
pub struct UpdateDeviceGeneralInfo(pub DeviceId, pub DeviceGeneralInfo);
impl Handler<UpdateDeviceGeneralInfo> for EnergyActor {
type Result = anyhow::Result<()>;
fn handle(&mut self, msg: UpdateDeviceGeneralInfo, _ctx: &mut Context<Self>) -> Self::Result {
log::info!(
"Requested to update device general info {:?}... {:#?}",
&msg.0,
&msg.1
);
self.devices.update_general_info(&msg.0, msg.1)?;
Ok(())
}
}
/// Delete a device
#[derive(Message)]
#[rtype(result = "anyhow::Result<()>")]

View File

@ -147,6 +147,10 @@ pub async fn secure_server(energy_actor: EnergyActorAddr) -> anyhow::Result<()>
"/web_api/device/{id}/validate",
web::post().to(devices_controller::validate_device),
)
.route(
"/web_api/device/{id}",
web::patch().to(devices_controller::update_device),
)
.route(
"/web_api/device/{id}",
web::delete().to(devices_controller::delete_device),

View File

@ -1,4 +1,4 @@
use crate::devices::device::DeviceId;
use crate::devices::device::{DeviceGeneralInfo, DeviceId};
use crate::energy::energy_actor;
use crate::server::custom_error::HttpResult;
use crate::server::WebEnergyActor;
@ -54,6 +54,26 @@ pub async fn validate_device(actor: WebEnergyActor, id: web::Path<DeviceInPath>)
Ok(HttpResponse::Accepted().finish())
}
/// Update a device information
pub async fn update_device(
actor: WebEnergyActor,
id: web::Path<DeviceInPath>,
update: web::Json<DeviceGeneralInfo>,
) -> HttpResult {
if let Some(e) = update.error() {
return Ok(HttpResponse::BadRequest().json(e));
}
actor
.send(energy_actor::UpdateDeviceGeneralInfo(
id.id.clone(),
update.0.clone(),
))
.await??;
Ok(HttpResponse::Accepted().finish())
}
/// Delete a device
pub async fn delete_device(actor: WebEnergyActor, id: web::Path<DeviceInPath>) -> HttpResult {
actor

View File

@ -1,4 +1,5 @@
use crate::app_config::AppConfig;
use crate::constants::StaticConstraints;
use actix_web::HttpResponse;
pub async fn secure_home() -> HttpResponse {
@ -10,12 +11,14 @@ pub async fn secure_home() -> HttpResponse {
#[derive(serde::Serialize)]
struct ServerConfig {
auth_disabled: bool,
constraints: StaticConstraints,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
auth_disabled: AppConfig::get().unsecure_disable_login,
constraints: Default::default(),
}
}
}