50 lines
3.6 KiB
Python
50 lines
3.6 KiB
Python
"""操作日志 P2:完整性校验、高风险预警和保留策略。"""
|
||
import hashlib, json
|
||
from services import db as _db
|
||
from services.audit_schema import ensure_audit_schema
|
||
_READY=False
|
||
|
||
def _payload(row):
|
||
keys=('id','user_id','action','detail','ip_address','created_at','page','field','event_type','result_status','risk_level','object_type','object_id','request_id')
|
||
return json.dumps({k:str(row.get(k) or '') for k in keys},ensure_ascii=False,sort_keys=True,separators=(',',':'))
|
||
|
||
def calculate_hash(row): return hashlib.sha256(_payload(row).encode('utf-8')).hexdigest()
|
||
|
||
def ensure_governance_schema():
|
||
global _READY
|
||
if _READY:return
|
||
ensure_audit_schema()
|
||
cols={r['COLUMN_NAME'] for r in _db.query("SELECT COLUMN_NAME FROM information_schema.columns WHERE table_schema=DATABASE() AND table_name='dashboard_logs'")}
|
||
if 'integrity_hash' not in cols:_db.query("ALTER TABLE dashboard_logs ADD COLUMN integrity_hash CHAR(64) NULL")
|
||
if 'integrity_checked_at' not in cols:_db.query("ALTER TABLE dashboard_logs ADD COLUMN integrity_checked_at DATETIME NULL")
|
||
_db.query("""CREATE TABLE IF NOT EXISTS audit_alerts(id BIGINT AUTO_INCREMENT PRIMARY KEY,log_id BIGINT NOT NULL,user_id INT NULL,alert_type VARCHAR(50) NOT NULL,risk_level VARCHAR(20) NOT NULL,title VARCHAR(200) NOT NULL,detail TEXT,status VARCHAR(20) NOT NULL DEFAULT 'open',created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,resolved_at DATETIME NULL,resolved_by INT NULL,UNIQUE KEY uq_alert_log_type(log_id,alert_type),KEY idx_alert_status(status,created_at))""")
|
||
_db.query("""CREATE TABLE IF NOT EXISTS audit_retention_config(id INT PRIMARY KEY DEFAULT 1,online_days INT NOT NULL DEFAULT 180,archive_days INT NOT NULL DEFAULT 730,auto_archive TINYINT NOT NULL DEFAULT 0,updated_by INT NULL,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)""")
|
||
_db.query("INSERT IGNORE INTO audit_retention_config(id) VALUES(1)")
|
||
rows=_db.query("SELECT * FROM dashboard_logs WHERE integrity_hash IS NULL OR integrity_hash='' ORDER BY id")
|
||
_db.query("""INSERT IGNORE INTO audit_alerts(log_id,user_id,alert_type,risk_level,title,detail)
|
||
SELECT id,user_id,CASE WHEN risk_level='high' THEN 'high_risk' ELSE 'failed' END,
|
||
risk_level,CASE WHEN risk_level='high' THEN '高风险操作' ELSE '失败或拒绝操作' END,detail
|
||
FROM dashboard_logs WHERE risk_level='high' OR result_status IN ('failed','denied')""")
|
||
if rows:
|
||
conn=_db.get_db()
|
||
try:
|
||
with conn.cursor() as cur: cur.executemany("UPDATE dashboard_logs SET integrity_hash=%s WHERE id=%s",[(calculate_hash(r),r['id']) for r in rows])
|
||
conn.commit()
|
||
finally:conn.close()
|
||
_READY=True
|
||
|
||
def finalize_log(log_id):
|
||
if not log_id:return
|
||
ensure_governance_schema(); rows=_db.query("SELECT * FROM dashboard_logs WHERE id=%s",(log_id,))
|
||
if not rows:return
|
||
row=rows[0]; digest=calculate_hash(row); _db.query("UPDATE dashboard_logs SET integrity_hash=%s WHERE id=%s",(digest,log_id))
|
||
if row.get('risk_level')=='high' or row.get('result_status') in ('failed','denied'):
|
||
title='高风险操作' if row.get('risk_level')=='high' else '失败或拒绝操作'
|
||
_db.query("INSERT IGNORE INTO audit_alerts(log_id,user_id,alert_type,risk_level,title,detail) VALUES(%s,%s,%s,%s,%s,%s)",(log_id,row.get('user_id'),'high_risk' if row.get('risk_level')=='high' else 'failed',row.get('risk_level') or 'medium',title,row.get('detail') or ''))
|
||
|
||
def verify_logs(limit=10000):
|
||
ensure_governance_schema(); rows=_db.query("SELECT * FROM dashboard_logs ORDER BY id DESC LIMIT %s",(limit,)); bad=[]
|
||
for row in rows:
|
||
if row.get('integrity_hash')!=calculate_hash(row):bad.append(row['id'])
|
||
return {'checked':len(rows),'invalid':len(bad),'invalid_ids':bad[:100]}
|