Files
claudetools/projects/msp-tools/guru-rmm/server/src/api/metrics.rs
azcomputerguru 65086f4407 fix(security): Implement Phase 1 critical security fixes
CORS:
- Restrict CORS to DASHBOARD_URL environment variable
- Default to production dashboard domain

Authentication:
- Add AuthUser requirement to all agent management endpoints
- Add AuthUser requirement to all command endpoints
- Add AuthUser requirement to all metrics endpoints
- Add audit logging for command execution (user_id tracked)

Agent Security:
- Replace Unicode characters with ASCII markers [OK]/[ERROR]/[WARNING]
- Add certificate pinning for update downloads (allowlist domains)
- Fix insecure temp file creation (use /var/run/gururmm with 0700 perms)
- Fix rollback script backgrounding (use setsid instead of literal &)

Dashboard Security:
- Move token storage from localStorage to sessionStorage
- Add proper TypeScript types (remove 'any' from error handlers)
- Centralize token management functions

Legacy Agent:
- Add -AllowInsecureTLS parameter (opt-in required)
- Add Windows Event Log audit trail when insecure mode used
- Update documentation with security warnings

Closes: Phase 1 items in issue #1

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-20 21:16:24 -07:00

71 lines
2.0 KiB
Rust

//! Metrics API endpoints
use axum::{
extract::{Path, Query, State},
http::StatusCode,
Json,
};
use chrono::{DateTime, Utc};
use serde::Deserialize;
use uuid::Uuid;
use crate::auth::AuthUser;
use crate::db::{self, Metrics, MetricsSummary};
use crate::AppState;
/// Query parameters for metrics
#[derive(Debug, Deserialize)]
pub struct MetricsQuery {
/// Number of records to return (default: 100)
pub limit: Option<i64>,
/// Start time for range query
pub start: Option<DateTime<Utc>>,
/// End time for range query
pub end: Option<DateTime<Utc>>,
}
/// Get metrics for a specific agent
/// Requires authentication.
pub async fn get_agent_metrics(
State(state): State<AppState>,
_user: AuthUser,
Path(id): Path<Uuid>,
Query(query): Query<MetricsQuery>,
) -> Result<Json<Vec<Metrics>>, (StatusCode, String)> {
// First verify the agent exists
let _agent = db::get_agent_by_id(&state.db, id)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
.ok_or((StatusCode::NOT_FOUND, "Agent not found".to_string()))?;
let metrics = if let (Some(start), Some(end)) = (query.start, query.end) {
// Range query
db::get_agent_metrics_range(&state.db, id, start, end)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
} else {
// Simple limit query
let limit = query.limit.unwrap_or(100).min(1000); // Cap at 1000
db::get_agent_metrics(&state.db, id, limit)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?
};
Ok(Json(metrics))
}
/// Get summary metrics across all agents
/// Requires authentication.
pub async fn get_summary(
State(state): State<AppState>,
_user: AuthUser,
) -> Result<Json<MetricsSummary>, (StatusCode, String)> {
let summary = db::get_metrics_summary(&state.db)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
Ok(Json(summary))
}