portal_config.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. """门户跳转集成配置:全部从环境变量(.env)读取。
  2. 对接对端门户系统时,优先只改 .env(provider=http 的声明式对接);
  3. 形态特殊时改 portal_provider.py 中的一个函数(见该文件内模板)。
  4. 本模块在每次请求时读取环境变量,改 .env 后重启进程生效。
  5. """
  6. from __future__ import annotations
  7. import os
  8. from dataclasses import dataclass
  9. ALLOWED_PROVIDERS = {"", "mock", "http", "custom"}
  10. ALLOWED_LOCAL_FIELDS = {"externalId", "username", "realName", "orgName"}
  11. class PortalConfigError(Exception):
  12. """门户配置缺失或非法(服务端 .env 问题,非用户输入问题)。"""
  13. @dataclass(frozen=True)
  14. class PortalConfig:
  15. provider: str # ""=关闭 / mock / http / custom
  16. token_param: str
  17. # ---- http provider:验签 ----
  18. verify_url: str
  19. verify_method: str # GET / POST
  20. verify_token_in: str # header / body / query
  21. verify_header: str
  22. verify_header_template: str # 内含 {token} 占位
  23. verify_success_field: str
  24. verify_success_value: str
  25. verify_user_field: str # 验签响应中用户信息对象所在字段路径(如 data)
  26. # ---- http provider:独立的用户信息接口(可空=验签已含用户信息)----
  27. userinfo_url: str
  28. userinfo_uid_param: str
  29. # ---- 用户信息字段映射:local -> remote ----
  30. field_map: dict[str, str]
  31. # ---- 新用户落库默认值 ----
  32. default_org_id: int | None
  33. # ---- 其他 ----
  34. http_timeout_seconds: int
  35. @property
  36. def enabled(self) -> bool:
  37. return bool(self.provider)
  38. def _env(name: str, default: str = "") -> str:
  39. return os.environ.get(name, default).strip()
  40. def _parse_field_map(raw: str) -> dict[str, str]:
  41. """解析 ``local:remote,local:remote`` 形式的字段映射。"""
  42. field_map: dict[str, str] = {}
  43. for chunk in raw.split(","):
  44. chunk = chunk.strip()
  45. if not chunk:
  46. continue
  47. local, sep, remote = chunk.partition(":")
  48. local = local.strip()
  49. remote = remote.strip()
  50. if not sep or local not in ALLOWED_LOCAL_FIELDS or not remote:
  51. raise PortalConfigError(
  52. f"DMS_PORTAL_FIELD_MAP格式非法({chunk}),"
  53. f"合法local字段:{sorted(ALLOWED_LOCAL_FIELDS)}"
  54. )
  55. field_map[local] = remote
  56. return field_map
  57. def load_portal_config() -> PortalConfig:
  58. provider = _env("DMS_PORTAL_PROVIDER").lower()
  59. if provider not in ALLOWED_PROVIDERS:
  60. raise PortalConfigError(
  61. "DMS_PORTAL_PROVIDER必须是 mock/http/custom 之一,留空表示关闭门户登录"
  62. )
  63. field_map = _parse_field_map(
  64. _env(
  65. "DMS_PORTAL_FIELD_MAP",
  66. "externalId:userId,username:loginName,realName:realName,orgName:orgName",
  67. )
  68. )
  69. if "externalId" not in field_map:
  70. raise PortalConfigError("DMS_PORTAL_FIELD_MAP必须包含externalId映射")
  71. raw_org_id = _env("DMS_PORTAL_DEFAULT_ORG_ID")
  72. try:
  73. default_org_id = int(raw_org_id) if raw_org_id else None
  74. except ValueError as exc:
  75. raise PortalConfigError("DMS_PORTAL_DEFAULT_ORG_ID必须是整数") from exc
  76. timeout_raw = _env("DMS_PORTAL_HTTP_TIMEOUT_SECONDS", "10")
  77. try:
  78. timeout_seconds = int(timeout_raw) if timeout_raw else 10
  79. except ValueError as exc:
  80. raise PortalConfigError("DMS_PORTAL_HTTP_TIMEOUT_SECONDS必须是整数") from exc
  81. token_in = _env("DMS_PORTAL_VERIFY_TOKEN_IN", "header").lower()
  82. if token_in not in {"header", "body", "query"}:
  83. raise PortalConfigError("DMS_PORTAL_VERIFY_TOKEN_IN必须是header/body/query")
  84. return PortalConfig(
  85. provider=provider,
  86. token_param=_env("DMS_PORTAL_TOKEN_PARAM", "token"),
  87. verify_url=_env("DMS_PORTAL_VERIFY_URL"),
  88. verify_method=_env("DMS_PORTAL_VERIFY_METHOD", "POST").upper(),
  89. verify_token_in=token_in,
  90. verify_header=_env("DMS_PORTAL_VERIFY_HEADER", "Authorization"),
  91. verify_header_template=_env("DMS_PORTAL_VERIFY_HEADER_TEMPLATE", "Bearer {token}"),
  92. verify_success_field=_env("DMS_PORTAL_VERIFY_SUCCESS_FIELD", "code"),
  93. verify_success_value=_env("DMS_PORTAL_VERIFY_SUCCESS_VALUE", "0"),
  94. verify_user_field=_env("DMS_PORTAL_VERIFY_USER_FIELD", "data"),
  95. userinfo_url=_env("DMS_PORTAL_USERINFO_URL"),
  96. userinfo_uid_param=_env("DMS_PORTAL_USERINFO_UID_PARAM", "userId"),
  97. field_map=field_map,
  98. default_org_id=default_org_id,
  99. http_timeout_seconds=timeout_seconds,
  100. )