portal_auth.py 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. """门户跳转登录接口:验签/按需落库 + 首次设密。"""
  2. from __future__ import annotations
  3. from flask import request
  4. from dms.api.v1.blueprint import api_v1
  5. from dms.common.errors import InvalidArgumentError, LoginIpForbiddenError
  6. from dms.common.response import success_response
  7. from dms.services.audit_service import record_login_failure
  8. from dms.services.portal_auth_service import setup_portal_password, verify_portal_launch
  9. def _verify_payload() -> str:
  10. if not request.is_json:
  11. raise InvalidArgumentError("请求体必须是JSON对象")
  12. body = request.get_json(silent=True)
  13. if not isinstance(body, dict) or set(body) != {"query"}:
  14. raise InvalidArgumentError("请求体必须且只能包含query")
  15. query = body["query"]
  16. if not isinstance(query, str) or not query.strip():
  17. raise InvalidArgumentError("query不能为空")
  18. return query.strip()
  19. @api_v1.post("/auth/portal/verify")
  20. def portal_verify():
  21. """门户跳转验签:透传浏览器地址栏query串,返回跳转阶段与用户名。"""
  22. return success_response(verify_portal_launch(_verify_payload()))
  23. def _setup_payload() -> tuple[str, str, bool]:
  24. if not request.is_json:
  25. raise InvalidArgumentError("请求体必须是JSON对象")
  26. body = request.get_json(silent=True)
  27. if not isinstance(body, dict):
  28. raise InvalidArgumentError("请求体必须是JSON对象")
  29. expected = {"portalTicket", "newPassword", "keepSignedIn"}
  30. if set(body) != expected:
  31. raise InvalidArgumentError(
  32. "请求体必须且只能包含portalTicket、newPassword和keepSignedIn"
  33. )
  34. ticket = body["portalTicket"]
  35. password = body["newPassword"]
  36. keep_signed_in = body["keepSignedIn"]
  37. if not isinstance(ticket, str) or not ticket.strip():
  38. raise InvalidArgumentError("portalTicket不能为空")
  39. if not isinstance(password, str) or not password:
  40. raise InvalidArgumentError("newPassword不能为空")
  41. if not isinstance(keep_signed_in, bool):
  42. raise InvalidArgumentError("keepSignedIn必须是布尔值")
  43. return ticket.strip(), password, keep_signed_in
  44. @api_v1.post("/auth/portal/setup-password")
  45. def portal_setup_password():
  46. """门户用户首次设置独立密码并登录(票据一次性,5分钟内有效)。"""
  47. ticket, password, keep_signed_in = _setup_payload()
  48. try:
  49. payload = setup_portal_password(ticket, password, keep_signed_in)
  50. except LoginIpForbiddenError:
  51. record_login_failure(
  52. user=None,
  53. attempted_username="(portal-setup)",
  54. failure_reason="IP_NOT_ALLOWED",
  55. )
  56. raise
  57. return success_response(payload)