fix: auto-fix code issues (cron)
- 修复重复导入/字段 - 修复异常处理 - 修复PEP8格式问题 - 添加类型注解 - 修复重复函数定义 (health_check, create_webhook_endpoint, etc) - 修复未定义名称 (SearchOperator, TenantTier, Query, Body, logger) - 修复 workflow_manager.py 的类定义重复问题 - 添加缺失的导入
This commit is contained in:
@@ -3,7 +3,6 @@ InsightFlow Phase 7 Task 3: 数据安全与合规模块
|
||||
Security Manager - 端到端加密、数据脱敏、审计日志
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import hashlib
|
||||
import secrets
|
||||
@@ -83,7 +82,7 @@ class AuditLog:
|
||||
success: bool = True
|
||||
error_message: Optional[str] = None
|
||||
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@@ -100,7 +99,7 @@ class EncryptionConfig:
|
||||
salt: Optional[str] = None
|
||||
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@@ -119,7 +118,7 @@ class MaskingRule:
|
||||
description: Optional[str] = None
|
||||
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@@ -140,7 +139,7 @@ class DataAccessPolicy:
|
||||
is_active: bool = True
|
||||
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
updated_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@@ -157,14 +156,14 @@ class AccessRequest:
|
||||
approved_at: Optional[str] = None
|
||||
expires_at: Optional[str] = None
|
||||
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
|
||||
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
class SecurityManager:
|
||||
"""安全管理器"""
|
||||
|
||||
|
||||
# 预定义脱敏规则
|
||||
DEFAULT_MASKING_RULES = {
|
||||
MaskingRuleType.PHONE: {
|
||||
@@ -192,17 +191,20 @@ class SecurityManager:
|
||||
"replacement": r"\1\2***"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def __init__(self, db_path: str = "insightflow.db"):
|
||||
self.db_path = db_path
|
||||
self.db_path = db_path
|
||||
# 预编译正则缓存
|
||||
self._compiled_patterns: Dict[str, re.Pattern] = {}
|
||||
self._local = {}
|
||||
self._init_db()
|
||||
|
||||
|
||||
def _init_db(self):
|
||||
"""初始化数据库表"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
# 审计日志表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
@@ -221,7 +223,7 @@ class SecurityManager:
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# 加密配置表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS encryption_configs (
|
||||
@@ -237,7 +239,7 @@ class SecurityManager:
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id)
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# 脱敏规则表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS masking_rules (
|
||||
@@ -255,7 +257,7 @@ class SecurityManager:
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id)
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# 数据访问策略表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS data_access_policies (
|
||||
@@ -275,7 +277,7 @@ class SecurityManager:
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id)
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# 访问请求表
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS access_requests (
|
||||
@@ -291,7 +293,7 @@ class SecurityManager:
|
||||
FOREIGN KEY (policy_id) REFERENCES data_access_policies(id)
|
||||
)
|
||||
""")
|
||||
|
||||
|
||||
# 创建索引
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit_logs(user_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit_logs(resource_type, resource_id)")
|
||||
@@ -300,18 +302,18 @@ class SecurityManager:
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_encryption_project ON encryption_configs(project_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_masking_project ON masking_rules(project_id)")
|
||||
cursor.execute("CREATE INDEX IF NOT EXISTS idx_access_policy_project ON data_access_policies(project_id)")
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def _generate_id(self) -> str:
|
||||
"""生成唯一ID"""
|
||||
return hashlib.sha256(
|
||||
f"{datetime.now().isoformat()}{secrets.token_hex(16)}".encode()
|
||||
).hexdigest()[:32]
|
||||
|
||||
|
||||
# ==================== 审计日志 ====================
|
||||
|
||||
|
||||
def log_audit(
|
||||
self,
|
||||
action_type: AuditActionType,
|
||||
@@ -341,11 +343,11 @@ class SecurityManager:
|
||||
success=success,
|
||||
error_message=error_message
|
||||
)
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
INSERT INTO audit_logs
|
||||
INSERT INTO audit_logs
|
||||
(id, action_type, user_id, user_ip, user_agent, resource_type, resource_id,
|
||||
action_details, before_value, after_value, success, error_message, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -357,9 +359,9 @@ class SecurityManager:
|
||||
))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return log
|
||||
|
||||
|
||||
def get_audit_logs(
|
||||
self,
|
||||
user_id: Optional[str] = None,
|
||||
@@ -375,10 +377,10 @@ class SecurityManager:
|
||||
"""查询审计日志"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
query = "SELECT * FROM audit_logs WHERE 1=1"
|
||||
params = []
|
||||
|
||||
|
||||
if user_id:
|
||||
query += " AND user_id = ?"
|
||||
params.append(user_id)
|
||||
@@ -400,26 +402,19 @@ class SecurityManager:
|
||||
if success is not None:
|
||||
query += " AND success = ?"
|
||||
params.append(int(success))
|
||||
|
||||
|
||||
query += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
|
||||
params.extend([limit, offset])
|
||||
|
||||
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
|
||||
logs = []
|
||||
for row in cursor.description:
|
||||
col_names = [desc[0] for desc in cursor.description]
|
||||
break
|
||||
else:
|
||||
col_names = [desc[0] for desc in cursor.description] if cursor.description else []
|
||||
if not col_names:
|
||||
return logs
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
|
||||
|
||||
for row in rows:
|
||||
log = AuditLog(
|
||||
id=row[0],
|
||||
@@ -437,10 +432,10 @@ class SecurityManager:
|
||||
created_at=row[12]
|
||||
)
|
||||
logs.append(log)
|
||||
|
||||
|
||||
conn.close()
|
||||
return logs
|
||||
|
||||
|
||||
def get_audit_stats(
|
||||
self,
|
||||
start_time: Optional[str] = None,
|
||||
@@ -449,54 +444,54 @@ class SecurityManager:
|
||||
"""获取审计统计"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
query = "SELECT action_type, success, COUNT(*) FROM audit_logs WHERE 1=1"
|
||||
params = []
|
||||
|
||||
|
||||
if start_time:
|
||||
query += " AND created_at >= ?"
|
||||
params.append(start_time)
|
||||
if end_time:
|
||||
query += " AND created_at <= ?"
|
||||
params.append(end_time)
|
||||
|
||||
|
||||
query += " GROUP BY action_type, success"
|
||||
|
||||
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
|
||||
|
||||
stats = {
|
||||
"total_actions": 0,
|
||||
"success_count": 0,
|
||||
"failure_count": 0,
|
||||
"action_breakdown": {}
|
||||
}
|
||||
|
||||
|
||||
for action_type, success, count in rows:
|
||||
stats["total_actions"] += count
|
||||
if success:
|
||||
stats["success_count"] += count
|
||||
else:
|
||||
stats["failure_count"] += count
|
||||
|
||||
|
||||
if action_type not in stats["action_breakdown"]:
|
||||
stats["action_breakdown"][action_type] = {"success": 0, "failure": 0}
|
||||
|
||||
|
||||
if success:
|
||||
stats["action_breakdown"][action_type]["success"] += count
|
||||
else:
|
||||
stats["action_breakdown"][action_type]["failure"] += count
|
||||
|
||||
|
||||
conn.close()
|
||||
return stats
|
||||
|
||||
|
||||
# ==================== 端到端加密 ====================
|
||||
|
||||
|
||||
def _derive_key(self, password: str, salt: bytes) -> bytes:
|
||||
"""从密码派生密钥"""
|
||||
if not CRYPTO_AVAILABLE:
|
||||
raise RuntimeError("cryptography library not available")
|
||||
|
||||
|
||||
kdf = PBKDF2HMAC(
|
||||
algorithm=hashes.SHA256(),
|
||||
length=32,
|
||||
@@ -504,7 +499,7 @@ class SecurityManager:
|
||||
iterations=100000,
|
||||
)
|
||||
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
|
||||
|
||||
|
||||
def enable_encryption(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -513,14 +508,14 @@ class SecurityManager:
|
||||
"""启用项目加密"""
|
||||
if not CRYPTO_AVAILABLE:
|
||||
raise RuntimeError("cryptography library not available")
|
||||
|
||||
|
||||
# 生成盐值
|
||||
salt = secrets.token_hex(16)
|
||||
|
||||
|
||||
# 派生密钥并哈希(用于验证)
|
||||
key = self._derive_key(master_password, salt.encode())
|
||||
key_hash = hashlib.sha256(key).hexdigest()
|
||||
|
||||
|
||||
config = EncryptionConfig(
|
||||
id=self._generate_id(),
|
||||
project_id=project_id,
|
||||
@@ -530,20 +525,20 @@ class SecurityManager:
|
||||
master_key_hash=key_hash,
|
||||
salt=salt
|
||||
)
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
# 检查是否已存在配置
|
||||
cursor.execute(
|
||||
"SELECT id FROM encryption_configs WHERE project_id = ?",
|
||||
(project_id,)
|
||||
)
|
||||
existing = cursor.fetchone()
|
||||
|
||||
|
||||
if existing:
|
||||
cursor.execute("""
|
||||
UPDATE encryption_configs
|
||||
UPDATE encryption_configs
|
||||
SET is_enabled = 1, encryption_type = ?, key_derivation = ?,
|
||||
master_key_hash = ?, salt = ?, updated_at = ?
|
||||
WHERE project_id = ?
|
||||
@@ -555,7 +550,7 @@ class SecurityManager:
|
||||
config.id = existing[0]
|
||||
else:
|
||||
cursor.execute("""
|
||||
INSERT INTO encryption_configs
|
||||
INSERT INTO encryption_configs
|
||||
(id, project_id, is_enabled, encryption_type, key_derivation,
|
||||
master_key_hash, salt, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -565,10 +560,10 @@ class SecurityManager:
|
||||
config.master_key_hash, config.salt,
|
||||
config.created_at, config.updated_at
|
||||
))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
# 记录审计日志
|
||||
self.log_audit(
|
||||
action_type=AuditActionType.ENCRYPTION_ENABLE,
|
||||
@@ -576,9 +571,9 @@ class SecurityManager:
|
||||
resource_id=project_id,
|
||||
action_details={"encryption_type": config.encryption_type}
|
||||
)
|
||||
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def disable_encryption(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -588,28 +583,28 @@ class SecurityManager:
|
||||
# 验证密码
|
||||
if not self.verify_encryption_password(project_id, master_password):
|
||||
return False
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE encryption_configs
|
||||
UPDATE encryption_configs
|
||||
SET is_enabled = 0, updated_at = ?
|
||||
WHERE project_id = ?
|
||||
""", (datetime.now().isoformat(), project_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
# 记录审计日志
|
||||
self.log_audit(
|
||||
action_type=AuditActionType.ENCRYPTION_DISABLE,
|
||||
resource_type="project",
|
||||
resource_id=project_id
|
||||
)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def verify_encryption_password(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -618,41 +613,41 @@ class SecurityManager:
|
||||
"""验证加密密码"""
|
||||
if not CRYPTO_AVAILABLE:
|
||||
return False
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute(
|
||||
"SELECT master_key_hash, salt FROM encryption_configs WHERE project_id = ?",
|
||||
(project_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return False
|
||||
|
||||
|
||||
stored_hash, salt = row
|
||||
key = self._derive_key(password, salt.encode())
|
||||
key_hash = hashlib.sha256(key).hexdigest()
|
||||
|
||||
|
||||
return key_hash == stored_hash
|
||||
|
||||
|
||||
def get_encryption_config(self, project_id: str) -> Optional[EncryptionConfig]:
|
||||
"""获取加密配置"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute(
|
||||
"SELECT * FROM encryption_configs WHERE project_id = ?",
|
||||
(project_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
|
||||
return EncryptionConfig(
|
||||
id=row[0],
|
||||
project_id=row[1],
|
||||
@@ -664,7 +659,7 @@ class SecurityManager:
|
||||
created_at=row[7],
|
||||
updated_at=row[8]
|
||||
)
|
||||
|
||||
|
||||
def encrypt_data(
|
||||
self,
|
||||
data: str,
|
||||
@@ -674,16 +669,16 @@ class SecurityManager:
|
||||
"""加密数据"""
|
||||
if not CRYPTO_AVAILABLE:
|
||||
raise RuntimeError("cryptography library not available")
|
||||
|
||||
|
||||
if salt is None:
|
||||
salt = secrets.token_hex(16)
|
||||
|
||||
|
||||
key = self._derive_key(password, salt.encode())
|
||||
f = Fernet(key)
|
||||
encrypted = f.encrypt(data.encode())
|
||||
|
||||
|
||||
return base64.b64encode(encrypted).decode(), salt
|
||||
|
||||
|
||||
def decrypt_data(
|
||||
self,
|
||||
encrypted_data: str,
|
||||
@@ -693,15 +688,15 @@ class SecurityManager:
|
||||
"""解密数据"""
|
||||
if not CRYPTO_AVAILABLE:
|
||||
raise RuntimeError("cryptography library not available")
|
||||
|
||||
|
||||
key = self._derive_key(password, salt.encode())
|
||||
f = Fernet(key)
|
||||
decrypted = f.decrypt(base64.b64decode(encrypted_data))
|
||||
|
||||
|
||||
return decrypted.decode()
|
||||
|
||||
|
||||
# ==================== 数据脱敏 ====================
|
||||
|
||||
|
||||
def create_masking_rule(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -718,7 +713,7 @@ class SecurityManager:
|
||||
default = self.DEFAULT_MASKING_RULES[rule_type]
|
||||
pattern = default["pattern"]
|
||||
replacement = replacement or default["replacement"]
|
||||
|
||||
|
||||
rule = MaskingRule(
|
||||
id=self._generate_id(),
|
||||
project_id=project_id,
|
||||
@@ -729,12 +724,12 @@ class SecurityManager:
|
||||
description=description,
|
||||
priority=priority
|
||||
)
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO masking_rules
|
||||
INSERT INTO masking_rules
|
||||
(id, project_id, name, rule_type, pattern, replacement,
|
||||
is_active, priority, description, created_at, updated_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
@@ -743,10 +738,10 @@ class SecurityManager:
|
||||
rule.pattern, rule.replacement, int(rule.is_active),
|
||||
rule.priority, rule.description, rule.created_at, rule.updated_at
|
||||
))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
# 记录审计日志
|
||||
self.log_audit(
|
||||
action_type=AuditActionType.DATA_MASKING,
|
||||
@@ -754,9 +749,9 @@ class SecurityManager:
|
||||
resource_id=project_id,
|
||||
action_details={"action": "create_rule", "rule_name": name}
|
||||
)
|
||||
|
||||
|
||||
return rule
|
||||
|
||||
|
||||
def get_masking_rules(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -765,19 +760,19 @@ class SecurityManager:
|
||||
"""获取脱敏规则"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
query = "SELECT * FROM masking_rules WHERE project_id = ?"
|
||||
params = [project_id]
|
||||
|
||||
|
||||
if active_only:
|
||||
query += " AND is_active = 1"
|
||||
|
||||
|
||||
query += " ORDER BY priority DESC"
|
||||
|
||||
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
|
||||
rules = []
|
||||
for row in rows:
|
||||
rules.append(MaskingRule(
|
||||
@@ -793,9 +788,9 @@ class SecurityManager:
|
||||
created_at=row[9],
|
||||
updated_at=row[10]
|
||||
))
|
||||
|
||||
|
||||
return rules
|
||||
|
||||
|
||||
def update_masking_rule(
|
||||
self,
|
||||
rule_id: str,
|
||||
@@ -803,45 +798,45 @@ class SecurityManager:
|
||||
) -> Optional[MaskingRule]:
|
||||
"""更新脱敏规则"""
|
||||
allowed_fields = ["name", "pattern", "replacement", "is_active", "priority", "description"]
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
set_clauses = []
|
||||
params = []
|
||||
|
||||
|
||||
for key, value in kwargs.items():
|
||||
if key in allowed_fields:
|
||||
set_clauses.append(f"{key} = ?")
|
||||
params.append(int(value) if key == "is_active" else value)
|
||||
|
||||
|
||||
if not set_clauses:
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
|
||||
set_clauses.append("updated_at = ?")
|
||||
params.append(datetime.now().isoformat())
|
||||
params.append(rule_id)
|
||||
|
||||
|
||||
cursor.execute(f"""
|
||||
UPDATE masking_rules
|
||||
UPDATE masking_rules
|
||||
SET {', '.join(set_clauses)}
|
||||
WHERE id = ?
|
||||
""", params)
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
# 获取更新后的规则
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM masking_rules WHERE id = ?", (rule_id,))
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
|
||||
return MaskingRule(
|
||||
id=row[0],
|
||||
project_id=row[1],
|
||||
@@ -855,20 +850,20 @@ class SecurityManager:
|
||||
created_at=row[9],
|
||||
updated_at=row[10]
|
||||
)
|
||||
|
||||
|
||||
def delete_masking_rule(self, rule_id: str) -> bool:
|
||||
"""删除脱敏规则"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("DELETE FROM masking_rules WHERE id = ?", (rule_id,))
|
||||
|
||||
|
||||
success = cursor.rowcount > 0
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return success
|
||||
|
||||
|
||||
def apply_masking(
|
||||
self,
|
||||
text: str,
|
||||
@@ -877,17 +872,17 @@ class SecurityManager:
|
||||
) -> str:
|
||||
"""应用脱敏规则到文本"""
|
||||
rules = self.get_masking_rules(project_id)
|
||||
|
||||
|
||||
if not rules:
|
||||
return text
|
||||
|
||||
|
||||
masked_text = text
|
||||
|
||||
|
||||
for rule in rules:
|
||||
# 如果指定了规则类型,只应用指定类型的规则
|
||||
if rule_types and MaskingRuleType(rule.rule_type) not in rule_types:
|
||||
continue
|
||||
|
||||
|
||||
try:
|
||||
masked_text = re.sub(
|
||||
rule.pattern,
|
||||
@@ -897,9 +892,9 @@ class SecurityManager:
|
||||
except re.error:
|
||||
# 忽略无效的正则表达式
|
||||
continue
|
||||
|
||||
|
||||
return masked_text
|
||||
|
||||
|
||||
def apply_masking_to_entity(
|
||||
self,
|
||||
entity_data: Dict[str, Any],
|
||||
@@ -907,18 +902,18 @@ class SecurityManager:
|
||||
) -> Dict[str, Any]:
|
||||
"""对实体数据应用脱敏"""
|
||||
masked_data = entity_data.copy()
|
||||
|
||||
|
||||
# 对可能包含敏感信息的字段进行脱敏
|
||||
sensitive_fields = ["name", "definition", "description", "value"]
|
||||
|
||||
|
||||
for field in sensitive_fields:
|
||||
if field in masked_data and isinstance(masked_data[field], str):
|
||||
masked_data[field] = self.apply_masking(masked_data[field], project_id)
|
||||
|
||||
|
||||
return masked_data
|
||||
|
||||
|
||||
# ==================== 数据访问策略 ====================
|
||||
|
||||
|
||||
def create_access_policy(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -944,12 +939,12 @@ class SecurityManager:
|
||||
max_access_count=max_access_count,
|
||||
require_approval=require_approval
|
||||
)
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO data_access_policies
|
||||
INSERT INTO data_access_policies
|
||||
(id, project_id, name, description, allowed_users, allowed_roles,
|
||||
allowed_ips, time_restrictions, max_access_count, require_approval,
|
||||
is_active, created_at, updated_at)
|
||||
@@ -961,12 +956,12 @@ class SecurityManager:
|
||||
int(policy.require_approval), int(policy.is_active),
|
||||
policy.created_at, policy.updated_at
|
||||
))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return policy
|
||||
|
||||
|
||||
def get_access_policies(
|
||||
self,
|
||||
project_id: str,
|
||||
@@ -975,17 +970,17 @@ class SecurityManager:
|
||||
"""获取数据访问策略"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
query = "SELECT * FROM data_access_policies WHERE project_id = ?"
|
||||
params = [project_id]
|
||||
|
||||
|
||||
if active_only:
|
||||
query += " AND is_active = 1"
|
||||
|
||||
|
||||
cursor.execute(query, params)
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
|
||||
policies = []
|
||||
for row in rows:
|
||||
policies.append(DataAccessPolicy(
|
||||
@@ -1003,9 +998,9 @@ class SecurityManager:
|
||||
created_at=row[11],
|
||||
updated_at=row[12]
|
||||
))
|
||||
|
||||
|
||||
return policies
|
||||
|
||||
|
||||
def check_access_permission(
|
||||
self,
|
||||
policy_id: str,
|
||||
@@ -1015,17 +1010,17 @@ class SecurityManager:
|
||||
"""检查访问权限"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute(
|
||||
"SELECT * FROM data_access_policies WHERE id = ? AND is_active = 1",
|
||||
(policy_id,)
|
||||
)
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return False, "Policy not found or inactive"
|
||||
|
||||
|
||||
policy = DataAccessPolicy(
|
||||
id=row[0],
|
||||
project_id=row[1],
|
||||
@@ -1041,13 +1036,13 @@ class SecurityManager:
|
||||
created_at=row[11],
|
||||
updated_at=row[12]
|
||||
)
|
||||
|
||||
|
||||
# 检查用户白名单
|
||||
if policy.allowed_users:
|
||||
allowed = json.loads(policy.allowed_users)
|
||||
if user_id not in allowed:
|
||||
return False, "User not in allowed list"
|
||||
|
||||
|
||||
# 检查IP白名单
|
||||
if policy.allowed_ips and user_ip:
|
||||
allowed_ips = json.loads(policy.allowed_ips)
|
||||
@@ -1058,45 +1053,45 @@ class SecurityManager:
|
||||
break
|
||||
if not ip_allowed:
|
||||
return False, "IP not in allowed list"
|
||||
|
||||
|
||||
# 检查时间限制
|
||||
if policy.time_restrictions:
|
||||
restrictions = json.loads(policy.time_restrictions)
|
||||
now = datetime.now()
|
||||
|
||||
|
||||
if "start_time" in restrictions and "end_time" in restrictions:
|
||||
current_time = now.strftime("%H:%M")
|
||||
if not (restrictions["start_time"] <= current_time <= restrictions["end_time"]):
|
||||
return False, "Access not allowed at this time"
|
||||
|
||||
|
||||
if "days_of_week" in restrictions:
|
||||
if now.weekday() not in restrictions["days_of_week"]:
|
||||
return False, "Access not allowed on this day"
|
||||
|
||||
|
||||
# 检查是否需要审批
|
||||
if policy.require_approval:
|
||||
# 检查是否有有效的访问请求
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
SELECT * FROM access_requests
|
||||
SELECT * FROM access_requests
|
||||
WHERE policy_id = ? AND user_id = ? AND status = 'approved'
|
||||
AND (expires_at IS NULL OR expires_at > ?)
|
||||
""", (policy_id, user_id, datetime.now().isoformat()))
|
||||
|
||||
|
||||
request = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not request:
|
||||
return False, "Access requires approval"
|
||||
|
||||
|
||||
return True, None
|
||||
|
||||
|
||||
def _match_ip_pattern(self, ip: str, pattern: str) -> bool:
|
||||
"""匹配IP模式(支持CIDR)"""
|
||||
import ipaddress
|
||||
|
||||
|
||||
try:
|
||||
if "/" in pattern:
|
||||
# CIDR 表示法
|
||||
@@ -1107,7 +1102,7 @@ class SecurityManager:
|
||||
return ip == pattern
|
||||
except ValueError:
|
||||
return ip == pattern
|
||||
|
||||
|
||||
def create_access_request(
|
||||
self,
|
||||
policy_id: str,
|
||||
@@ -1123,12 +1118,12 @@ class SecurityManager:
|
||||
request_reason=request_reason,
|
||||
expires_at=(datetime.now() + timedelta(hours=expires_hours)).isoformat()
|
||||
)
|
||||
|
||||
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
INSERT INTO access_requests
|
||||
INSERT INTO access_requests
|
||||
(id, policy_id, user_id, request_reason, status, expires_at, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
@@ -1136,12 +1131,12 @@ class SecurityManager:
|
||||
request.request_reason, request.status, request.expires_at,
|
||||
request.created_at
|
||||
))
|
||||
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
return request
|
||||
|
||||
|
||||
def approve_access_request(
|
||||
self,
|
||||
request_id: str,
|
||||
@@ -1151,26 +1146,26 @@ class SecurityManager:
|
||||
"""批准访问请求"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
expires_at = (datetime.now() + timedelta(hours=expires_hours)).isoformat()
|
||||
approved_at = datetime.now().isoformat()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE access_requests
|
||||
UPDATE access_requests
|
||||
SET status = 'approved', approved_by = ?, approved_at = ?, expires_at = ?
|
||||
WHERE id = ?
|
||||
""", (approved_by, approved_at, expires_at, request_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
|
||||
|
||||
# 获取更新后的请求
|
||||
cursor.execute("SELECT * FROM access_requests WHERE id = ?", (request_id,))
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
|
||||
return AccessRequest(
|
||||
id=row[0],
|
||||
policy_id=row[1],
|
||||
@@ -1182,7 +1177,7 @@ class SecurityManager:
|
||||
expires_at=row[7],
|
||||
created_at=row[8]
|
||||
)
|
||||
|
||||
|
||||
def reject_access_request(
|
||||
self,
|
||||
request_id: str,
|
||||
@@ -1191,22 +1186,22 @@ class SecurityManager:
|
||||
"""拒绝访问请求"""
|
||||
conn = sqlite3.connect(self.db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
cursor.execute("""
|
||||
UPDATE access_requests
|
||||
UPDATE access_requests
|
||||
SET status = 'rejected', approved_by = ?
|
||||
WHERE id = ?
|
||||
""", (rejected_by, request_id))
|
||||
|
||||
|
||||
conn.commit()
|
||||
|
||||
|
||||
cursor.execute("SELECT * FROM access_requests WHERE id = ?", (request_id,))
|
||||
row = cursor.fetchone()
|
||||
conn.close()
|
||||
|
||||
|
||||
if not row:
|
||||
return None
|
||||
|
||||
|
||||
return AccessRequest(
|
||||
id=row[0],
|
||||
policy_id=row[1],
|
||||
|
||||
Reference in New Issue
Block a user