simple_auth.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  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. app_id: Optional[str] = Field(
  76. None,
  77. description="应用ID。不提供时为平台登录(返回access_token),提供时为应用SSO登录(返回ticket)"
  78. )
  79. sign: Optional[str] = Field(
  80. None,
  81. description="签名(可选)。如果提供,必须同时提供timestamp。用于服务端安全调用"
  82. )
  83. timestamp: Optional[int] = Field(
  84. None,
  85. description="时间戳(可选)。如果提供,必须同时提供sign"
  86. )
  87. @validator('timestamp')
  88. def validate_sign_timestamp(cls, v, values):
  89. """验证 sign 和 timestamp 必须同时提供或同时不提供"""
  90. sign = values.get('sign')
  91. if (sign and not v) or (not sign and v):
  92. raise ValueError('sign 和 timestamp 必须同时提供或同时不提供')
  93. return v
  94. class UserRegisterRequest(BaseModel):
  95. mobile: str
  96. password: str
  97. name: str # Added field
  98. app_id: Optional[str] = None # Optional, if registering via an app context
  99. class AdminPasswordResetRequest(BaseModel):
  100. user_id: int
  101. admin_password: str
  102. class AdminPasswordResetResponse(BaseModel):
  103. new_password: str
  104. class ChangePasswordRequest(BaseModel):
  105. old_password: str
  106. new_password: str
  107. class UserMappingResponse(BaseModel):
  108. app_name: str
  109. app_id: str
  110. protocol_type: str
  111. mapped_key: Optional[str] = None
  112. mapped_email: Optional[str] = None
  113. is_active: bool
  114. class MyMappingsResponse(BaseModel):
  115. total: int
  116. items: List[UserMappingResponse]
  117. class UserPromoteRequest(BaseModel):
  118. user_id: int
  119. new_role: str
  120. class SsoLoginRequest(BaseModel):
  121. app_id: str = Field(
  122. ...,
  123. description="应用ID(必填)。只支持 SIMPLE_API 类型的应用"
  124. )
  125. username: Optional[str] = Field(
  126. None,
  127. description="用户名(条件必填)。如果用户未登录UAP平台,则必须提供 username 和 password"
  128. )
  129. password: Optional[str] = Field(
  130. None,
  131. description="密码(条件必填)。如果用户未登录UAP平台,则必须提供 username 和 password"
  132. )
  133. class Config:
  134. json_schema_extra = {
  135. "examples": [
  136. {
  137. "description": "场景1:用户已登录UAP(使用会话)",
  138. "value": {
  139. "app_id": "my_app_123"
  140. }
  141. },
  142. {
  143. "description": "场景2:用户未登录(使用凭据)",
  144. "value": {
  145. "app_id": "my_app_123",
  146. "username": "13800138000",
  147. "password": "password123"
  148. }
  149. }
  150. ]
  151. }
  152. class SsoLoginResponse(BaseModel):
  153. redirect_url: str = Field(
  154. ...,
  155. description="带票据的重定向URL,格式:{应用回调URL}?ticket={票据}。前端应直接跳转到此URL"
  156. )
  157. class LaunchpadAppResponse(BaseModel):
  158. """快捷导航应用响应模型(包含分类和描述)"""
  159. app_name: str
  160. app_id: str
  161. protocol_type: str
  162. mapped_key: Optional[str] = None
  163. mapped_email: Optional[str] = None
  164. is_active: bool
  165. description: Optional[str] = None # 应用描述
  166. category_id: Optional[int] = None # 分类ID
  167. category_name: Optional[str] = None # 分类名称
  168. class LaunchpadAppsResponse(BaseModel):
  169. """快捷导航应用列表响应"""
  170. total: int # 总数量(方便前端使用)
  171. items: List[LaunchpadAppResponse]