fix(health): 审计问题修复 — 权限守卫 + OAuth中间件 + FHIR声明 + SSE聚合
- OAuthClientList/RealtimeMonitor/OfflineEventList/StatisticsDashboard 补权限守卫 - OAuth 中间件注入 TenantContext + FHIR scope→permission 映射 - FHIR CapabilityStatement 移除未实现的 $lastn 操作 - useVitalSSE 修复批量同步事件数据聚合逻辑
This commit is contained in:
@@ -31,7 +31,7 @@ where
|
||||
"mode": "server",
|
||||
"resource": [
|
||||
{ "type": "Patient", "interaction": [{"code": "read"}, {"code": "search-type"}], "operation": [{"name": "everything"}] },
|
||||
{ "type": "Observation", "interaction": [{"code": "read"}, {"code": "search-type"}], "operation": [{"name": "lastn"}] },
|
||||
{ "type": "Observation", "interaction": [{"code": "read"}, {"code": "search-type"}] },
|
||||
{ "type": "Device", "interaction": [{"code": "read"}, {"code": "search-type"}] },
|
||||
{ "type": "DiagnosticReport", "interaction": [{"code": "read"}, {"code": "search-type"}] },
|
||||
{ "type": "Encounter", "interaction": [{"code": "read"}, {"code": "search-type"}] },
|
||||
@@ -40,8 +40,7 @@ where
|
||||
{ "type": "Task", "interaction": [{"code": "read"}, {"code": "search-type"}] },
|
||||
],
|
||||
"operation": [
|
||||
{ "name": "everything", "definition": "/fhir/R4/Patient/{id}/$everything" },
|
||||
{ "name": "lastn", "definition": "/fhir/R4/Observation/$lastn" },
|
||||
{ "name": "everything", "definition": "/fhir/R4/Patient/{id}/$everything" }
|
||||
]
|
||||
}]
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ use axum::{
|
||||
Json,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// Client Credentials JWT Claims
|
||||
@@ -107,12 +108,48 @@ pub async fn oauth_auth_middleware(request: Request, next: Next) -> Response {
|
||||
let fhir_ctx = FhirAuthContext {
|
||||
client_id: claims.sub,
|
||||
tenant_id: claims.tid,
|
||||
scopes: claims.scopes,
|
||||
scopes: claims.scopes.clone(),
|
||||
allowed_patient_ids: claims.allowed_patient_ids,
|
||||
};
|
||||
|
||||
// 同时注入 TenantContext,兼容 require_permission() 和 ctx.tenant_id
|
||||
// 将 FHIR scope 映射为内部 permission code
|
||||
let permissions: Vec<String> = claims
|
||||
.scopes
|
||||
.iter()
|
||||
.flat_map(|s| scope_to_permissions(s))
|
||||
.collect();
|
||||
|
||||
let tenant_ctx = erp_core::types::TenantContext {
|
||||
tenant_id: claims.tid,
|
||||
user_id: claims.sub,
|
||||
roles: vec!["api_client".to_string()],
|
||||
permissions,
|
||||
department_ids: vec![],
|
||||
permission_data_scopes: HashMap::new(),
|
||||
};
|
||||
|
||||
let mut request = request;
|
||||
request.extensions_mut().insert(tenant_ctx);
|
||||
request.extensions_mut().insert(fhir_ctx);
|
||||
|
||||
next.run(request).await
|
||||
}
|
||||
|
||||
/// FHIR scope → 内部 permission code 映射
|
||||
fn scope_to_permissions(scope: &str) -> Vec<String> {
|
||||
match scope {
|
||||
"Patient.read" => vec!["health.patient.list".to_string()],
|
||||
"Observation.read" => vec![
|
||||
"health.device-readings.list".to_string(),
|
||||
"health.health-data.list".to_string(),
|
||||
],
|
||||
"Device.read" => vec!["health.devices.list".to_string()],
|
||||
"Practitioner.read" => vec!["health.doctor.list".to_string()],
|
||||
"Appointment.read" => vec!["health.appointment.list".to_string()],
|
||||
"DiagnosticReport.read" => vec!["health.health-data.list".to_string()],
|
||||
"Encounter.read" => vec!["health.consultation.list".to_string()],
|
||||
"Task.read" => vec!["health.follow-up.list".to_string()],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user