fix(saas): P0 安全修复 + P1 功能补全 — 角色提升、Admin 引导、IP 记录、密码修改
P0 安全修复: - 修复 account update 自角色提升漏洞: 非 admin 用户更新自己时剥离 role 字段 - 添加 Admin 引导机制: accounts 表为空时自动从环境变量创建 super_admin P1 功能补全: - 所有 17 个 log_operation 调用点传入真实客户端 IP (ConnectInfo + X-Forwarded-For) - AuthContext 新增 client_ip 字段, middleware 层自动提取 - main.rs 使用 into_make_service_with_connect_info 启用 SocketAddr 注入 - 新增 PUT /api/v1/auth/password 密码修改端点 (验证旧密码 + argon2 哈希) - 桌面端 SaaS 设置页添加密码修改 UI (折叠式表单) - SaaSClient 添加 changePassword() 方法 - 集成测试修复: 注入模拟 ConnectInfo 适配 onshot 测试模式
This commit is contained in:
@@ -38,7 +38,7 @@ pub async fn create_provider(
|
||||
check_permission(&ctx, "provider:manage")?;
|
||||
let provider = service::create_provider(&state.db, &req).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "provider.create", "provider", &provider.id,
|
||||
Some(serde_json::json!({"name": &req.name})), None).await?;
|
||||
Some(serde_json::json!({"name": &req.name})), ctx.client_ip.as_deref()).await?;
|
||||
Ok((StatusCode::CREATED, Json(provider)))
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ pub async fn update_provider(
|
||||
) -> SaasResult<Json<ProviderInfo>> {
|
||||
check_permission(&ctx, "provider:manage")?;
|
||||
let provider = service::update_provider(&state.db, &id, &req).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "provider.update", "provider", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "provider.update", "provider", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(provider))
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub async fn delete_provider(
|
||||
) -> SaasResult<Json<serde_json::Value>> {
|
||||
check_permission(&ctx, "provider:manage")?;
|
||||
service::delete_provider(&state.db, &id).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "provider.delete", "provider", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "provider.delete", "provider", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(serde_json::json!({"ok": true})))
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ pub async fn create_model(
|
||||
check_permission(&ctx, "model:manage")?;
|
||||
let model = service::create_model(&state.db, &req).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "model.create", "model", &model.id,
|
||||
Some(serde_json::json!({"model_id": &req.model_id, "provider_id": &req.provider_id})), None).await?;
|
||||
Some(serde_json::json!({"model_id": &req.model_id, "provider_id": &req.provider_id})), ctx.client_ip.as_deref()).await?;
|
||||
Ok((StatusCode::CREATED, Json(model)))
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ pub async fn update_model(
|
||||
) -> SaasResult<Json<ModelInfo>> {
|
||||
check_permission(&ctx, "model:manage")?;
|
||||
let model = service::update_model(&state.db, &id, &req).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "model.update", "model", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "model.update", "model", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(model))
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ pub async fn delete_model(
|
||||
) -> SaasResult<Json<serde_json::Value>> {
|
||||
check_permission(&ctx, "model:manage")?;
|
||||
service::delete_model(&state.db, &id).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "model.delete", "model", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "model.delete", "model", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(serde_json::json!({"ok": true})))
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ pub async fn create_api_key(
|
||||
) -> SaasResult<(StatusCode, Json<AccountApiKeyInfo>)> {
|
||||
let key = service::create_account_api_key(&state.db, &ctx.account_id, &req).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "api_key.create", "api_key", &key.id,
|
||||
Some(serde_json::json!({"provider_id": &req.provider_id})), None).await?;
|
||||
Some(serde_json::json!({"provider_id": &req.provider_id})), ctx.client_ip.as_deref()).await?;
|
||||
Ok((StatusCode::CREATED, Json(key)))
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ pub async fn rotate_api_key(
|
||||
Json(req): Json<RotateApiKeyRequest>,
|
||||
) -> SaasResult<Json<serde_json::Value>> {
|
||||
service::rotate_account_api_key(&state.db, &id, &ctx.account_id, &req.new_key_value).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "api_key.rotate", "api_key", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "api_key.rotate", "api_key", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(serde_json::json!({"ok": true})))
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ pub async fn revoke_api_key(
|
||||
Extension(ctx): Extension<AuthContext>,
|
||||
) -> SaasResult<Json<serde_json::Value>> {
|
||||
service::revoke_account_api_key(&state.db, &id, &ctx.account_id).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "api_key.revoke", "api_key", &id, None, None).await?;
|
||||
log_operation(&state.db, &ctx.account_id, "api_key.revoke", "api_key", &id, None, ctx.client_ip.as_deref()).await?;
|
||||
Ok(Json(serde_json::json!({"ok": true})))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user