from __future__ import annotations from datetime import datetime from pydantic import BaseModel, Field, field_validator, model_validator class SsoTicketRequest(BaseModel): externalUserId: str = Field(min_length=1, max_length=120) verifiedPhone: str | None = Field(default=None, min_length=11, max_length=20) displayName: str | None = Field(default=None, max_length=100) returnUrl: str | None = Field(default=None, max_length=1000) class SsoExchangeRequest(BaseModel): code: str = Field(min_length=20, max_length=200) class SsoClientSaveRequest(BaseModel): appId: str = Field(min_length=3, max_length=80, pattern=r"^[A-Za-z0-9][A-Za-z0-9._-]+$") name: str = Field(min_length=1, max_length=100) redirectUris: list[str] = Field(default_factory=list, max_length=20) allowAutoRegister: bool = False defaultEntitlementPlanId: int | None = Field(default=None, gt=0) status: int = Field(default=1, ge=0, le=1) @field_validator("redirectUris") @classmethod def validate_redirect_uris(cls, values: list[str]) -> list[str]: cleaned: list[str] = [] for raw in values: value = raw.strip() if not value: continue if not value.startswith(("https://", "http://localhost", "http://127.0.0.1")): raise ValueError("回跳地址必须使用 HTTPS;本地调试可使用 localhost 或 127.0.0.1") if value not in cleaned: cleaned.append(value) return cleaned @model_validator(mode="after") def validate_auto_register_plan(self): if self.allowAutoRegister and self.defaultEntitlementPlanId is None: raise ValueError("允许自动注册时必须选择默认权益版本") return self class SsoClientUpdateRequest(BaseModel): name: str = Field(min_length=1, max_length=100) redirectUris: list[str] = Field(default_factory=list, max_length=20) allowAutoRegister: bool = False defaultEntitlementPlanId: int | None = Field(default=None, gt=0) status: int = Field(default=1, ge=0, le=1) @field_validator("redirectUris") @classmethod def validate_redirect_uris(cls, values: list[str]) -> list[str]: return SsoClientSaveRequest.validate_redirect_uris(values) @model_validator(mode="after") def validate_auto_register_plan(self): return SsoClientSaveRequest.validate_auto_register_plan(self) class SsoPublicConfigRequest(BaseModel): userClientUrl: str = Field(default="", max_length=1000) @field_validator("userClientUrl") @classmethod def validate_user_client_url(cls, value: str) -> str: cleaned = value.strip().rstrip("/") if cleaned and not cleaned.startswith(("https://", "http://localhost", "http://127.0.0.1")): raise ValueError("用户端公网地址必须使用 HTTPS;本地调试可使用 localhost 或 127.0.0.1") return cleaned class SsoClientItem(BaseModel): id: int appId: str name: str redirectUris: list[str] allowAutoRegister: bool defaultEntitlementPlanId: int | None = None defaultEntitlementPlanName: str | None = None status: int identityCount: int lastUsedAt: datetime | None = None createdAt: datetime updatedAt: datetime class SsoIdentityItem(BaseModel): id: int clientId: int clientName: str appId: str userId: int userName: str phone: str externalUserId: str displayNameSnapshot: str | None = None lastLoginAt: datetime | None = None createdAt: datetime class SsoAuditItem(BaseModel): id: int appId: str | None = None clientName: str | None = None userId: int | None = None externalUserId: str | None = None action: str status: str errorMessage: str | None = None ip: str | None = None createdAt: datetime