rustkernel_core/security/
mod.rs

1//! Security Infrastructure
2//!
3//! Enterprise security features for RustKernels:
4//!
5//! - **Authentication**: JWT/OAuth token validation
6//! - **Authorization**: Role-based access control (RBAC)
7//! - **Multi-tenancy**: Tenant isolation and resource quotas
8//! - **Secrets**: Secure credential management
9//!
10//! # Feature Flags
11//!
12//! - `auth`: Enable authentication features
13//! - `crypto`: Enable encryption features
14//! - `tls`: Enable TLS support
15//! - `multi-tenancy`: Enable multi-tenant isolation
16//!
17//! # Example
18//!
19//! ```rust,ignore
20//! use rustkernel_core::security::{AuthConfig, SecurityContext};
21//!
22//! let config = AuthConfig::jwt("secret-key");
23//! let ctx = SecurityContext::authenticate(&token, &config)?;
24//!
25//! // Check permissions
26//! ctx.require_permission(KernelPermission::Execute)?;
27//! ```
28
29pub mod auth;
30pub mod rbac;
31pub mod secrets;
32pub mod tenancy;
33
34pub use auth::{AuthConfig, AuthProvider, AuthToken, TokenClaims};
35pub use rbac::{KernelPermission, Permission, PermissionSet, Role, RoleBinding};
36pub use secrets::{SecretRef, SecretStore, SecretValue};
37pub use tenancy::{ResourceQuota, Tenant, TenantConfig, TenantId};
38
39use serde::{Deserialize, Serialize};
40use std::collections::HashSet;
41
42/// Unified security configuration
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct SecurityConfig {
45    /// Authentication configuration
46    pub auth: Option<AuthConfig>,
47    /// Enable RBAC
48    pub rbac_enabled: bool,
49    /// Enable multi-tenancy
50    pub multi_tenancy_enabled: bool,
51    /// Default tenant ID for unauthenticated requests
52    pub default_tenant: Option<TenantId>,
53    /// Enable audit logging for security events
54    pub audit_logging: bool,
55}
56
57impl Default for SecurityConfig {
58    fn default() -> Self {
59        Self {
60            auth: None,
61            rbac_enabled: false,
62            multi_tenancy_enabled: false,
63            default_tenant: None,
64            audit_logging: true,
65        }
66    }
67}
68
69impl SecurityConfig {
70    /// Create a new security config
71    pub fn new() -> Self {
72        Self::default()
73    }
74
75    /// Development configuration (minimal security)
76    pub fn development() -> Self {
77        Self::default()
78    }
79
80    /// Production configuration
81    pub fn production() -> Self {
82        Self {
83            auth: Some(AuthConfig::default()),
84            rbac_enabled: true,
85            multi_tenancy_enabled: true,
86            default_tenant: None,
87            audit_logging: true,
88        }
89    }
90
91    /// Set auth configuration
92    pub fn with_auth(mut self, config: AuthConfig) -> Self {
93        self.auth = Some(config);
94        self
95    }
96
97    /// Enable RBAC
98    pub fn with_rbac(mut self, enabled: bool) -> Self {
99        self.rbac_enabled = enabled;
100        self
101    }
102
103    /// Enable multi-tenancy
104    pub fn with_multi_tenancy(mut self, enabled: bool) -> Self {
105        self.multi_tenancy_enabled = enabled;
106        self
107    }
108
109    /// Set default tenant
110    pub fn with_default_tenant(mut self, tenant: TenantId) -> Self {
111        self.default_tenant = Some(tenant);
112        self
113    }
114}
115
116/// Security context for an authenticated request
117#[derive(Debug, Clone)]
118pub struct SecurityContext {
119    /// Authenticated user ID
120    pub user_id: Option<String>,
121    /// Tenant ID
122    pub tenant_id: Option<TenantId>,
123    /// Roles assigned to the user
124    pub roles: HashSet<Role>,
125    /// Permissions derived from roles
126    pub permissions: PermissionSet,
127    /// Token claims (if JWT auth)
128    pub claims: Option<TokenClaims>,
129    /// Whether this is a system/service context
130    pub is_system: bool,
131}
132
133impl SecurityContext {
134    /// Create an anonymous context
135    pub fn anonymous() -> Self {
136        Self {
137            user_id: None,
138            tenant_id: None,
139            roles: HashSet::new(),
140            permissions: PermissionSet::empty(),
141            claims: None,
142            is_system: false,
143        }
144    }
145
146    /// Create a system context with full permissions
147    pub fn system() -> Self {
148        Self {
149            user_id: Some("system".to_string()),
150            tenant_id: None,
151            roles: {
152                let mut roles = HashSet::new();
153                roles.insert(Role::Admin);
154                roles
155            },
156            permissions: PermissionSet::all(),
157            claims: None,
158            is_system: true,
159        }
160    }
161
162    /// Create a user context
163    pub fn user(user_id: impl Into<String>, tenant_id: Option<TenantId>) -> Self {
164        Self {
165            user_id: Some(user_id.into()),
166            tenant_id,
167            roles: HashSet::new(),
168            permissions: PermissionSet::empty(),
169            claims: None,
170            is_system: false,
171        }
172    }
173
174    /// Add a role
175    pub fn with_role(mut self, role: Role) -> Self {
176        let perms = role.permissions();
177        self.roles.insert(role);
178        // Update permissions based on role
179        self.permissions = self.permissions.union(&perms);
180        self
181    }
182
183    /// Add multiple roles
184    pub fn with_roles(mut self, roles: impl IntoIterator<Item = Role>) -> Self {
185        for role in roles {
186            let perms = role.permissions();
187            self.roles.insert(role);
188            self.permissions = self.permissions.union(&perms);
189        }
190        self
191    }
192
193    /// Set claims
194    pub fn with_claims(mut self, claims: TokenClaims) -> Self {
195        self.claims = Some(claims);
196        self
197    }
198
199    /// Check if user is authenticated
200    pub fn is_authenticated(&self) -> bool {
201        self.user_id.is_some()
202    }
203
204    /// Check if user has a permission
205    pub fn has_permission(&self, permission: Permission) -> bool {
206        self.is_system || self.permissions.contains(permission)
207    }
208
209    /// Check if user has a role
210    pub fn has_role(&self, role: &Role) -> bool {
211        self.is_system || self.roles.contains(role)
212    }
213
214    /// Require a permission, returning error if not granted
215    pub fn require_permission(&self, permission: Permission) -> Result<(), SecurityError> {
216        if self.has_permission(permission) {
217            Ok(())
218        } else {
219            Err(SecurityError::PermissionDenied {
220                permission: format!("{:?}", permission),
221                user_id: self.user_id.clone(),
222            })
223        }
224    }
225
226    /// Require authentication
227    pub fn require_authenticated(&self) -> Result<(), SecurityError> {
228        if self.is_authenticated() {
229            Ok(())
230        } else {
231            Err(SecurityError::Unauthenticated)
232        }
233    }
234
235    /// Check if context can access a specific tenant
236    pub fn can_access_tenant(&self, tenant_id: &TenantId) -> bool {
237        self.is_system || self.tenant_id.as_ref() == Some(tenant_id) || self.has_role(&Role::Admin)
238    }
239}
240
241impl Default for SecurityContext {
242    fn default() -> Self {
243        Self::anonymous()
244    }
245}
246
247/// Security errors
248#[derive(Debug, thiserror::Error)]
249pub enum SecurityError {
250    /// User is not authenticated
251    #[error("Authentication required")]
252    Unauthenticated,
253
254    /// Token validation failed
255    #[error("Invalid token: {reason}")]
256    InvalidToken {
257        /// The reason for token validation failure
258        reason: String,
259    },
260
261    /// Token has expired
262    #[error("Token expired")]
263    TokenExpired,
264
265    /// Permission denied
266    #[error("Permission denied: {permission} for user {user_id:?}")]
267    PermissionDenied {
268        /// The permission that was denied
269        permission: String,
270        /// The user ID if available
271        user_id: Option<String>,
272    },
273
274    /// Tenant access denied
275    #[error("Tenant access denied: {tenant_id}")]
276    TenantAccessDenied {
277        /// The tenant ID that was denied access
278        tenant_id: String,
279    },
280
281    /// Resource quota exceeded
282    #[error("Resource quota exceeded: {resource}")]
283    QuotaExceeded {
284        /// The resource that exceeded quota
285        resource: String,
286    },
287
288    /// Secret not found
289    #[error("Secret not found: {name}")]
290    SecretNotFound {
291        /// The name of the secret that was not found
292        name: String,
293    },
294
295    /// Encryption error
296    #[error("Encryption error: {reason}")]
297    EncryptionError {
298        /// The reason for encryption failure
299        reason: String,
300    },
301
302    /// Configuration error
303    #[error("Security configuration error: {reason}")]
304    ConfigError {
305        /// The configuration error reason
306        reason: String,
307    },
308}
309
310impl From<SecurityError> for crate::error::KernelError {
311    fn from(e: SecurityError) -> Self {
312        match e {
313            SecurityError::Unauthenticated
314            | SecurityError::InvalidToken { .. }
315            | SecurityError::TokenExpired
316            | SecurityError::PermissionDenied { .. }
317            | SecurityError::TenantAccessDenied { .. } => {
318                crate::error::KernelError::Unauthorized(e.to_string())
319            }
320            SecurityError::QuotaExceeded { .. } => {
321                crate::error::KernelError::ResourceExhausted(e.to_string())
322            }
323            _ => crate::error::KernelError::ConfigError(e.to_string()),
324        }
325    }
326}
327
328#[cfg(test)]
329mod tests {
330    use super::*;
331
332    #[test]
333    fn test_anonymous_context() {
334        let ctx = SecurityContext::anonymous();
335        assert!(!ctx.is_authenticated());
336        assert!(!ctx.is_system);
337    }
338
339    #[test]
340    fn test_system_context() {
341        let ctx = SecurityContext::system();
342        assert!(ctx.is_authenticated());
343        assert!(ctx.is_system);
344        assert!(ctx.has_permission(Permission::KernelExecute));
345        assert!(ctx.has_permission(Permission::KernelAdmin));
346    }
347
348    #[test]
349    fn test_user_context() {
350        let ctx = SecurityContext::user("user-123", Some(TenantId::new("tenant-456")))
351            .with_role(Role::User);
352
353        assert!(ctx.is_authenticated());
354        assert!(!ctx.is_system);
355        assert_eq!(ctx.user_id.as_deref(), Some("user-123"));
356        assert!(ctx.has_permission(Permission::KernelExecute));
357        assert!(!ctx.has_permission(Permission::KernelAdmin));
358    }
359
360    #[test]
361    fn test_permission_check() {
362        let ctx = SecurityContext::user("user-123", None).with_role(Role::User);
363
364        assert!(ctx.require_permission(Permission::KernelExecute).is_ok());
365        assert!(ctx.require_permission(Permission::KernelAdmin).is_err());
366    }
367
368    #[test]
369    fn test_tenant_access() {
370        let tenant = TenantId::new("tenant-123");
371        let ctx = SecurityContext::user("user-123", Some(tenant.clone()));
372
373        assert!(ctx.can_access_tenant(&tenant));
374        assert!(!ctx.can_access_tenant(&TenantId::new("other-tenant")));
375    }
376}