identity.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. from datetime import datetime
  2. from typing import TYPE_CHECKING, Generic, TypeVar
  3. from core_shared import JSONValue
  4. from pydantic import BaseModel, Field
  5. if TYPE_CHECKING:
  6. from app.db.models import ApiKey, Role, RolePermissionBinding, User
  7. T = TypeVar("T")
  8. class ApiErrorResponse(BaseModel):
  9. errorType: str
  10. message: str
  11. details: dict[str, JSONValue] = Field(default_factory=dict)
  12. class ApiResponse(BaseModel, Generic[T]):
  13. success: bool = True
  14. data: T | None = None
  15. error: ApiErrorResponse | None = None
  16. requestId: str
  17. serverTime: datetime
  18. class PageRequest(BaseModel):
  19. page: int = Field(default=1, ge=1)
  20. pageSize: int = Field(default=20, ge=1, le=200)
  21. keyword: str | None = None
  22. sortBy: str = "createdTime"
  23. sortOrder: str = "desc"
  24. @property
  25. def offset(self) -> int:
  26. return (self.page - 1) * self.pageSize
  27. class PageResult(BaseModel, Generic[T]):
  28. items: list[T]
  29. total: int
  30. page: int
  31. pageSize: int
  32. hasMore: bool
  33. @classmethod
  34. def from_items(
  35. cls,
  36. *,
  37. items: list[T],
  38. total: int,
  39. page: int,
  40. page_size: int) -> "PageResult[T]":
  41. return cls(
  42. items=items,
  43. total=total,
  44. page=page,
  45. pageSize=page_size,
  46. hasMore=page * page_size < total)
  47. class UserDto(BaseModel):
  48. id: str
  49. username: str
  50. displayName: str | None = None
  51. email: str | None = None
  52. metadata: dict[str, JSONValue] = Field(default_factory=dict)
  53. lastLoginTime: datetime | None = None
  54. createdTime: datetime
  55. updatedTime: datetime
  56. @classmethod
  57. def from_entity(cls, entity: "User") -> "UserDto":
  58. return cls(
  59. id=entity.id,
  60. username=entity.username,
  61. displayName=entity.display_name,
  62. email=entity.email,
  63. metadata=entity.metadata_json or {},
  64. lastLoginTime=entity.last_login_time,
  65. createdTime=entity.created_time,
  66. updatedTime=entity.updated_time)
  67. class RoleDto(BaseModel):
  68. id: str
  69. name: str
  70. description: str | None = None
  71. permissionBindingCount: int
  72. createdTime: datetime
  73. updatedTime: datetime
  74. @classmethod
  75. def from_entity(cls, entity: "Role", *, permission_binding_count: int = 0) -> "RoleDto":
  76. return cls(
  77. id=entity.id,
  78. name=entity.name,
  79. description=entity.description,
  80. permissionBindingCount=permission_binding_count,
  81. createdTime=entity.created_time,
  82. updatedTime=entity.updated_time)
  83. class RolePermissionBindingDto(BaseModel):
  84. id: str
  85. roleId: str
  86. permission: str
  87. scopeType: str | None = None
  88. scopeId: str | None = None
  89. createdTime: datetime
  90. @classmethod
  91. def from_entity(cls, entity: "RolePermissionBinding") -> "RolePermissionBindingDto":
  92. return cls(
  93. id=entity.id,
  94. roleId=entity.role_id,
  95. permission=entity.permission,
  96. scopeType=entity.scope_type,
  97. scopeId=entity.scope_id,
  98. createdTime=entity.created_time)
  99. class ApiKeyDto(BaseModel):
  100. id: str
  101. name: str
  102. keyPrefix: str
  103. scopes: str | None = None
  104. expiresTime: datetime | None = None
  105. lastUsedTime: datetime | None = None
  106. revokedTime: datetime | None = None
  107. createdTime: datetime
  108. @classmethod
  109. def from_entity(cls, entity: "ApiKey") -> "ApiKeyDto":
  110. return cls(
  111. id=entity.id,
  112. name=entity.name,
  113. keyPrefix=entity.key_prefix,
  114. scopes=entity.scopes,
  115. expiresTime=entity.expires_time,
  116. lastUsedTime=entity.last_used_time,
  117. revokedTime=entity.revoked_time,
  118. createdTime=entity.created_time)
  119. class LoginRequestDto(BaseModel):
  120. username: str
  121. password: str
  122. class LoginData(BaseModel):
  123. accessToken: str
  124. tokenType: str = "bearer"
  125. expiresTime: datetime
  126. user: UserDto
  127. class TokenVerifyRequestDto(BaseModel):
  128. accessToken: str
  129. class TokenVerifyData(BaseModel):
  130. active: bool
  131. userId: str | None = None
  132. username: str | None = None
  133. expiresTime: datetime | None = None
  134. reason: str | None = None
  135. class AuthMeData(BaseModel):
  136. user: UserDto
  137. roles: list[RoleDto]
  138. permissions: list[str]
  139. class RolePermissionBindingAddRequest(BaseModel):
  140. roleId: str
  141. permission: str
  142. scopeType: str | None = None
  143. scopeId: str | None = None
  144. class RolePermissionBindingListRequest(PageRequest):
  145. roleId: str
  146. class BindingRemoveRequest(BaseModel):
  147. bindingId: str
  148. class PermissionCheckRequestDto(BaseModel):
  149. userId: str
  150. permission: str
  151. scopeType: str | None = None
  152. scopeId: str | None = None
  153. class PermissionCheckData(BaseModel):
  154. allowed: bool
  155. reason: str
  156. matchedRoleIds: list[str] = Field(default_factory=list)
  157. class ApiKeyCreateRequestDto(BaseModel):
  158. name: str
  159. scopes: str | None = None
  160. expiresTime: datetime | None = None
  161. class ApiKeyCreateData(BaseModel):
  162. apiKey: ApiKeyDto
  163. secret: str
  164. class ApiKeyRevokeRequest(BaseModel):
  165. apiKeyId: str
  166. class DeleteData(BaseModel):
  167. deleted: bool
  168. bindingId: str | None = None
  169. apiKeyId: str | None = None