Files
wecom_it_smart_desk/backend/app/utils/env_gating.py
T

100 lines
3.4 KiB
Python
Raw Normal View History

# =============================================================================
# 企微IT智能服务台 — 环境门禁(三端认证重构 AUTH-01)
# =============================================================================
# 说明:根据 app_env 判断当前是否生产环境,统一控制以下强校验的启用:
# 1. 企微 WebViewwxworkUA 校验
# 2. 管理后台 IP 白名单
# 3. 真实企微 OAuth2 静默授权
#
# 设计原则(决策来源:system_design.md 环境门禁矩阵):
# - 仅 production 环境启用上述强校验
# - dev / test / 未配置 一律视为非生产,跳过强校验,方便本地开发
#
# 使用方式:
# from app.utils.env_gating import is_production, ip_in_whitelist
# if is_production():
# ...
# =============================================================================
from ipaddress import ip_address, ip_network
from typing import List, Optional
from app.config import settings
def is_production() -> bool:
"""判断是否生产环境。
仅当 app_env == "production" 时返回 True。
其他值(dev / test / 空字符串)一律视为非生产环境,
从而跳过 UA 校验 / IP 白名单 / 真实 OAuth 等强校验。
Returns:
bool: True=生产环境,False=非生产环境
"""
return (settings.app_env or "dev").strip().lower() == "production"
def _parse_allowed_ips(raw: Optional[str]) -> List[str]:
"""把逗号分隔的 IP / 网段字符串解析为去空白后的列表。
Args:
raw: 形如 "117.147.35.138,218.75.34.87,10.240.0.0/16" 的字符串
Returns:
List[str]: 非空条目列表(已去除首尾空白)
"""
if not raw:
return []
return [item.strip() for item in raw.split(",") if item.strip()]
def ip_in_whitelist(client_ip: str, allowed: Optional[str] = None) -> bool:
"""判断客户端 IP 是否在白名单内(支持 CIDR 网段)。
匹配规则:
- 白名单为空 → 保守拒绝(不开放)
- client_ip 格式非法 → 拒绝
- 白名单条目含 "/" → 按 CIDR 网段匹配(ip_network, strict=False
- 白名单条目为单 IP → 精确相等匹配
Args:
client_ip: 客户端 IP(如 "10.240.1.5"
allowed: 白名单字符串(逗号分隔,支持 CIDR)。
缺省时读取 settings.admin_allowed_ips。
Returns:
bool: True=在白名单内,False=不在 / 格式非法 / 白名单为空
"""
if not client_ip:
return False
allowed_raw = allowed if allowed is not None else settings.admin_allowed_ips
entries = _parse_allowed_ips(allowed_raw)
if not entries:
# 白名单为空 → 保守策略:不开放任何 IP
return False
try:
client = ip_address(client_ip)
except ValueError:
# 客户端 IP 格式非法 → 拒绝
return False
for entry in entries:
try:
if "/" in entry:
# CIDR 网段匹配(如 10.240.0.0/16
network = ip_network(entry, strict=False)
if client in network:
return True
else:
# 单 IP 精确匹配
if client == ip_address(entry):
return True
except ValueError:
# 单个白名单条目格式非法 → 跳过,继续匹配其他条目
continue
return False