100 lines
3.4 KiB
Python
100 lines
3.4 KiB
Python
# =============================================================================
|
||
# 企微IT智能服务台 — 环境门禁(三端认证重构 AUTH-01)
|
||
# =============================================================================
|
||
# 说明:根据 app_env 判断当前是否生产环境,统一控制以下强校验的启用:
|
||
# 1. 企微 WebView(wxwork)UA 校验
|
||
# 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
|