Skip to main content

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