chore: initial baseline with P0-safety .gitignore

This commit is contained in:
Simon
2026-06-14 16:49:18 +08:00
commit 63262292d7
510 changed files with 146008 additions and 0 deletions
+312
View File
@@ -0,0 +1,312 @@
# =============================================================================
# 企微IT智能服务台 — 外部系统适配器抽象基类 + 统一数据模型
# =============================================================================
# 说明:
# 1. 定义所有外部系统共用的抽象接口(ABC)
# 2. 定义统一的DTO模型(TerminalInfo/SecurityStatus/VpnSession
# 3. 每个外部系统实现此接口,上层业务只依赖抽象接口
#
# 设计原则:
# - 默认返回None/空 — 子类按需覆写自己支持的方法
# - 不支持的能力不报错,返回None让调用方走降级逻辑
# - raw_data字段保留原始响应,调试用,生产环境可关闭
# =============================================================================
import logging
from abc import ABC, abstractmethod
from datetime import datetime
from typing import Dict, List, Optional
from pydantic import BaseModel, Field
logger = logging.getLogger(__name__)
# =============================================================================
# 统一数据模型(DTO
# =============================================================================
class TerminalInfo(BaseModel):
"""统一终端信息模型 — 所有Adapter返回同一结构
做什么:把联软/火绒/aTrust不同格式的终端数据映射到统一结构
为什么:上层业务代码不需要关心数据来自哪个系统
"""
# ── 来源标识 ──
source_system: str = Field(..., description="数据来源系统标识: lianruan/huorong/atrust/ehr")
# ── 基础标识 ──
terminal_id: Optional[str] = Field(None, description="终端在来源系统中的唯一ID")
computer_name: str = Field(..., description="计算机名")
# ── 网络信息 ──
ip_addresses: List[str] = Field(default_factory=list, description="IP地址列表(含VPN虚拟IP")
mac_addresses: List[str] = Field(default_factory=list, description="MAC地址列表")
# ── 系统信息 ──
os_version: Optional[str] = Field(None, description="操作系统版本")
is_online: bool = Field(False, description="是否在线")
# ── 用户映射(核心字段)──
logged_in_user: Optional[str] = Field(None, description="当前登录用户账号 — 映射核心字段")
logged_in_user_name: Optional[str] = Field(None, description="用户姓名")
department: Optional[str] = Field(None, description="所属部门")
# ── 硬件摘要 ──
hardware_summary: Optional[Dict] = Field(None, description="硬件摘要(CPU/内存/磁盘使用率等)")
# ── 时间信息 ──
last_seen: Optional[datetime] = Field(None, description="最后在线时间")
# ── 调试用 ──
raw_data: Optional[Dict] = Field(None, description="原始响应数据(调试用,生产可关闭)")
class VulnerabilityItem(BaseModel):
"""漏洞条目"""
name: str = Field(..., description="漏洞名称")
level: str = Field("info", description="严重程度: critical/high/medium/low/info")
description: Optional[str] = Field(None, description="漏洞描述")
publish_time: Optional[str] = Field(None, description="发布时间")
class SecurityStatus(BaseModel):
"""统一安全状态模型
做什么:聚合火绒的病毒/漏洞/隔离数据
为什么:坐席需要一目了然看到终端安全全貌
"""
source_system: str = Field(..., description="数据来源系统标识")
terminal_id: str = Field(..., description="终端ID")
computer_name: Optional[str] = Field(None, description="计算机名")
# ── 安全指标 ──
virus_total: int = Field(0, description="病毒事件总数")
virus_uncleaned: int = Field(0, description="未处理病毒数")
vulnerabilities: List[VulnerabilityItem] = Field(default_factory=list, description="高危漏洞列表")
high_vuln_count: int = Field(0, description="高危漏洞数量")
# ── 隔离状态 ──
is_isolated: bool = Field(False, description="是否被隔离")
isolation_source: Optional[str] = Field(None, description="隔离来源系统")
# ── 检查时间 ──
checked_at: datetime = Field(default_factory=datetime.now, description="检查时间")
class VpnSession(BaseModel):
"""VPN会话模型(仅aTrust
做什么:描述一个aTrust VPN在线会话
为什么:坐席需要知道远程员工是否通过VPN在线、VPN IP是什么
"""
source_system: str = "atrust"
session_id: Optional[str] = Field(None, description="会话ID(用于踢出操作)")
username: str = Field(..., description="用户名(登录名)")
display_name: Optional[str] = Field(None, description="显示名")
remote_ip: str = Field(..., description="接入IP(公网IP或'内网IP'")
vpn_ip: Optional[str] = Field(None, description="VPN虚拟内网IP — 火绒交叉匹配关键字段")
is_trusted: bool = Field(False, description="终端是否已授信")
os: Optional[str] = Field(None, description="接入终端操作系统")
last_login: Optional[datetime] = Field(None, description="最后登录时间")
domain: Optional[str] = Field(None, description="登录域")
# =============================================================================
# 适配器抽象基类
# =============================================================================
class ExternalSystemAdapter(ABC):
"""外部系统适配器抽象基类
做什么:定义所有外部系统共用的接口规范
为什么:让上层业务代码只依赖抽象接口,不感知底层系统差异
设计原则:
- 默认方法返回None/空列表/False,子类按需覆写自己支持的能力
- 不支持的能力不报错,让调用方走降级逻辑
- 每个Adapter只负责一个外部系统的对接
"""
@property
@abstractmethod
def system_name(self) -> str:
"""系统标识名称
返回值: 'lianruan' / 'huorong' / 'atrust' / 'ehr' / 'mock'
"""
...
@property
@abstractmethod
def is_available(self) -> bool:
"""当前系统是否可用(凭证已配置+网络可达)
做什么:检查配置是否完整,不实际发起网络请求
为什么:调用方可据此决定是否跳过本系统
"""
...
@abstractmethod
async def health_check(self) -> bool:
"""健康检查 — 验证凭证和网络连通性
做什么:实际发起一次轻量级API调用,确认系统可达
为什么:定期健康检查可提前发现连接问题
"""
...
# =========================================================================
# 终端查询能力
# =========================================================================
async def get_terminal_by_user(self, username: str) -> Optional[TerminalInfo]:
"""通过员工账号查询终端信息(映射核心方法)
做什么:输入员工账号,返回该员工使用的终端信息
为什么:这是员工→终端映射的核心入口
各系统实现方式:
- 联软:queryDevByParams(strusername=xxx) — 精确匹配
- 火绒:_list(ip=xxx) — 需配合联软IP交叉匹配
- aTrustqueryAll(bindUserList) — 终端绑定用户
- eHR:不提供终端数据,返回None
Args:
username: 员工账号(如 'songxian'
Returns:
TerminalInfo 或 None(系统不支持或未找到)
"""
return None
async def get_terminal_by_computer(self, computer_name: str) -> Optional[TerminalInfo]:
"""通过计算机名查询终端信息
Args:
computer_name: 计算机名(如 'IT-SONGXIAN'
"""
return None
async def get_terminal_detail(self, terminal_id: str) -> Optional[TerminalInfo]:
"""查询终端详细信息(硬件/软件/网络配置)
做什么:返回比 get_terminal_by_user 更详细的信息
为什么:排查时需要硬件配置、磁盘使用率、已安装软件等
各系统实现方式:
- 联软:getDevAllInfo — 极详细(主板/CPU/内存/硬盘/网卡/显示器)
- 火绒:_info2 — 中等详细(硬件/软件/网络配置)
- aTrust/eHR:不支持
Args:
terminal_id: 终端在来源系统中的唯一ID
"""
return None
# =========================================================================
# 安全能力
# =========================================================================
async def get_security_status(self, terminal_id: str) -> Optional[SecurityStatus]:
"""获取终端安全状态(病毒/漏洞/隔离状态)
做什么:聚合安全指标,坐席一目了然
为什么:安全问题通常需要紧急处理
仅火绒支持此接口。
Args:
terminal_id: 终端ID(火绒的client_id
"""
return None
async def isolate_terminal(self, terminal_id: str, reason: str) -> bool:
"""隔离终端(断网)
做什么:调用火绒 _create(type=netctrl) 隔离终端
为什么:安全事件紧急处理,阻断威胁扩散
仅火绒支持。调用前必须二次确认+审计日志记录。
Args:
terminal_id: 终端ID
reason: 隔离原因(记入审计日志)
Returns:
True=成功, False=失败
Raises:
NotImplementedError: 本系统不支持隔离操作
"""
raise NotImplementedError(f"{self.system_name} 不支持终端隔离")
async def unisolate_terminal(self, terminal_id: str) -> bool:
"""解除终端隔离(恢复网络)
仅火绒支持。
Args:
terminal_id: 终端ID
Returns:
True=成功, False=失败
"""
raise NotImplementedError(f"{self.system_name} 不支持解除隔离")
# =========================================================================
# VPN/在线状态能力
# =========================================================================
async def get_vpn_sessions(self, username: Optional[str] = None) -> List[VpnSession]:
"""查询VPN在线会话
做什么:获取当前通过aTrust在线的VPN会话
为什么:坐席需要知道远程员工VPN状态和IP
仅aTrust支持。
Args:
username: 可选,过滤指定用户
Returns:
VPN会话列表
"""
return []
async def get_online_status(self, username: str) -> bool:
"""查询用户是否在线
做什么:检查用户终端是否当前在线
为什么:坐席需要知道用户是否可达
各系统实现方式:
- 联软:existOnlineUser
- 火绒:_list(is_online=True) + IP交叉匹配
- aTrustgetUserStatus
Args:
username: 员工账号
Returns:
True=在线, False=离线或未知
"""
return False
# =========================================================================
# 辅助方法
# =========================================================================
def _log_not_implemented(self, method_name: str) -> None:
"""记录未实现方法的调试日志
做什么:当子类未覆写某个方法时记录DEBUG级日志
为什么:开发期帮助发现调用链路问题,生产环境可关闭DEBUG
"""
logger.debug(
f"[{self.system_name}] {method_name} 未实现,"
f"将走降级逻辑"
)