1use openlark_core::error::{
7 CoreError, ErrorCode, ErrorContext, ErrorTrait, authentication_error, business_error,
8 configuration_error, network_error_with_details, permission_missing_error, rate_limit_error,
9 token_expired_error, validation_error,
10};
11use serde::Serialize;
12use std::time::Duration;
13
14use openlark_core::error::ApiError;
16
17pub type SecurityError = CoreError;
19
20pub type SecurityResult<T> = Result<T, SecurityError>;
22
23#[derive(Debug, Copy, Clone)]
25pub struct SecurityErrorBuilder;
26
27impl SecurityErrorBuilder {
28 pub fn device_not_found(device_id: impl Into<String>) -> SecurityError {
30 let mut ctx = ErrorContext::new();
31 ctx.add_context("device_id", device_id.into());
32 ctx.add_context("operation", "device_lookup");
33
34 CoreError::Validation {
35 field: "device_id".into(),
36 message: "设备未找到,请检查设备ID是否正确".to_string(),
37 code: ErrorCode::ValidationError,
38 ctx: Box::new(ctx),
39 }
40 }
41
42 pub fn device_connection_failed(
44 device_id: impl Into<String>,
45 reason: impl Into<String>,
46 ) -> SecurityError {
47 let mut ctx = ErrorContext::new();
48 ctx.add_context("device_id", device_id.into());
49 ctx.add_context("connection_reason", reason.into());
50 ctx.add_context("operation", "device_connection");
51
52 network_error_with_details(
53 "设备连接失败",
54 None::<String>,
55 Some(format!(
56 "device:{}",
57 ctx.get_context("device_id").unwrap_or_default()
58 )),
59 )
60 }
61
62 pub fn device_temporarily_unavailable(
64 device_id: impl Into<String>,
65 retry_after: Option<Duration>,
66 ) -> SecurityError {
67 let mut ctx = ErrorContext::new();
68 ctx.add_context("device_id", device_id.into());
69 ctx.add_context("availability", "temporary");
70
71 CoreError::ServiceUnavailable {
72 service: "security_device".into(),
73 retry_after,
74 code: ErrorCode::ServiceUnavailable,
75 ctx: Box::new(ctx),
76 }
77 }
78
79 pub fn access_denied(resource: impl Into<String>, reason: impl Into<String>) -> SecurityError {
81 let mut ctx = ErrorContext::new();
82 ctx.add_context("resource", resource.into());
83 ctx.add_context("deny_reason", reason.into());
84 ctx.add_context("operation", "access_control");
85
86 permission_missing_error(&["security:access"])
87 }
88
89 pub fn insufficient_permissions(
91 required_permissions: &[impl AsRef<str>],
92 current_permissions: &[impl AsRef<str>],
93 ) -> SecurityError {
94 let mut ctx = ErrorContext::new();
95 ctx.add_context(
96 "required_permissions",
97 required_permissions
98 .iter()
99 .map(|s| s.as_ref())
100 .collect::<Vec<_>>()
101 .join(","),
102 );
103 ctx.add_context(
104 "current_permissions",
105 current_permissions
106 .iter()
107 .map(|s| s.as_ref())
108 .collect::<Vec<_>>()
109 .join(","),
110 );
111
112 CoreError::Authentication {
113 message: "安全权限不足".to_string(),
114 code: ErrorCode::PermissionMissing,
115 ctx: Box::new(ctx),
116 }
117 }
118
119 pub fn face_recognition_failed(
121 reason: impl Into<String>,
122 image_id: Option<impl Into<String>>,
123 ) -> SecurityError {
124 let mut ctx = ErrorContext::new();
125 ctx.add_context("recognition_reason", reason.into());
126 if let Some(id) = image_id {
127 ctx.add_context("image_id", id.into());
128 }
129 ctx.add_context("operation", "face_recognition");
130
131 validation_error("face_image", "人脸识别失败,请重新上传清晰的人脸照片")
132 }
133
134 pub fn face_recognition_service_unavailable() -> SecurityError {
136 let mut ctx = ErrorContext::new();
137 ctx.add_context("service", "face_recognition");
138 ctx.add_context("operation", "face_recognition");
139
140 CoreError::ServiceUnavailable {
141 service: "face_recognition".into(),
142 retry_after: Some(Duration::from_secs(30)),
143 code: ErrorCode::ServiceUnavailable,
144 ctx: Box::new(ctx),
145 }
146 }
147
148 pub fn visitor_permission_expired(
150 visitor_id: impl Into<String>,
151 visit_type: impl Into<String>,
152 ) -> SecurityError {
153 let mut ctx = ErrorContext::new();
154 ctx.add_context("visitor_id", visitor_id.into());
155 ctx.add_context("visit_type", visit_type.into());
156 ctx.add_context("operation", "visitor_access");
157
158 business_error("访客权限已过期,请重新申请")
159 }
160
161 pub fn visitor_authentication_failed(
163 visitor_id: impl Into<String>,
164 reason: impl Into<String>,
165 ) -> SecurityError {
166 let mut ctx = ErrorContext::new();
167 ctx.add_context("visitor_id", visitor_id.into());
168 ctx.add_context("auth_reason", reason.into());
169 ctx.add_context("operation", "visitor_authentication");
170
171 authentication_error("访客身份验证失败")
172 }
173
174 pub fn compliance_check_failed(
176 compliance_type: impl Into<String>,
177 reason: impl Into<String>,
178 resource_id: Option<impl Into<String>>,
179 ) -> SecurityError {
180 let compliance_type_str = compliance_type.into();
181 let reason_str = reason.into();
182 let mut ctx = ErrorContext::new();
183 ctx.add_context("compliance_type", compliance_type_str);
184 ctx.add_context("violation_reason", reason_str.clone());
185 if let Some(id) = resource_id {
186 ctx.add_context("resource_id", id.into());
187 }
188 ctx.add_context("operation", "compliance_check");
189
190 CoreError::Business {
191 message: format!("合规检查失败: {reason_str}"),
192 code: ErrorCode::BusinessError,
193 ctx: Box::new(ctx),
194 }
195 }
196
197 pub fn audit_log_failed(
199 log_type: impl Into<String>,
200 reason: impl Into<String>,
201 ) -> SecurityError {
202 let mut ctx = ErrorContext::new();
203 ctx.add_context("log_type", log_type.into());
204 ctx.add_context("failure_reason", reason.into());
205 ctx.add_context("operation", "audit_logging");
206
207 CoreError::Internal {
208 code: ErrorCode::InternalError,
209 message: "审计日志写入失败".to_string(),
210 source: None,
211 ctx: Box::new(ctx),
212 }
213 }
214
215 pub fn security_config_invalid(
217 config_key: impl Into<String>,
218 reason: impl Into<String>,
219 ) -> SecurityError {
220 let config_key_str = config_key.into();
221 let reason_str = reason.into();
222 let mut ctx = ErrorContext::new();
223 ctx.add_context("config_key", config_key_str.clone());
224 ctx.add_context("error_reason", reason_str.clone());
225 ctx.add_context("operation", "security_config");
226
227 configuration_error(format!("安全配置参数 {config_key_str} 无效: {reason_str}"))
228 }
229
230 pub fn time_sync_failed(service: impl Into<String>, deviation_ms: i64) -> SecurityError {
232 let mut ctx = ErrorContext::new();
233 ctx.add_context("sync_service", service.into());
234 ctx.add_context("time_deviation_ms", deviation_ms.to_string());
235 ctx.add_context("operation", "time_sync");
236
237 business_error("时间同步失败,安全验证需要精确的时间同步")
238 }
239
240 pub fn crypto_operation_failed(
242 operation: impl Into<String>,
243 algorithm: impl Into<String>,
244 reason: impl Into<String>,
245 ) -> SecurityError {
246 let mut ctx = ErrorContext::new();
247 ctx.add_context("crypto_operation", operation.into());
248 ctx.add_context("algorithm", algorithm.into());
249 ctx.add_context("failure_reason", reason.into());
250 ctx.add_context("operation", "cryptography");
251
252 CoreError::Internal {
253 code: ErrorCode::InternalError,
254 message: "加密操作失败".to_string(),
255 source: None,
256 ctx: Box::new(ctx),
257 }
258 }
259
260 pub fn security_check_timeout(
262 check_type: impl Into<String>,
263 timeout_duration: Duration,
264 ) -> SecurityError {
265 let mut ctx = ErrorContext::new();
266 ctx.add_context("check_type", check_type.into());
267 ctx.add_context(
268 "timeout_duration_ms",
269 timeout_duration.as_millis().to_string(),
270 );
271 ctx.add_context("operation", "security_check");
272
273 CoreError::Timeout {
274 duration: timeout_duration,
275 operation: Some(format!(
276 "security_check:{}",
277 ctx.get_context("check_type").unwrap_or_default()
278 )),
279 ctx: Box::new(ctx),
280 }
281 }
282
283 pub fn security_api_rate_limited(
285 endpoint: impl Into<String>,
286 limit: u32,
287 window_seconds: u64,
288 ) -> SecurityError {
289 let mut ctx = ErrorContext::new();
290 ctx.add_context("api_endpoint", endpoint.into());
291 ctx.add_context("rate_limit", limit.to_string());
292 ctx.add_context("window_seconds", window_seconds.to_string());
293
294 rate_limit_error(limit, Duration::from_secs(window_seconds), None)
295 }
296}
297
298pub fn map_feishu_security_error(
300 feishu_code: i32,
301 message: &str,
302 request_id: Option<&str>,
303) -> SecurityError {
304 let mut ctx = ErrorContext::new();
305 if let Some(req_id) = request_id {
306 ctx.set_request_id(req_id);
307 }
308 ctx.add_context("feishu_code", feishu_code.to_string());
309 ctx.add_context("service", "security");
310
311 match ErrorCode::from_feishu_code(feishu_code) {
313 Some(ErrorCode::PermissionMissing) => CoreError::Authentication {
315 message: format!("安全权限不足: {message}"),
316 code: ErrorCode::PermissionMissing,
317 ctx: Box::new(ctx),
318 },
319 Some(ErrorCode::AccessTokenExpiredV2) => {
321 token_expired_error(format!("安全访问令牌已过期: {message}"))
322 }
323 Some(ErrorCode::ValidationError) => validation_error("security_parameter", message),
325 Some(ErrorCode::BusinessError) => {
327 SecurityErrorBuilder::compliance_check_failed("business_rule", message, None::<String>)
328 }
329 _ => {
331 CoreError::Api(Box::new(ApiError {
333 status: feishu_code as u16,
334 endpoint: "security".into(),
335 message: message.to_string(),
336 source: None,
337 code: ErrorCode::from_feishu_code(feishu_code).unwrap_or(ErrorCode::InternalError),
338 ctx: Box::new(ctx),
339 }))
340 }
341 }
342}
343
344pub trait SecurityErrorExt {
346 fn is_device_error(&self) -> bool;
348
349 fn is_permission_error(&self) -> bool;
351
352 fn is_compliance_error(&self) -> bool;
354
355 fn is_authentication_error(&self) -> bool;
357
358 fn security_operation(&self) -> Option<&str>;
360
361 fn affected_resource_id(&self) -> Option<&str>;
363
364 fn to_security_event(&self) -> SecurityEvent;
366}
367
368impl SecurityErrorExt for SecurityError {
369 fn is_device_error(&self) -> bool {
370 self.context().get_context("device_id").is_some()
371 }
372
373 fn is_permission_error(&self) -> bool {
374 matches!(
375 self,
376 CoreError::Authentication {
377 code: ErrorCode::PermissionMissing,
378 ..
379 }
380 )
381 }
382
383 fn is_compliance_error(&self) -> bool {
384 self.context().get_context("compliance_type").is_some()
385 }
386
387 fn is_authentication_error(&self) -> bool {
388 matches!(self, CoreError::Authentication { .. })
389 }
390
391 fn security_operation(&self) -> Option<&str> {
392 match self.context().get_context("operation") {
393 Some(s) => Some(s),
394 None => None,
395 }
396 }
397
398 fn affected_resource_id(&self) -> Option<&str> {
399 match self
400 .context()
401 .get_context("device_id")
402 .or_else(|| self.context().get_context("visitor_id"))
403 {
404 Some(s) => Some(s),
405 None => None,
406 }
407 }
408
409 fn to_security_event(&self) -> SecurityEvent {
410 SecurityEvent {
411 event_id: uuid::Uuid::new_v4().to_string(),
412 timestamp: chrono::Utc::now(),
413 event_type: "security_error".to_string(),
414 severity: "medium".to_string(),
415 operation: self.security_operation().unwrap_or("unknown").to_string(),
416 resource_id: self.affected_resource_id().map(|s| s.to_string()),
417 error_code: ErrorCode::InternalError,
418 message: "安全错误".to_string(),
419 context: serde_json::json!({}),
420 }
421 }
422}
423
424#[derive(Debug, Clone, Serialize)]
426pub struct SecurityEvent {
427 pub event_id: String,
429 pub timestamp: chrono::DateTime<chrono::Utc>,
431 pub event_type: String,
433 pub severity: String,
435 pub operation: String,
437 pub resource_id: Option<String>,
439 pub error_code: ErrorCode,
441 pub message: String,
443 pub context: serde_json::Value,
445}
446
447#[derive(Debug, Clone, Copy)]
449pub struct SecurityErrorAnalyzer;
450
451impl SecurityErrorAnalyzer {
452 pub fn analyze_security_risk(error: &SecurityError) -> SecurityRiskAssessment {
454 let risk_level = match error {
455 CoreError::Authentication { .. } if error.is_permission_error() => {
456 SecurityRiskLevel::High
457 }
458 CoreError::Business { .. } => SecurityRiskLevel::Critical,
459 CoreError::Internal { .. } => SecurityRiskLevel::High,
460 CoreError::Validation { .. } => SecurityRiskLevel::Medium,
461 _ => SecurityRiskLevel::Low,
462 };
463
464 let risk_type = if error.is_permission_error() {
465 SecurityRiskType::AccessControl
466 } else if error.is_compliance_error() {
467 SecurityRiskType::Compliance
468 } else if error.is_device_error() {
469 SecurityRiskType::DeviceSecurity
470 } else {
471 SecurityRiskType::General
472 };
473
474 SecurityRiskAssessment {
475 risk_level,
476 risk_type,
477 immediate_action: SecurityAction::LogAndMonitor,
478 escalation_required: matches!(
479 error,
480 CoreError::Business { .. } | CoreError::Internal { .. }
481 ),
482 compliance_impact: ComplianceImpact::Low,
483 }
484 }
485}
486
487#[derive(Debug, Clone, Copy, Serialize)]
489pub struct SecurityRiskAssessment {
490 pub risk_level: SecurityRiskLevel,
492 pub risk_type: SecurityRiskType,
494 pub immediate_action: SecurityAction,
496 pub escalation_required: bool,
498 pub compliance_impact: ComplianceImpact,
500}
501
502#[derive(Debug, Clone, Copy, Serialize)]
504pub enum SecurityRiskLevel {
505 Low,
507 Medium,
509 High,
511 Critical,
513}
514
515#[derive(Debug, Clone, Copy, Serialize)]
517pub enum SecurityRiskType {
518 AccessControl,
520 Authentication,
522 DeviceSecurity,
524 Compliance,
526 General,
528}
529
530#[derive(Debug, Clone, Copy, Serialize)]
532pub enum SecurityAction {
533 RevokeAccess,
535 InitiateInvestigation,
537 ActivateBackup,
539 LogAndMonitor,
541 BlockRequest,
543}
544
545#[derive(Debug, Clone, Copy, Serialize)]
547pub enum ComplianceImpact {
548 Low,
550 Medium,
552 High,
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559
560 #[test]
561 fn test_security_error_creation() {
562 let error = SecurityErrorBuilder::device_not_found("device_123");
563 assert!(error.is_validation_error());
564 assert!(error.is_device_error());
565 }
566
567 #[test]
568 fn test_permission_error() {
569 let error = SecurityErrorBuilder::access_denied("admin_panel", "insufficient_role");
570 assert!(error.is_permission_error());
571 }
572
573 #[test]
574 fn test_compliance_error() {
575 let error = SecurityErrorBuilder::compliance_check_failed(
576 "gdpr",
577 "data_retention_violation",
578 Some("data_set_456"),
579 );
580 assert!(error.is_compliance_error());
581 }
582
583 #[test]
584 fn test_security_event_generation() {
585 let error = SecurityErrorBuilder::device_not_found("device_123");
586 let event = error.to_security_event();
587 assert_eq!(event.message, "安全错误");
588 }
589
590 #[test]
591 fn test_feishu_error_mapping() {
592 let error = map_feishu_security_error(99991672, "权限不足", Some("req_123"));
593 assert!(error.is_permission_error());
594 }
595
596 #[test]
597 fn test_security_risk_assessment() {
598 let error = SecurityErrorBuilder::access_denied("secure_area", "no_clearance");
599 let assessment = SecurityErrorAnalyzer::analyze_security_risk(&error);
600 assert!(matches!(assessment.risk_level, SecurityRiskLevel::High));
601 }
602}