1use openlark_core::error::{
7 authentication_error, business_error, configuration_error, network_error_with_details,
8 permission_missing_error, rate_limit_error, token_expired_error, validation_error, CoreError,
9 ErrorCode, ErrorContext, ErrorTrait,
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!(
228 "安全配置参数 {} 无效: {}",
229 config_key_str, reason_str
230 ))
231 }
232
233 pub fn time_sync_failed(service: impl Into<String>, deviation_ms: i64) -> SecurityError {
235 let mut ctx = ErrorContext::new();
236 ctx.add_context("sync_service", service.into());
237 ctx.add_context("time_deviation_ms", deviation_ms.to_string());
238 ctx.add_context("operation", "time_sync");
239
240 business_error("时间同步失败,安全验证需要精确的时间同步")
241 }
242
243 pub fn crypto_operation_failed(
245 operation: impl Into<String>,
246 algorithm: impl Into<String>,
247 reason: impl Into<String>,
248 ) -> SecurityError {
249 let mut ctx = ErrorContext::new();
250 ctx.add_context("crypto_operation", operation.into());
251 ctx.add_context("algorithm", algorithm.into());
252 ctx.add_context("failure_reason", reason.into());
253 ctx.add_context("operation", "cryptography");
254
255 CoreError::Internal {
256 code: ErrorCode::InternalError,
257 message: "加密操作失败".to_string(),
258 source: None,
259 ctx: Box::new(ctx),
260 }
261 }
262
263 pub fn security_check_timeout(
265 check_type: impl Into<String>,
266 timeout_duration: Duration,
267 ) -> SecurityError {
268 let mut ctx = ErrorContext::new();
269 ctx.add_context("check_type", check_type.into());
270 ctx.add_context(
271 "timeout_duration_ms",
272 timeout_duration.as_millis().to_string(),
273 );
274 ctx.add_context("operation", "security_check");
275
276 CoreError::Timeout {
277 duration: timeout_duration,
278 operation: Some(format!(
279 "security_check:{}",
280 ctx.get_context("check_type").unwrap_or_default()
281 )),
282 ctx: Box::new(ctx),
283 }
284 }
285
286 pub fn security_api_rate_limited(
288 endpoint: impl Into<String>,
289 limit: u32,
290 window_seconds: u64,
291 ) -> SecurityError {
292 let mut ctx = ErrorContext::new();
293 ctx.add_context("api_endpoint", endpoint.into());
294 ctx.add_context("rate_limit", limit.to_string());
295 ctx.add_context("window_seconds", window_seconds.to_string());
296
297 rate_limit_error(limit, Duration::from_secs(window_seconds), None)
298 }
299}
300
301pub fn map_feishu_security_error(
303 feishu_code: i32,
304 message: &str,
305 request_id: Option<&str>,
306) -> SecurityError {
307 let mut ctx = ErrorContext::new();
308 if let Some(req_id) = request_id {
309 ctx.set_request_id(req_id);
310 }
311 ctx.add_context("feishu_code", feishu_code.to_string());
312 ctx.add_context("service", "security");
313
314 match ErrorCode::from_feishu_code(feishu_code) {
316 Some(ErrorCode::PermissionMissing) => CoreError::Authentication {
318 message: format!("安全权限不足: {}", message),
319 code: ErrorCode::PermissionMissing,
320 ctx: Box::new(ctx),
321 },
322 Some(ErrorCode::AccessTokenExpiredV2) => {
324 token_expired_error(format!("安全访问令牌已过期: {}", message))
325 }
326 Some(ErrorCode::ValidationError) => validation_error("security_parameter", message),
328 Some(ErrorCode::BusinessError) => {
330 SecurityErrorBuilder::compliance_check_failed("business_rule", message, None::<String>)
331 }
332 _ => {
334 CoreError::Api(Box::new(ApiError {
336 status: feishu_code as u16,
337 endpoint: "security".into(),
338 message: message.to_string(),
339 source: None,
340 code: ErrorCode::from_feishu_code(feishu_code).unwrap_or(ErrorCode::InternalError),
341 ctx: Box::new(ctx),
342 }))
343 }
344 }
345}
346
347pub trait SecurityErrorExt {
349 fn is_device_error(&self) -> bool;
351
352 fn is_permission_error(&self) -> bool;
354
355 fn is_compliance_error(&self) -> bool;
357
358 fn is_authentication_error(&self) -> bool;
360
361 fn security_operation(&self) -> Option<&str>;
363
364 fn affected_resource_id(&self) -> Option<&str>;
366
367 fn to_security_event(&self) -> SecurityEvent;
369}
370
371impl SecurityErrorExt for SecurityError {
372 fn is_device_error(&self) -> bool {
373 self.context().get_context("device_id").is_some()
374 }
375
376 fn is_permission_error(&self) -> bool {
377 matches!(
378 self,
379 CoreError::Authentication {
380 code: ErrorCode::PermissionMissing,
381 ..
382 }
383 )
384 }
385
386 fn is_compliance_error(&self) -> bool {
387 self.context().get_context("compliance_type").is_some()
388 }
389
390 fn is_authentication_error(&self) -> bool {
391 matches!(self, CoreError::Authentication { .. })
392 }
393
394 fn security_operation(&self) -> Option<&str> {
395 match self.context().get_context("operation") {
396 Some(s) => Some(s),
397 None => None,
398 }
399 }
400
401 fn affected_resource_id(&self) -> Option<&str> {
402 match self
403 .context()
404 .get_context("device_id")
405 .or_else(|| self.context().get_context("visitor_id"))
406 {
407 Some(s) => Some(s),
408 None => None,
409 }
410 }
411
412 fn to_security_event(&self) -> SecurityEvent {
413 SecurityEvent {
414 event_id: uuid::Uuid::new_v4().to_string(),
415 timestamp: chrono::Utc::now(),
416 event_type: "security_error".to_string(),
417 severity: "medium".to_string(),
418 operation: self.security_operation().unwrap_or("unknown").to_string(),
419 resource_id: self.affected_resource_id().map(|s| s.to_string()),
420 error_code: ErrorCode::InternalError,
421 message: "安全错误".to_string(),
422 context: serde_json::json!({}),
423 }
424 }
425}
426
427#[derive(Debug, Clone, Serialize)]
429pub struct SecurityEvent {
430 pub event_id: String,
432 pub timestamp: chrono::DateTime<chrono::Utc>,
434 pub event_type: String,
436 pub severity: String,
438 pub operation: String,
440 pub resource_id: Option<String>,
442 pub error_code: ErrorCode,
444 pub message: String,
446 pub context: serde_json::Value,
448}
449
450#[derive(Debug, Clone, Copy)]
452pub struct SecurityErrorAnalyzer;
453
454impl SecurityErrorAnalyzer {
455 pub fn analyze_security_risk(error: &SecurityError) -> SecurityRiskAssessment {
457 let risk_level = match error {
458 CoreError::Authentication { .. } if error.is_permission_error() => {
459 SecurityRiskLevel::High
460 }
461 CoreError::Business { .. } => SecurityRiskLevel::Critical,
462 CoreError::Internal { .. } => SecurityRiskLevel::High,
463 CoreError::Validation { .. } => SecurityRiskLevel::Medium,
464 _ => SecurityRiskLevel::Low,
465 };
466
467 let risk_type = if error.is_permission_error() {
468 SecurityRiskType::AccessControl
469 } else if error.is_compliance_error() {
470 SecurityRiskType::Compliance
471 } else if error.is_device_error() {
472 SecurityRiskType::DeviceSecurity
473 } else {
474 SecurityRiskType::General
475 };
476
477 SecurityRiskAssessment {
478 risk_level,
479 risk_type,
480 immediate_action: SecurityAction::LogAndMonitor,
481 escalation_required: matches!(
482 error,
483 CoreError::Business { .. } | CoreError::Internal { .. }
484 ),
485 compliance_impact: ComplianceImpact::Low,
486 }
487 }
488}
489
490#[derive(Debug, Clone, Copy, Serialize)]
492pub struct SecurityRiskAssessment {
493 pub risk_level: SecurityRiskLevel,
495 pub risk_type: SecurityRiskType,
497 pub immediate_action: SecurityAction,
499 pub escalation_required: bool,
501 pub compliance_impact: ComplianceImpact,
503}
504
505#[derive(Debug, Clone, Copy, Serialize)]
507pub enum SecurityRiskLevel {
508 Low,
510 Medium,
512 High,
514 Critical,
516}
517
518#[derive(Debug, Clone, Copy, Serialize)]
520pub enum SecurityRiskType {
521 AccessControl,
523 Authentication,
525 DeviceSecurity,
527 Compliance,
529 General,
531}
532
533#[derive(Debug, Clone, Copy, Serialize)]
535pub enum SecurityAction {
536 RevokeAccess,
538 InitiateInvestigation,
540 ActivateBackup,
542 LogAndMonitor,
544 BlockRequest,
546}
547
548#[derive(Debug, Clone, Copy, Serialize)]
550pub enum ComplianceImpact {
551 Low,
553 Medium,
555 High,
557}
558
559#[cfg(test)]
560mod tests {
561 use super::*;
562
563 #[test]
564 fn test_security_error_creation() {
565 let error = SecurityErrorBuilder::device_not_found("device_123");
566 assert!(error.is_validation_error());
567 assert!(error.is_device_error());
568 }
569
570 #[test]
571 fn test_permission_error() {
572 let error = SecurityErrorBuilder::access_denied("admin_panel", "insufficient_role");
573 assert!(error.is_permission_error());
574 }
575
576 #[test]
577 fn test_compliance_error() {
578 let error = SecurityErrorBuilder::compliance_check_failed(
579 "gdpr",
580 "data_retention_violation",
581 Some("data_set_456"),
582 );
583 assert!(error.is_compliance_error());
584 }
585
586 #[test]
587 fn test_security_event_generation() {
588 let error = SecurityErrorBuilder::device_not_found("device_123");
589 let event = error.to_security_event();
590 assert_eq!(event.message, "安全错误");
591 }
592
593 #[test]
594 fn test_feishu_error_mapping() {
595 let error = map_feishu_security_error(99991672, "权限不足", Some("req_123"));
596 assert!(error.is_permission_error());
597 }
598
599 #[test]
600 fn test_security_risk_assessment() {
601 let error = SecurityErrorBuilder::access_denied("secure_area", "no_clearance");
602 let assessment = SecurityErrorAnalyzer::analyze_security_risk(&error);
603 assert!(matches!(assessment.risk_level, SecurityRiskLevel::High));
604 }
605}