2026-03-04 00:18:33 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
2026-03-04 15:30:13 +08:00
|
|
|
from ipaddress import ip_address, ip_network
|
2026-03-04 00:18:33 +08:00
|
|
|
|
2026-03-04 15:30:13 +08:00
|
|
|
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
|
|
|
|
|
|
|
|
|
from app.models.token_binding import BINDING_MODE_ALL, BINDING_MODE_MULTIPLE, BINDING_MODE_SINGLE
|
2026-03-04 00:18:33 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
class BindingItem(BaseModel):
|
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
|
|
|
|
id: int
|
|
|
|
|
token_display: str
|
|
|
|
|
bound_ip: str
|
2026-03-04 15:30:13 +08:00
|
|
|
binding_mode: str
|
|
|
|
|
allowed_ips: list[str]
|
2026-03-04 00:18:33 +08:00
|
|
|
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)
|
2026-03-04 15:30:13 +08:00
|
|
|
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
|
2026-03-04 00:18:33 +08:00
|
|
|
|
2026-03-04 15:30:13 +08:00
|
|
|
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
|
2026-03-04 00:18:33 +08:00
|
|
|
|
2026-03-04 15:30:13 +08:00
|
|
|
raise ValueError("Unsupported binding mode.")
|