Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
174 lines
5.7 KiB
Rust
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);
|
|
}
|
|
}
|