67 lines
1.8 KiB
Python
67 lines
1.8 KiB
Python
from typing import List, Literal, Optional
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
|
|
|
|
USERNAME_PATTERN = r"^[a-z_][a-z0-9_-]{0,31}$"
|
|
GROUPNAME_PATTERN = r"^[a-z_][a-z0-9_-]{0,31}$"
|
|
|
|
|
|
class UserCreateRequest(BaseModel):
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
username: str = Field(pattern=USERNAME_PATTERN)
|
|
password_hash: str = Field(min_length=10, max_length=512)
|
|
primary_group: Optional[str] = Field(default=None, pattern=GROUPNAME_PATTERN)
|
|
groups: List[str] = Field(default_factory=list)
|
|
|
|
@field_validator("groups")
|
|
@classmethod
|
|
def validate_groups(cls, value: List[str]) -> List[str]:
|
|
deduped = list(dict.fromkeys(value))
|
|
for group in deduped:
|
|
if not __import__("re").match(GROUPNAME_PATTERN, group):
|
|
raise ValueError(f"Invalid group name: {group}")
|
|
return deduped
|
|
|
|
|
|
class UserSummary(BaseModel):
|
|
username: str
|
|
uid: int
|
|
gid: int
|
|
home_dir: str
|
|
shell: str
|
|
|
|
|
|
class GroupCreateRequest(BaseModel):
|
|
groupname: str = Field(pattern=GROUPNAME_PATTERN)
|
|
|
|
|
|
class GroupSummary(BaseModel):
|
|
groupname: str
|
|
gid: int
|
|
members: List[str]
|
|
|
|
|
|
class UserGroupsUpdateRequest(BaseModel):
|
|
groups: List[str] = Field(min_length=1)
|
|
mode: Literal["append", "replace"] = "append"
|
|
|
|
@field_validator("groups")
|
|
@classmethod
|
|
def validate_groups(cls, value: List[str]) -> List[str]:
|
|
deduped = list(dict.fromkeys(value))
|
|
for group in deduped:
|
|
if not __import__("re").match(GROUPNAME_PATTERN, group):
|
|
raise ValueError(f"Invalid group name: {group}")
|
|
return deduped
|
|
|
|
|
|
class UserPasswordUpdateRequest(BaseModel):
|
|
password_hash: str = Field(min_length=10, max_length=512)
|
|
|
|
|
|
class ApiResponse(BaseModel):
|
|
status: str = "ok"
|
|
message: str
|