Skip to main content

fraiseql_core/security/
tls_enforcer.rs

1//! TLS Security Enforcement
2//!
3//! This module provides TLS/SSL security enforcement for GraphQL connections.
4//! It validates:
5//! - HTTPS requirement (TLS mandatory)
6//! - Minimum TLS version
7//! - Mutual TLS (mTLS) requirement for client certificates
8//! - Client certificate validity
9//!
10//! # Architecture
11//!
12//! The TLS enforcer acts as a gatekeeper in the security middleware:
13//! ```text
14//! HTTP Request with TLS info
15//!     ↓
16//! TlsEnforcer::validate_connection()
17//!     ├─ Check 1: Is HTTPS required? (tls_required)
18//!     ├─ Check 2: Is minimum TLS version met? (min_version)
19//!     ├─ Check 3: Is mTLS required? (mtls_required)
20//!     └─ Check 4: Is client cert valid? (client_cert_valid)
21//!     ↓
22//! Result<()> (OK or TlsError)
23//! ```
24//!
25//! # Examples
26//!
27//! ```no_run
28//! use fraiseql_core::security::{TlsEnforcer, TlsConfig, TlsConnection, TlsVersion};
29//!
30//! // Create enforcer with strict configuration
31//! let config = TlsConfig {
32//!     tls_required: true,
33//!     mtls_required: true,
34//!     min_version: TlsVersion::V1_3,
35//! };
36//! let enforcer = TlsEnforcer::from_config(config);
37//!
38//! // Validate a connection
39//! let conn = TlsConnection {
40//!     is_secure: true,
41//!     version: TlsVersion::V1_3,
42//!     has_client_cert: true,
43//!     client_cert_valid: true,
44//! };
45//!
46//! match enforcer.validate_connection(&conn) {
47//!     Ok(()) => println!("Connection is secure"),
48//!     Err(e) => eprintln!("TLS validation failed: {}", e),
49//! }
50//! ```
51
52use std::fmt;
53
54use serde::{Deserialize, Serialize};
55
56use crate::security::errors::{Result, SecurityError};
57
58/// TLS/SSL protocol version.
59///
60/// Represents the version of TLS/SSL used for the connection.
61#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
62#[non_exhaustive]
63pub enum TlsVersion {
64    /// TLS 1.0 (deprecated, insecure)
65    V1_0,
66    /// TLS 1.1 (deprecated, insecure)
67    V1_1,
68    /// TLS 1.2 (modern baseline)
69    V1_2,
70    /// TLS 1.3 (current standard)
71    V1_3,
72}
73
74impl fmt::Display for TlsVersion {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        match self {
77            Self::V1_0 => write!(f, "TLS 1.0"),
78            Self::V1_1 => write!(f, "TLS 1.1"),
79            Self::V1_2 => write!(f, "TLS 1.2"),
80            Self::V1_3 => write!(f, "TLS 1.3"),
81        }
82    }
83}
84
85/// TLS connection information extracted from HTTP request.
86///
87/// This struct captures the essential TLS/security information from an
88/// incoming connection. It's created by the HTTP adapter layer and passed
89/// to the enforcer for validation.
90#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct TlsConnection {
92    /// Whether the connection is over HTTPS/TLS (true) or HTTP (false)
93    pub is_secure: bool,
94
95    /// The TLS protocol version used (only valid if `is_secure=true`)
96    pub version: TlsVersion,
97
98    /// Whether a client certificate was presented
99    pub has_client_cert: bool,
100
101    /// Whether the client certificate has been validated by the server
102    /// (Note: The server does this validation; this flag indicates the result)
103    pub client_cert_valid: bool,
104}
105
106impl TlsConnection {
107    /// Create a new TLS connection info (typically HTTP, not secure)
108    #[must_use]
109    pub const fn new_http() -> Self {
110        Self {
111            is_secure:         false,
112            version:           TlsVersion::V1_2, // Irrelevant for HTTP
113            has_client_cert:   false,
114            client_cert_valid: false,
115        }
116    }
117
118    /// Create a new secure TLS connection
119    #[must_use]
120    pub const fn new_secure(version: TlsVersion) -> Self {
121        Self {
122            is_secure: true,
123            version,
124            has_client_cert: false,
125            client_cert_valid: false,
126        }
127    }
128
129    /// Create a new secure TLS connection with a valid client certificate
130    #[must_use]
131    pub const fn new_secure_with_client_cert(version: TlsVersion) -> Self {
132        Self {
133            is_secure: true,
134            version,
135            has_client_cert: true,
136            client_cert_valid: true,
137        }
138    }
139}
140
141/// TLS Security Configuration
142///
143/// Defines what TLS/SSL requirements must be met for a connection.
144/// This is typically derived from a `SecurityProfile`.
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct TlsConfig {
147    /// If true, all connections must be HTTPS (TLS required)
148    pub tls_required: bool,
149
150    /// If true, all connections must include a valid client certificate
151    pub mtls_required: bool,
152
153    /// Minimum allowed TLS version (enforce via check 2)
154    pub min_version: TlsVersion,
155}
156
157impl TlsConfig {
158    /// Create a permissive TLS configuration (for development/staging)
159    ///
160    /// - HTTPS optional
161    /// - Client certs optional
162    /// - TLS 1.2 minimum (if TLS is used)
163    #[must_use]
164    pub const fn permissive() -> Self {
165        Self {
166            tls_required:  false,
167            mtls_required: false,
168            min_version:   TlsVersion::V1_2,
169        }
170    }
171
172    /// Create a standard TLS configuration (for production)
173    ///
174    /// - HTTPS required
175    /// - Client certs optional
176    /// - TLS 1.2 minimum
177    #[must_use]
178    pub const fn standard() -> Self {
179        Self {
180            tls_required:  true,
181            mtls_required: false,
182            min_version:   TlsVersion::V1_2,
183        }
184    }
185
186    /// Create a strict TLS configuration (for regulated environments)
187    ///
188    /// - HTTPS required
189    /// - Client certs required (mTLS)
190    /// - TLS 1.3 minimum
191    #[must_use]
192    pub const fn strict() -> Self {
193        Self {
194            tls_required:  true,
195            mtls_required: true,
196            min_version:   TlsVersion::V1_3,
197        }
198    }
199}
200
201/// TLS Security Enforcer
202///
203/// Validates incoming connections against TLS security requirements.
204/// Used as the first layer in the security middleware pipeline.
205#[derive(Debug, Clone)]
206pub struct TlsEnforcer {
207    config: TlsConfig,
208}
209
210impl TlsEnforcer {
211    /// Create a new TLS enforcer from configuration
212    #[must_use]
213    pub const fn from_config(config: TlsConfig) -> Self {
214        Self { config }
215    }
216
217    /// Create enforcer with permissive settings (development)
218    #[must_use]
219    pub const fn permissive() -> Self {
220        Self::from_config(TlsConfig::permissive())
221    }
222
223    /// Create enforcer with standard settings (production)
224    #[must_use]
225    pub const fn standard() -> Self {
226        Self::from_config(TlsConfig::standard())
227    }
228
229    /// Create enforcer with strict settings (regulated)
230    #[must_use]
231    pub const fn strict() -> Self {
232        Self::from_config(TlsConfig::strict())
233    }
234
235    /// Validate a TLS connection against the enforcer's configuration.
236    ///
237    /// Performs 4 validation checks in order:
238    /// 1. HTTPS requirement (if `tls_required=true`, reject HTTP)
239    /// 2. Minimum TLS version (if secure, check version >= `min_version`)
240    /// 3. mTLS requirement (if `mtls_required=true`, require client cert)
241    /// 4. Client cert validity (if client cert present, it must be valid)
242    ///
243    /// # Errors
244    ///
245    /// Returns [`SecurityError::TlsRequired`] if the connection is HTTP but TLS is required.
246    /// Returns [`SecurityError::TlsVersionTooOld`] if the TLS version is below `min_version`.
247    /// Returns [`SecurityError::MtlsRequired`] if mTLS is required but no client cert is present.
248    /// Returns [`SecurityError::InvalidClientCert`] if a client cert is present but invalid.
249    pub fn validate_connection(&self, conn: &TlsConnection) -> Result<()> {
250        // Check 1: HTTPS requirement
251        if self.config.tls_required && !conn.is_secure {
252            return Err(SecurityError::TlsRequired {
253                detail: "HTTPS required, but connection is HTTP".to_string(),
254            });
255        }
256
257        // Check 2: TLS version minimum (only check if connection is secure)
258        if conn.is_secure && conn.version < self.config.min_version {
259            return Err(SecurityError::TlsVersionTooOld {
260                current:  conn.version,
261                required: self.config.min_version,
262            });
263        }
264
265        // Check 3: mTLS requirement
266        if self.config.mtls_required && !conn.has_client_cert {
267            return Err(SecurityError::MtlsRequired {
268                detail: "Client certificate required, but none provided".to_string(),
269            });
270        }
271
272        // Check 4: Client certificate validity
273        if conn.has_client_cert && !conn.client_cert_valid {
274            return Err(SecurityError::InvalidClientCert {
275                detail: "Client certificate provided but validation failed".to_string(),
276            });
277        }
278
279        Ok(())
280    }
281
282    /// Get the underlying configuration
283    #[must_use]
284    pub const fn config(&self) -> &TlsConfig {
285        &self.config
286    }
287}