Add USB storage device control feature

- Add USB policy fields to machines schema (policy, status, error)
- Create usbPolicyEvents table for audit logging
- Implement Convex mutations/queries for USB policy management
- Add REST API endpoints for desktop agent communication
- Create Rust usb_control module for Windows registry manipulation
- Integrate USB policy check in agent heartbeat loop
- Add USB policy control component in admin device overview
- Add localhost:3001 to auth trustedOrigins for dev

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-04 13:30:59 -03:00
parent 0e9310d6e4
commit 49aa143a80
11 changed files with 1116 additions and 1 deletions

View file

@ -1129,6 +1129,109 @@ async fn post_heartbeat(
Ok(())
}
#[derive(Debug, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct UsbPolicyResponse {
pending: bool,
policy: Option<String>,
applied_at: Option<i64>,
}
#[derive(Debug, serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct UsbPolicyStatusReport {
machine_token: String,
status: String,
error: Option<String>,
current_policy: Option<String>,
}
async fn check_and_apply_usb_policy(base_url: &str, token: &str) {
let url = format!("{}/api/machines/usb-policy?machineToken={}", base_url, token);
let response = match HTTP_CLIENT.get(&url).send().await {
Ok(resp) => resp,
Err(e) => {
eprintln!("[agent] Falha ao verificar politica USB: {e}");
return;
}
};
let policy_response: UsbPolicyResponse = match response.json().await {
Ok(data) => data,
Err(e) => {
eprintln!("[agent] Falha ao parsear resposta de politica USB: {e}");
return;
}
};
if !policy_response.pending {
return;
}
let policy_str = match policy_response.policy {
Some(p) => p,
None => {
eprintln!("[agent] Politica USB pendente mas sem valor de policy");
return;
}
};
eprintln!("[agent] Aplicando politica USB: {}", policy_str);
#[cfg(target_os = "windows")]
{
use crate::usb_control::{apply_usb_policy, UsbPolicy};
let policy = match UsbPolicy::from_str(&policy_str) {
Some(p) => p,
None => {
eprintln!("[agent] Politica USB invalida: {}", policy_str);
report_usb_policy_status(base_url, token, "FAILED", Some(format!("Politica invalida: {}", policy_str)), None).await;
return;
}
};
match apply_usb_policy(policy) {
Ok(result) => {
eprintln!("[agent] Politica USB aplicada com sucesso: {:?}", result);
report_usb_policy_status(base_url, token, "APPLIED", None, Some(policy_str)).await;
}
Err(e) => {
eprintln!("[agent] Falha ao aplicar politica USB: {e}");
report_usb_policy_status(base_url, token, "FAILED", Some(e.to_string()), None).await;
}
}
}
#[cfg(not(target_os = "windows"))]
{
eprintln!("[agent] Controle de USB nao suportado neste sistema operacional");
report_usb_policy_status(base_url, token, "FAILED", Some("Sistema operacional nao suportado".to_string()), None).await;
}
}
async fn report_usb_policy_status(
base_url: &str,
token: &str,
status: &str,
error: Option<String>,
current_policy: Option<String>,
) {
let url = format!("{}/api/machines/usb-policy", base_url);
let report = UsbPolicyStatusReport {
machine_token: token.to_string(),
status: status.to_string(),
error,
current_policy,
};
if let Err(e) = HTTP_CLIENT.post(&url).json(&report).send().await {
eprintln!("[agent] Falha ao reportar status de politica USB: {e}");
}
}
struct HeartbeatHandle {
token: String,
base_url: String,
@ -1198,6 +1301,9 @@ impl AgentRuntime {
eprintln!("[agent] Falha inicial ao enviar heartbeat: {error}");
}
// Verifica politica USB apos heartbeat inicial
check_and_apply_usb_policy(&base_clone, &token_clone).await;
let mut ticker = tokio::time::interval(Duration::from_secs(interval));
ticker.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
@ -1215,6 +1321,9 @@ impl AgentRuntime {
{
eprintln!("[agent] Falha ao enviar heartbeat: {error}");
}
// Verifica politica USB apos cada heartbeat
check_and_apply_usb_policy(&base_clone, &token_clone).await;
}
});