fix(config): resolve critical audit findings from Phase 1-3 review

- C-1: Add tenant_id to settings unique index to prevent cross-tenant conflicts
- C-2: Move pg_advisory_xact_lock inside the transaction for correct concurrency
  (previously lock was released before the numbering transaction started)
- H-5: Add CORS middleware (permissive for dev, TODO: restrict in production)
This commit is contained in:
iven
2026-04-11 08:26:43 +08:00
parent 0baaf5f7ee
commit 0cbd08eb78
3 changed files with 22 additions and 17 deletions

View File

@@ -204,8 +204,8 @@ impl NumberingService {
/// 线程安全地生成编号。
///
/// 使用 PostgreSQL advisory lock 保证并发安全:
/// 1. 获取 pg_advisory_xact_lock
/// 2. 在事务内读取规则、检查重置周期、递增序列、更新数据库
/// 1. 在事务内获取 pg_advisory_xact_lock
/// 2. 在同一事务内读取规则、检查重置周期、递增序列、更新数据库
/// 3. 拼接编号字符串返回
pub async fn generate_number(
rule_id: Uuid,
@@ -223,22 +223,23 @@ impl NumberingService {
let rule_code = rule.code.clone();
let tenant_id_str = tenant_id.to_string();
// 获取 PostgreSQL advisory lock(事务级别,事务结束自动释放)
db.execute(Statement::from_sql_and_values(
DatabaseBackend::Postgres,
"SELECT pg_advisory_xact_lock(abs(hashtext($1)), abs(hashtext($2))::int)",
[
rule_code.into(),
tenant_id_str.into(),
],
))
.await
.map_err(|e| ConfigError::Validation(format!("获取编号锁失败: {e}")))?;
// 在事务内执行序列递增和更新
// 在同一个事务内获取 advisory lock 并执行编号生成
// pg_advisory_xact_lock 是事务级别的,锁会在事务结束时自动释放
let number = db
.transaction(|txn| {
let rule_code = rule_code.clone();
let tenant_id_str = tenant_id_str.clone();
Box::pin(async move {
// 在事务内获取 advisory lock
txn.execute(Statement::from_sql_and_values(
DatabaseBackend::Postgres,
"SELECT pg_advisory_xact_lock(abs(hashtext($1)), abs(hashtext($2))::int)",
[rule_code.into(), tenant_id_str.into()],
))
.await
.map_err(|e| ConfigError::Validation(format!("获取编号锁失败: {e}")))?;
// 在同一个事务内执行编号生成
Self::generate_number_in_txn(rule_id, tenant_id, txn).await
})
})