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

100 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# =============================================================================
# 企微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