simple_auth.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. from pydantic import BaseModel, Field, validator
  2. from typing import Optional, List
  3. class TicketExchangeRequest(BaseModel):
  4. app_id: str
  5. target_app_id: str
  6. user_mobile: str
  7. timestamp: int
  8. sign: str
  9. class TicketExchangeResponse(BaseModel):
  10. ticket: str
  11. redirect_url: str
  12. class TicketValidateRequest(BaseModel):
  13. ticket: str
  14. app_id: str # The app trying to validate
  15. sign: str
  16. timestamp: int
  17. class TicketValidateResponse(BaseModel):
  18. valid: bool
  19. user_id: Optional[int] = None
  20. mobile: Optional[str] = None
  21. mapped_key: Optional[str] = None
  22. mapped_email: Optional[str] = None
  23. class PasswordLoginRequest(BaseModel):
  24. app_id: Optional[str] = Field(
  25. None,
  26. description="应用ID。不提供时为平台登录(返回access_token),提供时为应用SSO登录(返回ticket)"
  27. )
  28. identifier: str = Field(
  29. ...,
  30. description="用户标识:手机号、映射key或映射email"
  31. )
  32. password: str = Field(
  33. ...,
  34. description="用户密码"
  35. )
  36. remember_me: bool = Field(
  37. False,
  38. description="是否记住登录(长期有效)"
  39. )
  40. sign: Optional[str] = Field(
  41. None,
  42. description="签名(可选)。如果提供,必须同时提供timestamp。用于服务端安全调用"
  43. )
  44. timestamp: Optional[int] = Field(
  45. None,
  46. description="时间戳(可选)。如果提供,必须同时提供sign"
  47. )
  48. @validator('timestamp')
  49. def validate_sign_timestamp(cls, v, values):
  50. """验证 sign 和 timestamp 必须同时提供或同时不提供"""
  51. sign = values.get('sign')
  52. if (sign and not v) or (not sign and v):
  53. raise ValueError('sign 和 timestamp 必须同时提供或同时不提供')
  54. return v
  55. class PasswordLoginResponse(BaseModel):
  56. ticket: Optional[str] = Field(
  57. None,
  58. description="票据。当提供app_id时返回,用于应用SSO登录"
  59. )
  60. access_token: Optional[str] = Field(
  61. None,
  62. description="访问令牌。当未提供app_id时返回,用于平台登录"
  63. )
  64. token_type: Optional[str] = Field(
  65. None,
  66. description="令牌类型,固定为 'bearer'"
  67. )
  68. role: Optional[str] = Field(
  69. None,
  70. description="用户角色:SUPER_ADMIN / DEVELOPER / ORDINARY_USER"
  71. )
  72. class SmsLoginRequest(BaseModel):
  73. mobile: str = Field(..., description="手机号")
  74. code: str = Field(..., description="验证码")
  75. remember_me: bool = Field(
  76. False,
  77. description="记住我:为 true 时 JWT 使用较长有效期(与密码登录一致)",
  78. )
  79. app_id: Optional[str] = Field(
  80. None,
  81. description="应用ID。不提供时为平台登录(返回access_token),提供时为应用SSO登录(返回ticket)"
  82. )
  83. sign: Optional[str] = Field(
  84. None,
  85. description="签名(可选)。如果提供,必须同时提供timestamp。用于服务端安全调用"
  86. )
  87. timestamp: Optional[int] = Field(
  88. None,
  89. description="时间戳(可选)。如果提供,必须同时提供sign"
  90. )
  91. @validator('timestamp')
  92. def validate_sign_timestamp(cls, v, values):
  93. """验证 sign 和 timestamp 必须同时提供或同时不提供"""
  94. sign = values.get('sign')
  95. if (sign and not v) or (not sign and v):
  96. raise ValueError('sign 和 timestamp 必须同时提供或同时不提供')
  97. return v
  98. class UserRegisterRequest(BaseModel):
  99. mobile: str
  100. password: str
  101. name: str # Added field
  102. app_id: Optional[str] = None # Optional, if registering via an app context
  103. class AdminPasswordResetRequest(BaseModel):
  104. user_id: int
  105. admin_password: str
  106. class AdminPasswordResetResponse(BaseModel):
  107. new_password: str
  108. class ChangePasswordRequest(BaseModel):
  109. old_password: str
  110. new_password: str
  111. class UserMappingResponse(BaseModel):
  112. app_name: str
  113. app_id: str
  114. protocol_type: str
  115. mapped_key: Optional[str] = None
  116. mapped_email: Optional[str] = None
  117. is_active: bool
  118. class MyMappingsResponse(BaseModel):
  119. total: int
  120. items: List[UserMappingResponse]
  121. class UserPromoteRequest(BaseModel):
  122. user_id: int
  123. new_role: str
  124. class SsoLoginRequest(BaseModel):
  125. app_id: str = Field(
  126. ...,
  127. description="应用ID(必填)。只支持 SIMPLE_API 类型的应用"
  128. )
  129. username: Optional[str] = Field(
  130. None,
  131. description="用户名(条件必填)。如果用户未登录UAP平台,则必须提供 username 和 password"
  132. )
  133. password: Optional[str] = Field(
  134. None,
  135. description="密码(条件必填)。如果用户未登录UAP平台,则必须提供 username 和 password"
  136. )
  137. class Config:
  138. json_schema_extra = {
  139. "examples": [
  140. {
  141. "description": "场景1:用户已登录UAP(使用会话)",
  142. "value": {
  143. "app_id": "my_app_123"
  144. }
  145. },
  146. {
  147. "description": "场景2:用户未登录(使用凭据)",
  148. "value": {
  149. "app_id": "my_app_123",
  150. "username": "13800138000",
  151. "password": "password123"
  152. }
  153. }
  154. ]
  155. }
  156. class SsoLoginResponse(BaseModel):
  157. redirect_url: str = Field(
  158. ...,
  159. description="带票据的重定向URL,格式:{应用回调URL}?ticket={票据}。前端应直接跳转到此URL"
  160. )
  161. class LaunchpadAppResponse(BaseModel):
  162. """快捷导航应用响应模型(包含分类和描述)"""
  163. app_name: str
  164. app_id: str
  165. protocol_type: str
  166. mapped_key: Optional[str] = None
  167. mapped_email: Optional[str] = None
  168. is_active: bool
  169. description: Optional[str] = None # 应用描述
  170. category_id: Optional[int] = None # 分类ID
  171. category_name: Optional[str] = None # 分类名称
  172. class LaunchpadAppsResponse(BaseModel):
  173. """快捷导航应用列表响应"""
  174. total: int # 总数量(方便前端使用)
  175. items: List[LaunchpadAppResponse]