Files
foxhunt/common/src/tls.rs
jgrusewski 8b9abcc3c1 fix: resolve all clippy errors across 37+ workspace crates
Eliminate ~4,260 clippy deny-level errors that blocked workspace-wide
clippy runs. Errors cascaded: upstream crate failures (ctrader-openapi,
risk-data) hid thousands of downstream errors in ml, tli, backtesting.

Key changes:
- ctrader-openapi: fix shadow_unrelated/shadow_reuse (renamed vars)
- risk-data/risk: replace non-ASCII em dashes with ASCII equivalents
- tli: allow deny lints on prost-generated proto code, fix shadows
- trading_engine: fix let_underscore_must_use, wildcard matches, shadows
- broker_gateway_service: allow dead_code on unused redis_client field
- ml (4030 errors): remove local deny overrides for unwrap/expect/indexing
  (workspace warn level sufficient), add crate-level allows for non-safety
  mass-violation lints (non_ascii_literal, shadow_*, str_to_string, etc.),
  batch-fix em dashes, unseparated literal suffixes, format_push_string,
  wildcard matches, impl_trait_in_params, mutex_atomic, and more
- backtesting: replace unwrap() on first()/last() with match destructure
- tests: simplify loop-that-never-loops, fix mutex unwrap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 12:44:10 +01:00

174 lines
5.7 KiB
Rust

//! Shared TLS types for Foxhunt services.
//!
//! This module contains type definitions shared across all service TLS configurations:
//! - [`TlsProtocolVersion`] - TLS 1.2 / 1.3 selection
//! - [`UserRole`] - RBAC roles extracted from client certificates
//! - [`ClientIdentity`] - Client identity extracted from mTLS certificates
//!
//! Each service retains its own `TlsConfig` struct and validation logic because
//! the validation approaches differ (async vs sync, delegated vs monolithic).
/// TLS protocol version options.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlsProtocolVersion {
/// TLS version 1.2
Tls12,
/// TLS version 1.3
Tls13,
}
/// User roles based on certificate attributes (RBAC).
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum UserRole {
/// Administrator with full access
Admin,
/// Trader with trading permissions
Trader,
/// Analyst with read/analysis permissions
Analyst,
/// Risk manager with risk oversight
RiskManager,
/// Compliance officer with audit access
ComplianceOfficer,
/// Read-only access
ReadOnly,
}
impl UserRole {
/// Get permissions for this role.
pub fn get_permissions(&self) -> Vec<&'static str> {
match self {
UserRole::Admin => vec![
"trading.submit_order",
"trading.cancel_order",
"trading.modify_order",
"risk.view_positions",
"risk.modify_limits",
"analytics.view_data",
"analytics.run_backtest",
"compliance.view_reports",
"system.configure",
],
UserRole::Trader => vec![
"trading.submit_order",
"trading.cancel_order",
"trading.modify_order",
"risk.view_positions",
"analytics.view_data",
],
UserRole::Analyst => vec![
"analytics.view_data",
"analytics.run_backtest",
"risk.view_positions",
],
UserRole::RiskManager => vec![
"risk.view_positions",
"risk.modify_limits",
"analytics.view_data",
"compliance.view_reports",
],
UserRole::ComplianceOfficer => vec![
"compliance.view_reports",
"analytics.view_data",
"risk.view_positions",
],
UserRole::ReadOnly => vec!["analytics.view_data"],
}
}
}
/// Client identity extracted from mTLS certificate.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClientIdentity {
/// Common Name (CN) from certificate
pub common_name: String,
/// Organizational Unit (OU) from certificate
pub organizational_unit: String,
/// Certificate serial number
pub serial_number: String,
/// Certificate issuer
pub issuer: String,
}
impl ClientIdentity {
/// Check if client is authorized for trading operations.
pub fn is_authorized_for_trading(&self) -> bool {
matches!(self.organizational_unit.as_str(), "trading" | "admin")
}
/// Check if client is authorized for read-only operations.
pub fn is_authorized_for_readonly(&self) -> bool {
matches!(
self.organizational_unit.as_str(),
"trading" | "admin" | "analytics" | "risk" | "compliance"
)
}
/// Get user role based on certificate.
pub fn get_role(&self) -> UserRole {
match self.organizational_unit.as_str() {
"admin" => UserRole::Admin,
"trading" => UserRole::Trader,
"analytics" => UserRole::Analyst,
"risk" => UserRole::RiskManager,
"compliance" => UserRole::ComplianceOfficer,
_ => UserRole::ReadOnly,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_identity_authorization() {
let trading_identity = ClientIdentity {
common_name: "trader1.trading.foxhunt.internal".to_string(),
organizational_unit: "trading".to_string(),
serial_number: "12345".to_string(),
issuer: "Foxhunt Trading CA".to_string(),
};
assert!(trading_identity.is_authorized_for_trading());
assert!(trading_identity.is_authorized_for_readonly());
assert_eq!(trading_identity.get_role(), UserRole::Trader);
let readonly_identity = ClientIdentity {
common_name: "analyst1.analytics.foxhunt.internal".to_string(),
organizational_unit: "analytics".to_string(),
serial_number: "12346".to_string(),
issuer: "Foxhunt Trading CA".to_string(),
};
assert!(!readonly_identity.is_authorized_for_trading());
assert!(readonly_identity.is_authorized_for_readonly());
assert_eq!(readonly_identity.get_role(), UserRole::Analyst);
}
#[test]
fn test_user_role_permissions() {
let trader = UserRole::Trader;
let permissions = trader.get_permissions();
assert!(permissions.contains(&"trading.submit_order"));
assert!(permissions.contains(&"trading.cancel_order"));
assert!(!permissions.contains(&"system.configure"));
let readonly = UserRole::ReadOnly;
let readonly_permissions = readonly.get_permissions();
assert!(!readonly_permissions.contains(&"trading.submit_order"));
assert!(readonly_permissions.contains(&"analytics.view_data"));
}
#[test]
fn test_tls_protocol_version_equality() {
let tls13 = TlsProtocolVersion::Tls13;
let tls12 = TlsProtocolVersion::Tls12;
assert_ne!(tls13, tls12);
assert_eq!(tls13, TlsProtocolVersion::Tls13);
}
}