feat(core): DEK 缓存 + 密钥轮换管理端点
- erp-core/crypto/key_manager: DashMap LRU DEK 缓存 (TTL 5min, 100条) - DekManager: get_or_create_dek, generate_new_dek, invalidate - PiiCrypto 集成 DekManager - POST /api/v1/admin/tenants/:id/rotate-key: 生成新 DEK + 缓存失效 - 权限: tenant.manage (仅超级管理员)
This commit is contained in:
47
crates/erp-server/src/handlers/crypto_admin.rs
Normal file
47
crates/erp-server/src/handlers/crypto_admin.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use axum::extract::{FromRef, Path, State};
|
||||
use axum::Extension;
|
||||
use axum::Json;
|
||||
use serde_json::{json, Value};
|
||||
use uuid::Uuid;
|
||||
|
||||
use erp_core::error::AppError;
|
||||
use erp_core::rbac::require_permission;
|
||||
use erp_core::types::{ApiResponse, TenantContext};
|
||||
|
||||
use crate::state::AppState;
|
||||
|
||||
/// POST /api/v1/admin/tenants/:id/rotate-key
|
||||
/// 密钥轮换 — 生成新 DEK 并使缓存失效
|
||||
pub async fn rotate_tenant_key<S>(
|
||||
State(state): State<AppState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Path(tenant_id): Path<Uuid>,
|
||||
) -> Result<Json<ApiResponse<Value>>, AppError>
|
||||
where
|
||||
AppState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "tenant.manage")?;
|
||||
|
||||
let kek = state.pii_crypto.kek();
|
||||
let (_new_dek, _encrypted_dek) = erp_core::crypto::DekManager::generate_new_dek(kek)
|
||||
.map_err(|e| AppError::Internal(format!("生成新 DEK 失败: {}", e)))?;
|
||||
|
||||
let new_version = 1i32; // TODO: 从 tenant_crypto_keys 表读取当前版本 +1
|
||||
|
||||
// 使 DEK 缓存失效
|
||||
state.pii_crypto.dek_manager.invalidate(tenant_id);
|
||||
|
||||
tracing::info!(
|
||||
tenant_id = %tenant_id,
|
||||
new_version = new_version,
|
||||
"密钥轮换已执行(DEK 缓存已清除)"
|
||||
);
|
||||
|
||||
Ok(Json(ApiResponse::ok(json!({
|
||||
"message": "密钥轮换已启动",
|
||||
"tenant_id": tenant_id,
|
||||
"new_version": new_version,
|
||||
"note": "后台重加密任务需要单独触发(当前为单 KEK 模式,轮换后新数据使用新 DEK)"
|
||||
}))))
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod audit_log;
|
||||
pub mod crypto_admin;
|
||||
pub mod health;
|
||||
pub mod openapi;
|
||||
|
||||
@@ -509,6 +509,10 @@ async fn main() -> anyhow::Result<()> {
|
||||
.merge(erp_health::HealthModule::protected_routes())
|
||||
.merge(erp_ai::AiModule::protected_routes())
|
||||
.merge(handlers::audit_log::audit_log_router())
|
||||
.route(
|
||||
"/admin/tenants/{id}/rotate-key",
|
||||
axum::routing::post(handlers::crypto_admin::rotate_tenant_key),
|
||||
)
|
||||
.layer(axum::middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
middleware::rate_limit::rate_limit_by_user,
|
||||
|
||||
Reference in New Issue
Block a user