New crates
- platform-nats: async_nats JetStream bridge; pull/push consumers, explicit ACK,
subject prefixing under provisioning.>, 6 stream definitions on startup
- platform-db: SurrealDB pool (embedded RocksDB solo, Surreal<Mem> tests,
WebSocket server multi-user); migrate() with DEFINE TABLE IF NOT EXISTS DDL
Service integrations
- orchestrator: NATS pub on task state transitions, execution_logs → SurrealDB,
webhook handler (HMAC-SHA256), AuditCollector (batch INSERT, 100-event/1s flush)
- control-center: solo_auth_middleware (intentional bypass, --mode solo only),
NATS session events, WebSocket bridge via JetStream subscription (no polling)
- vault-service: NATS lease flow; credentials over HTTPS only (lease_id in NATS);
SurrealDB storage backend with MVCC retry + exponential backoff
- secretumvault: complete SurrealDB backend replacing HashMap; 9 unit + 19 integration tests
- extension-registry: NATS lifecycle events, vault:// credential resolver with TTL cache,
cache invalidation via provisioning.workspace.*.deploy.done
Clippy workspace clean
cargo clippy --workspace -- -D warnings: 0 errors
Patterns fixed: derivable_impls (#[default] on enum variants), excessive_nesting
(let-else, boolean arithmetic in retain, extracted helpers), io_error_other,
redundant_closure, iter_kv_map, manual_range_contains, pathbuf_instead_of_path
286 lines
10 KiB
Rust
286 lines
10 KiB
Rust
#![allow(
|
|
dead_code,
|
|
unused_imports,
|
|
unused_variables,
|
|
unused_assignments,
|
|
unused
|
|
)]
|
|
|
|
//! Control Center Library
|
|
//!
|
|
//! Provides JWT authentication, user management, role-based access control,
|
|
//! and real-time WebSocket events with SurrealDB integration.
|
|
|
|
// ============================================================================
|
|
// Core Modules (Always Available with 'core' feature)
|
|
// ============================================================================
|
|
|
|
pub mod app_state;
|
|
pub mod auth;
|
|
pub mod clients;
|
|
pub mod config;
|
|
pub mod error;
|
|
pub mod handlers;
|
|
pub mod middleware;
|
|
pub mod models;
|
|
pub mod services;
|
|
pub mod simple_config;
|
|
pub mod storage;
|
|
|
|
// ============================================================================
|
|
// Optional Modules (Feature-Gated)
|
|
// ============================================================================
|
|
|
|
// KMS: Key Management System
|
|
#[cfg(feature = "kms")]
|
|
pub mod kms;
|
|
|
|
// Audit: Security event logging
|
|
#[cfg(feature = "audit")]
|
|
pub mod audit;
|
|
|
|
// MFA: Multi-factor authentication
|
|
#[cfg(feature = "mfa")]
|
|
pub mod mfa;
|
|
|
|
// Compliance: Policy evaluation and compliance checking
|
|
#[cfg(feature = "compliance")]
|
|
pub mod compliance;
|
|
#[cfg(feature = "compliance")]
|
|
pub mod policies;
|
|
|
|
// Experimental: Advanced features in development
|
|
#[cfg(feature = "experimental")]
|
|
pub mod anomaly;
|
|
use std::sync::Arc;
|
|
|
|
// ============================================================================
|
|
// Re-exports - Stable API
|
|
// ============================================================================
|
|
|
|
// Core types (always available)
|
|
pub use app_state::{
|
|
create_app_state, create_app_state_with_builder, AppStateBuilder, DefaultAppStateBuilder,
|
|
};
|
|
// Re-export auth module types
|
|
pub use auth::{
|
|
jwt::{BlacklistStats, JwtService as NewJwtService, TokenClaims, TokenPair, TokenType},
|
|
password::{PasswordService, PasswordStrength},
|
|
user::{User as NewUser, UserRole, UserService as NewUserService, UserStatus},
|
|
AuthService as NewAuthService,
|
|
};
|
|
pub use error::{ApiErrorResponse, ControlCenterError, ErrorContext, Result};
|
|
// Feature-gated type re-exports
|
|
#[cfg(feature = "mfa")]
|
|
pub use mfa::{
|
|
MfaDeviceType, MfaService, MfaStatus, MfaVerification, TotpDevice, TotpService, WebAuthnDevice,
|
|
WebAuthnService,
|
|
};
|
|
// Re-export simple config as the public Config type
|
|
pub use simple_config::Config;
|
|
|
|
// ============================================================================
|
|
// Internal Imports
|
|
// ============================================================================
|
|
use crate::clients::OrchestratorClient;
|
|
use crate::error::infrastructure;
|
|
use crate::handlers::websocket::WebSocketManager;
|
|
#[cfg(feature = "kms")]
|
|
use crate::kms::advanced::{AuditBackend, AuditConfig, AuditLogFormat, AuditLogLevel, AuditLogger};
|
|
#[cfg(feature = "kms")]
|
|
use crate::kms::create_http_kms_client;
|
|
use crate::services::{
|
|
AuthService, DatabaseService, DynamicSecretsService, IacDeploymentService, IacDetectionService,
|
|
IacRulesService, JwtService, MonitoringService, PermissionService, RoleService,
|
|
RotationJobConfig, RotationJobScheduler, RotationScheduler, SecretSharing, SecretsService,
|
|
UserService,
|
|
};
|
|
|
|
/// Application state shared across all handlers
|
|
#[derive(Clone)]
|
|
pub struct AppState {
|
|
pub database_service: Arc<DatabaseService>,
|
|
pub jwt_service: Arc<JwtService>,
|
|
pub auth_service: Arc<AuthService>,
|
|
pub user_service: Arc<UserService>,
|
|
pub role_service: Arc<RoleService>,
|
|
pub permission_service: Arc<PermissionService>,
|
|
pub websocket_manager: Arc<WebSocketManager>,
|
|
pub iac_detection_service: Arc<IacDetectionService>,
|
|
pub iac_rules_service: Arc<IacRulesService>,
|
|
pub iac_deployment_service: Arc<IacDeploymentService>,
|
|
pub secrets_service: Arc<SecretsService>,
|
|
pub dynamic_secrets_service: Arc<DynamicSecretsService>,
|
|
pub rotation_scheduler: Arc<RotationScheduler>,
|
|
pub rotation_job_scheduler: Arc<RotationJobScheduler>,
|
|
pub secret_sharing: Arc<SecretSharing>,
|
|
pub monitoring_service: Arc<MonitoringService>,
|
|
pub orchestrator_client: Arc<OrchestratorClient>,
|
|
pub config: Config,
|
|
/// When true, auth middleware is replaced by a no-op that injects LocalUser
|
|
/// context.
|
|
pub solo_mode: bool,
|
|
/// NATS bridge for task status subscription (optional feature)
|
|
#[cfg(feature = "nats")]
|
|
pub nats: Option<Arc<platform_nats::NatsBridge>>,
|
|
}
|
|
|
|
impl AppState {
|
|
/// Create a new application state instance
|
|
pub async fn new(config: Config, solo_mode: bool) -> Result<Self> {
|
|
// Initialize database service
|
|
let database_service = Arc::new(DatabaseService::new(config.database.clone()).await?);
|
|
|
|
// Initialize JWT service
|
|
let jwt_service = Arc::new(JwtService::new(config.jwt.clone())?);
|
|
|
|
// Initialize user service
|
|
let user_service = Arc::new(UserService::new(database_service.clone()));
|
|
|
|
// Initialize role service
|
|
let role_service = Arc::new(RoleService::new(database_service.clone()));
|
|
|
|
// Initialize permission service
|
|
let permission_service = Arc::new(PermissionService::new(database_service.clone()));
|
|
|
|
// Initialize authentication service
|
|
let auth_service = Arc::new(AuthService::new(
|
|
database_service.clone(),
|
|
jwt_service.clone(),
|
|
user_service.clone(),
|
|
));
|
|
|
|
// Initialize WebSocket manager
|
|
let websocket_manager = Arc::new(WebSocketManager::new());
|
|
|
|
// Initialize IaC services
|
|
let iac_detection_service = Arc::new(IacDetectionService::new(database_service.clone()));
|
|
let iac_rules_service = Arc::new(IacRulesService::new(database_service.clone()));
|
|
let iac_deployment_service = Arc::new(IacDeploymentService::new(database_service.clone()));
|
|
|
|
// Initialize KMS HTTP Client (for external KMS communication)
|
|
// Configuration: http://localhost:9091 (default KMS service endpoint)
|
|
// Using factory function to reduce coupling to KmsServiceClient
|
|
let kms_client = Arc::new(create_http_kms_client("http://localhost:9091"));
|
|
|
|
// Initialize SurrealDB Storage for secrets using config
|
|
let storage = Arc::new(
|
|
crate::storage::surrealdb_storage::SurrealDbPolicyStorage::new(&config)
|
|
.await
|
|
.map_err(|e| {
|
|
ControlCenterError::Infrastructure(
|
|
infrastructure::InfrastructureError::Configuration(format!(
|
|
"Failed to initialize secrets storage: {}",
|
|
e
|
|
)),
|
|
)
|
|
})?,
|
|
);
|
|
|
|
// Initialize Audit Logger for secrets operations
|
|
// Create minimal audit config for secrets logging
|
|
let audit_config = AuditConfig {
|
|
enabled: true,
|
|
backend: AuditBackend::Stdout,
|
|
retention_days: 30,
|
|
log_level: AuditLogLevel::Info,
|
|
include_data: false,
|
|
max_file_size_mb: 100,
|
|
format: AuditLogFormat::Json,
|
|
database: None,
|
|
syslog: None,
|
|
};
|
|
|
|
let audit_logger = Arc::new(AuditLogger::new(audit_config).await.map_err(|e| {
|
|
ControlCenterError::Infrastructure(infrastructure::InfrastructureError::Internal(
|
|
format!("Failed to initialize audit logger: {}", e),
|
|
))
|
|
})?);
|
|
|
|
// Initialize Secrets Service with all dependencies
|
|
let secrets_service = Arc::new(SecretsService::new(kms_client, storage, audit_logger));
|
|
|
|
// Initialize Orchestrator HTTP Client (Phase 2.3)
|
|
// Configuration: orchestrator endpoint (defaults to localhost:9090)
|
|
// TODO: Phase 3 - Add orchestrator_url to Config struct
|
|
let orchestrator_url = "http://localhost:9090".to_string();
|
|
let orchestrator_client = Arc::new(OrchestratorClient::new(orchestrator_url.clone()));
|
|
|
|
// Initialize Dynamic Secrets Service (Phase 2.2)
|
|
let dynamic_secrets_service = Arc::new(DynamicSecretsService::new(
|
|
secrets_service.clone(),
|
|
orchestrator_url,
|
|
));
|
|
|
|
// Initialize Rotation Scheduler (Phase 3.1)
|
|
let rotation_scheduler = Arc::new(RotationScheduler::new());
|
|
|
|
// Initialize Rotation Job Scheduler (Phase 5)
|
|
let rotation_job_config = RotationJobConfig {
|
|
check_interval_secs: 3600, // Check every hour
|
|
max_concurrent: 5, // Max 5 concurrent rotations
|
|
auto_start: true, // Start automatically
|
|
};
|
|
let rotation_job_scheduler = Arc::new(RotationJobScheduler::new(
|
|
rotation_scheduler.clone(),
|
|
rotation_job_config,
|
|
));
|
|
|
|
// Start the rotation job scheduler
|
|
rotation_job_scheduler.start().await?;
|
|
|
|
// Initialize Secret Sharing Service (Phase 3.2)
|
|
let secret_sharing = Arc::new(SecretSharing::new());
|
|
|
|
// Initialize Monitoring Service (Phase 3.4)
|
|
let monitoring_service = Arc::new(MonitoringService::new());
|
|
|
|
Ok(Self {
|
|
database_service,
|
|
jwt_service,
|
|
auth_service,
|
|
user_service,
|
|
role_service,
|
|
permission_service,
|
|
websocket_manager,
|
|
iac_detection_service,
|
|
iac_rules_service,
|
|
iac_deployment_service,
|
|
secrets_service,
|
|
dynamic_secrets_service,
|
|
rotation_scheduler,
|
|
rotation_job_scheduler,
|
|
secret_sharing,
|
|
monitoring_service,
|
|
orchestrator_client,
|
|
config,
|
|
solo_mode,
|
|
#[cfg(feature = "nats")]
|
|
nats: {
|
|
use platform_nats::{NatsBridge, NatsConfig};
|
|
match NatsBridge::connect(&NatsConfig::default()).await {
|
|
Ok(bridge) => {
|
|
let bridge = std::sync::Arc::new(bridge);
|
|
tracing::info!("Connected to NATS");
|
|
Some(bridge)
|
|
}
|
|
Err(e) => {
|
|
tracing::warn!("NATS connection failed (bridge disabled): {}", e);
|
|
None
|
|
}
|
|
}
|
|
},
|
|
})
|
|
}
|
|
|
|
/// Health check for all services
|
|
pub async fn health_check(&self) -> Result<bool> {
|
|
// Check database connection
|
|
self.database_service.health_check().await?;
|
|
|
|
// TODO: Add other health checks as needed
|
|
|
|
Ok(true)
|
|
}
|
|
}
|