```
feat: 添加飞书集成和审计API密钥认证 - 在数据库配置中添加飞书模型导入 - 添加审计API密钥配置项和认证中间件 - 实现飞书事件重复处理防止机制 - 为审批路由添加API密钥认证 - 优化AI适配器错误处理并添加JSON解析异常捕获 - 更新测试用例以包含新的认证和事件处理逻辑 ```
This commit is contained in:
@@ -15,6 +15,8 @@ _db.close()
|
||||
|
||||
os.environ["DATABASE_URL"] = "sqlite:///" + _db.name.replace("\\", "/")
|
||||
os.environ["API_KEY"] = "test-key"
|
||||
os.environ["AUDIT_API_KEY"] = "audit-key"
|
||||
os.environ["AUDIT_API_ACTOR"] = "audit-manager"
|
||||
os.environ["APPROVAL_API_KEY"] = "approval-key"
|
||||
os.environ["APPROVAL_API_ACTOR"] = "approval-manager"
|
||||
os.environ["FEISHU_APP_ID"] = ""
|
||||
@@ -31,7 +33,7 @@ from fastapi.testclient import TestClient
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.core.database import Base, engine
|
||||
from app.core.pagination import bounded_limit, bounded_offset
|
||||
from app.core.security import require_api_key, require_approval_api_key
|
||||
from app.core.security import require_api_key, require_approval_api_key, require_audit_api_key
|
||||
from app.main import _allow_cors_credentials, app
|
||||
from app.modules.audit.constants import AUDIT_REDACTED_VALUE
|
||||
from app.modules.legacy_mysql.service import LegacyMySQLService
|
||||
@@ -48,6 +50,7 @@ from app.modules.reports.constants import (
|
||||
Base.metadata.create_all(bind=engine)
|
||||
client = TestClient(app)
|
||||
headers = {"X-API-Key": "test-key"}
|
||||
audit_headers = {"X-API-Key": "test-key", "X-Audit-API-Key": "audit-key"}
|
||||
approval_headers = {"X-API-Key": "test-key", "X-Approval-API-Key": "approval-key"}
|
||||
|
||||
|
||||
@@ -93,11 +96,16 @@ def test_project_report_and_feishu_command_preview() -> None:
|
||||
def test_feishu_webhook_routes_message_event() -> None:
|
||||
payload = {
|
||||
"schema": "2.0",
|
||||
"header": {"event_type": "im.message.receive_v1", "token": "test-feishu-token"},
|
||||
"header": {
|
||||
"event_id": "evt-smoke-risk-001",
|
||||
"event_type": "im.message.receive_v1",
|
||||
"token": "test-feishu-token",
|
||||
},
|
||||
"event": {
|
||||
"sender": {"sender_id": {"open_id": "ou_test"}},
|
||||
"message": {
|
||||
"chat_id": "oc_test",
|
||||
"message_id": "om_smoke_risk_001",
|
||||
"message_type": "text",
|
||||
"content": json.dumps({"text": "risk"}),
|
||||
},
|
||||
@@ -109,7 +117,14 @@ def test_feishu_webhook_routes_message_event() -> None:
|
||||
assert data["handled"] is True
|
||||
assert data["result"]["command"] == "risk_summary"
|
||||
|
||||
logs_response = client.get("/api/v1/audit/logs", headers=headers)
|
||||
duplicate_response = client.post("/api/v1/integrations/feishu/webhook", json=payload)
|
||||
assert duplicate_response.status_code == 200
|
||||
assert duplicate_response.json()["duplicate"] is True
|
||||
|
||||
blocked_logs_response = client.get("/api/v1/audit/logs", headers=headers)
|
||||
assert blocked_logs_response.status_code == 401
|
||||
|
||||
logs_response = client.get("/api/v1/audit/logs", headers=audit_headers)
|
||||
assert logs_response.status_code == 200
|
||||
audit_payload = json.dumps(logs_response.json(), ensure_ascii=False)
|
||||
assert "test-feishu-token" not in audit_payload
|
||||
@@ -138,8 +153,15 @@ def test_api_key_and_feishu_webhook_fail_closed(monkeypatch) -> None:
|
||||
with pytest.raises(HTTPException) as approval_exc_info:
|
||||
require_approval_api_key("approval-key")
|
||||
assert approval_exc_info.value.status_code == 503
|
||||
|
||||
monkeypatch.setenv("AUDIT_API_KEY", "")
|
||||
get_settings.cache_clear()
|
||||
with pytest.raises(HTTPException) as audit_exc_info:
|
||||
require_audit_api_key("audit-key")
|
||||
assert audit_exc_info.value.status_code == 503
|
||||
finally:
|
||||
monkeypatch.setenv("API_KEY", "test-key")
|
||||
monkeypatch.setenv("AUDIT_API_KEY", "audit-key")
|
||||
monkeypatch.setenv("APPROVAL_API_KEY", "approval-key")
|
||||
get_settings.cache_clear()
|
||||
|
||||
@@ -199,6 +221,21 @@ def test_approval_gate_for_high_risk_update() -> None:
|
||||
assert create_approval_response.json()["applicant"] == "api"
|
||||
create_ticket_id = create_approval_response.json()["ticket_id"]
|
||||
|
||||
approval_list_without_approval_key_response = client.get("/api/v1/approvals", headers=headers)
|
||||
assert approval_list_without_approval_key_response.status_code == 401
|
||||
|
||||
approval_detail_without_approval_key_response = client.get(
|
||||
f"/api/v1/approvals/{create_ticket_id}",
|
||||
headers=headers,
|
||||
)
|
||||
assert approval_detail_without_approval_key_response.status_code == 401
|
||||
|
||||
approval_detail_response = client.get(
|
||||
f"/api/v1/approvals/{create_ticket_id}",
|
||||
headers=approval_headers,
|
||||
)
|
||||
assert approval_detail_response.status_code == 200
|
||||
|
||||
approve_create_response = client.post(
|
||||
f"/api/v1/approvals/{create_ticket_id}/approve",
|
||||
headers=approval_headers,
|
||||
|
||||
Reference in New Issue
Block a user