from __future__ import annotations from datetime import datetime from ipaddress import ip_address, ip_network from pydantic import BaseModel, ConfigDict, Field, model_validator from app.models.token_binding import BINDING_MODE_ALL, BINDING_MODE_MULTIPLE, BINDING_MODE_SINGLE class BindingItem(BaseModel): model_config = ConfigDict(from_attributes=True) id: int token_display: str bound_ip: str binding_mode: str allowed_ips: list[str] status: int status_label: str first_used_at: datetime last_used_at: datetime created_at: datetime class BindingListResponse(BaseModel): items: list[BindingItem] total: int page: int page_size: int class BindingActionRequest(BaseModel): id: int = Field(gt=0) class BindingIPUpdateRequest(BaseModel): id: int = Field(gt=0) binding_mode: str = Field(default=BINDING_MODE_SINGLE) allowed_ips: list[str] = Field(default_factory=list) @model_validator(mode="after") def validate_binding_rule(self): allowed_ips = [item.strip() for item in self.allowed_ips if item.strip()] if self.binding_mode == BINDING_MODE_ALL: self.allowed_ips = [] return self if self.binding_mode == BINDING_MODE_SINGLE: if len(allowed_ips) != 1: raise ValueError("Single binding mode requires exactly one IP or CIDR.") ip_network(allowed_ips[0], strict=False) self.allowed_ips = allowed_ips return self if self.binding_mode == BINDING_MODE_MULTIPLE: if not allowed_ips: raise ValueError("Multiple binding mode requires at least one IP.") normalized: list[str] = [] for item in allowed_ips: ip_address(item) normalized.append(item) self.allowed_ips = normalized return self raise ValueError("Unsupported binding mode.")