修复(权限管理):统一 PM 系统导航与权限校验

This commit is contained in:
Cheng Zhou
2026-07-10 22:12:59 +08:00
parent f11a5c84d9
commit f2fa10c178
47 changed files with 4071 additions and 1555 deletions
@@ -69,7 +69,19 @@ async def test_system_permission_monitoring_definitions_are_admin_only():
assert all("PM" not in item["roles"] for item in monitoring_items)
async def _seed_permission_log(db_session, study_id: uuid.UUID, user_id: uuid.UUID, *, allowed: bool, elapsed_ms: float) -> None:
async def _seed_permission_log(
db_session,
study_id: uuid.UUID,
user_id: uuid.UUID,
*,
allowed: bool,
elapsed_ms: float,
endpoint_key: str = "admin.permissions.read",
role: str = "PM",
client_type: str | None = None,
client_version: str | None = None,
client_platform: str | None = None,
) -> None:
study_exists = (
await db_session.execute(text("SELECT id FROM studies WHERE id = :id"), {"id": str(study_id)})
).scalar_one_or_none()
@@ -119,21 +131,24 @@ async def _seed_permission_log(db_session, study_id: uuid.UUID, user_id: uuid.UU
"""
INSERT INTO permission_access_logs
(id, study_id, user_id, endpoint_key, role, allowed, elapsed_ms, ip_address,
request_snapshot, created_at)
client_type, client_version, client_platform, request_snapshot, created_at)
VALUES
(:id, :study_id, :user_id, :endpoint_key, :role, :allowed, :elapsed_ms, :ip_address,
:request_snapshot, CURRENT_TIMESTAMP)
:client_type, :client_version, :client_platform, :request_snapshot, CURRENT_TIMESTAMP)
"""
),
{
"id": str(uuid.uuid4()),
"study_id": str(study_id),
"user_id": str(user_id),
"endpoint_key": "admin.permissions.read",
"role": "PM",
"endpoint_key": endpoint_key,
"role": role,
"allowed": allowed,
"elapsed_ms": elapsed_ms,
"ip_address": "127.0.0.1",
"client_type": client_type,
"client_version": client_version,
"client_platform": client_platform,
"request_snapshot": None,
},
)
@@ -159,6 +174,53 @@ async def test_get_permission_metrics(db_session):
assert data["check_metrics"]["denied_checks"] == 1
@pytest.mark.asyncio
async def test_get_trends_returns_dense_summary_comparison_and_attribution(db_session):
baseline = await permission_monitoring.get_trends(db=db_session, _=AdminUserStub(), period="24h")
study_id = uuid.uuid4()
user_id = uuid.uuid4()
await _seed_permission_log(
db_session,
study_id,
user_id,
allowed=True,
elapsed_ms=5,
endpoint_key="subjects.read",
client_type="web",
client_version="0.1.0",
client_platform="macos",
)
await _seed_permission_log(
db_session,
study_id,
user_id,
allowed=False,
elapsed_ms=80,
endpoint_key="subjects.export",
client_type="web",
client_version="0.1.0",
client_platform="macos",
)
data = await permission_monitoring.get_trends(db=db_session, _=AdminUserStub(), period="24h")
assert data["period"] == "24h"
assert data["range"]["bucket_seconds"] == 3600
assert len(data["data_points"]) == 24
assert data["data_points"][-1]["sample_state"] == "partial"
assert data["summary"]["total_checks"] == baseline["summary"]["total_checks"] + 2
assert data["summary"]["denied_checks"] == baseline["summary"]["denied_checks"] + 1
assert data["summary"]["slow_check_count"] == baseline["summary"]["slow_check_count"] + 1
assert data["summary"]["active_study_count"] == baseline["summary"]["active_study_count"] + 1
assert data["summary"]["active_user_count"] == baseline["summary"]["active_user_count"] + 1
assert data["summary"]["active_endpoint_count"] >= 2
assert data["summary"]["max_elapsed_ms"] >= data["summary"]["p95_elapsed_ms"]
assert data["previous_summary"]["total_checks"] == 0
assert any(item["endpoint_key"] == "subjects.export" for item in data["attribution"]["top_denied"])
assert data["attribution"]["top_slow"][0]["endpoint_key"] == "subjects.export"
assert any(item["client_type"] == "web" for item in data["attribution"]["client_breakdown"])
@pytest.mark.asyncio
async def test_get_cache_statistics(db_session):
"""测试获取缓存统计"""
@@ -1206,6 +1268,26 @@ async def test_access_logs_include_security_events_for_admin(db_session, monkeyp
assert items_by_type["permission"]["request_snapshot"]["path"] == "/api/v1/projects/overview"
assert items_by_type["security"]["request_snapshot"]["headers"]["authorization"] == "[redacted]"
permission_only = await permission_monitoring.get_access_logs(
db=db_session,
_=AdminUserStub(),
study_id=None,
user_id=None,
endpoint_key=None,
role=None,
allowed=None,
start_time=None,
end_time=None,
client_ip=None,
client_type=None,
event_type="permission",
keyword=None,
page=1,
page_size=50,
)
assert permission_only["total"] == 1
assert permission_only["items"][0]["event_type"] == "permission"
@pytest.mark.asyncio
async def test_access_logs_ip_ranking_aggregates_by_ip_total(db_session):