rustkernel_core/security/
mod.rs1pub 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#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct SecurityConfig {
45 pub auth: Option<AuthConfig>,
47 pub rbac_enabled: bool,
49 pub multi_tenancy_enabled: bool,
51 pub default_tenant: Option<TenantId>,
53 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 pub fn new() -> Self {
72 Self::default()
73 }
74
75 pub fn development() -> Self {
77 Self::default()
78 }
79
80 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 pub fn with_auth(mut self, config: AuthConfig) -> Self {
93 self.auth = Some(config);
94 self
95 }
96
97 pub fn with_rbac(mut self, enabled: bool) -> Self {
99 self.rbac_enabled = enabled;
100 self
101 }
102
103 pub fn with_multi_tenancy(mut self, enabled: bool) -> Self {
105 self.multi_tenancy_enabled = enabled;
106 self
107 }
108
109 pub fn with_default_tenant(mut self, tenant: TenantId) -> Self {
111 self.default_tenant = Some(tenant);
112 self
113 }
114}
115
116#[derive(Debug, Clone)]
118pub struct SecurityContext {
119 pub user_id: Option<String>,
121 pub tenant_id: Option<TenantId>,
123 pub roles: HashSet<Role>,
125 pub permissions: PermissionSet,
127 pub claims: Option<TokenClaims>,
129 pub is_system: bool,
131}
132
133impl SecurityContext {
134 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 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 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 pub fn with_role(mut self, role: Role) -> Self {
176 let perms = role.permissions();
177 self.roles.insert(role);
178 self.permissions = self.permissions.union(&perms);
180 self
181 }
182
183 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 pub fn with_claims(mut self, claims: TokenClaims) -> Self {
195 self.claims = Some(claims);
196 self
197 }
198
199 pub fn is_authenticated(&self) -> bool {
201 self.user_id.is_some()
202 }
203
204 pub fn has_permission(&self, permission: Permission) -> bool {
206 self.is_system || self.permissions.contains(permission)
207 }
208
209 pub fn has_role(&self, role: &Role) -> bool {
211 self.is_system || self.roles.contains(role)
212 }
213
214 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 pub fn require_authenticated(&self) -> Result<(), SecurityError> {
228 if self.is_authenticated() {
229 Ok(())
230 } else {
231 Err(SecurityError::Unauthenticated)
232 }
233 }
234
235 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#[derive(Debug, thiserror::Error)]
249pub enum SecurityError {
250 #[error("Authentication required")]
252 Unauthenticated,
253
254 #[error("Invalid token: {reason}")]
256 InvalidToken {
257 reason: String,
259 },
260
261 #[error("Token expired")]
263 TokenExpired,
264
265 #[error("Permission denied: {permission} for user {user_id:?}")]
267 PermissionDenied {
268 permission: String,
270 user_id: Option<String>,
272 },
273
274 #[error("Tenant access denied: {tenant_id}")]
276 TenantAccessDenied {
277 tenant_id: String,
279 },
280
281 #[error("Resource quota exceeded: {resource}")]
283 QuotaExceeded {
284 resource: String,
286 },
287
288 #[error("Secret not found: {name}")]
290 SecretNotFound {
291 name: String,
293 },
294
295 #[error("Encryption error: {reason}")]
297 EncryptionError {
298 reason: String,
300 },
301
302 #[error("Security configuration error: {reason}")]
304 ConfigError {
305 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}