"""Models for API views.""" import uuid from datetime import datetime from typing import Annotated, Self, override from sqlmodel import Field, SQLModel from pydantic import AfterValidator, IPvAnyAddress, IPvAnyNetwork from sshecret.crypto import public_key_validator from . import models class ClientView(SQLModel): """View for a single client.""" id: uuid.UUID name: str description: str | None = None public_key: str policies: list[str] = ["0.0.0.0/0", "::/0"] secrets: list[str] = Field(default_factory=list) created_at: datetime | None updated_at: datetime | None = None @classmethod def from_client_list(cls, clients: list[models.Client]) -> list[Self]: """Generate a list of responses from a list of clients.""" responses: list[Self] = [cls.from_client(client) for client in clients] return responses @classmethod def from_client(cls, client: models.Client) -> Self: """Instantiate from a client.""" view = cls( id=client.id, name=client.name, description=client.description, public_key=client.public_key, created_at=client.created_at, updated_at=client.updated_at or None, ) if client.secrets: view.secrets = [secret.name for secret in client.secrets] if client.policies: view.policies = [policy.source for policy in client.policies] return view class ClientQueryResult(SQLModel): """Result class for queries towards the client list.""" clients: list[ClientView] = Field(default_factory=list) total_results: int remaining_results: int class ClientCreate(SQLModel): """Model to create a client.""" name: str description: str | None = None public_key: Annotated[str, AfterValidator(public_key_validator)] def to_client(self) -> models.Client: """Instantiate a client.""" return models.Client( name=self.name, public_key=self.public_key, description=self.description, ) class ClientUpdate(SQLModel): """Model to update the client public key.""" public_key: Annotated[str, AfterValidator(public_key_validator)] class BodyValue(SQLModel): """A generic model with just a value parameter.""" value: str class ClientSecretPublic(SQLModel): """Public model to manage client secrets.""" name: str secret: str description: str | None = None @classmethod def from_client_secret(cls, client_secret: models.ClientSecret) -> Self: """Instantiate from ClientSecret.""" return cls( name=client_secret.name, secret=client_secret.secret, description=client_secret.description, ) class ClientSecretResponse(ClientSecretPublic): """A secret view.""" created_at: datetime | None updated_at: datetime | None = None @override @classmethod def from_client_secret(cls, client_secret: models.ClientSecret) -> Self: """Instantiate from ClientSecret.""" return cls( name=client_secret.name, secret=client_secret.secret, created_at=client_secret.created_at, updated_at=client_secret.updated_at, ) class ClientPolicyView(SQLModel): """Update object for client policy.""" sources: list[str] = ["0.0.0.0/0", "::/0"] @classmethod def from_client(cls, client: models.Client) -> Self: """Create from client.""" if not client.policies: return cls() return cls(sources=[policy.source for policy in client.policies]) class ClientPolicyUpdate(SQLModel): """Model for updating policies.""" sources: list[IPvAnyAddress | IPvAnyNetwork] class ClientSecretList(SQLModel): """Model for aggregating identically named secrets.""" name: str clients: list[str] class ClientReference(SQLModel): """Reference to a client.""" id: str name: str class ClientSecretDetailList(SQLModel): """A more detailed version of the ClientSecretList.""" name: str ids: list[str] = Field(default_factory=list) clients: list[ClientReference] = Field(default_factory=list) class AuditInfo(SQLModel): """Information about audit information.""" entries: int