utp_service/backend/app/models/ssh_key.py
2026-04-09 14:33:34 +03:00

109 lines
4.6 KiB
Python

from datetime import datetime
from typing import Optional
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Integer, String, func
from sqlalchemy.orm import Mapped, mapped_column, relationship
from .base import Base
class SSHKey(Base):
"""Registry of SSH private key files stored on the server filesystem."""
__tablename__ = "ssh_keys"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
name: Mapped[str] = mapped_column(String(100), nullable=False, unique=True)
path: Mapped[str] = mapped_column(String(500), nullable=False)
description: Mapped[Optional[str]] = mapped_column(String(1000), nullable=True)
created_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), server_default=func.now(), nullable=False
)
class ObjectSSHCredential(Base):
"""Ordered list of SSH credentials (password or key) for an object.
Tried in ascending priority order when connecting to hosts of this object.
type='password': username + password_enc required.
type='key': username + ssh_key_id required.
"""
__tablename__ = "object_ssh_credentials"
__table_args__ = (
CheckConstraint("type IN ('password', 'key')", name="ck_obj_ssh_cred_type"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
object_id: Mapped[int] = mapped_column(
Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False, index=True
)
type: Mapped[str] = mapped_column(String(10), nullable=False)
username: Mapped[str] = mapped_column(String(100), nullable=False)
password_enc: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
ssh_key_id: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("ssh_keys.id", ondelete="SET NULL"), nullable=True
)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
object: Mapped["Object"] = relationship("Object", back_populates="ssh_credentials")
ssh_key: Mapped[Optional["SSHKey"]] = relationship("SSHKey")
class ObjectCategoryCredential(Base):
"""Per-category credentials on an object.
protocol='ssh': SSH access to devices of this category.
protocol='http': HTTP/API access (cameras, MikroTik, Proxmox, etc.).
type='password': username + password_enc required.
type='key': username + ssh_key_id required (ssh only).
"""
__tablename__ = "object_category_credentials"
__table_args__ = (
CheckConstraint("protocol IN ('ssh', 'http')", name="ck_obj_cat_cred_protocol"),
CheckConstraint("type IN ('password', 'key')", name="ck_obj_cat_cred_type"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
object_id: Mapped[int] = mapped_column(
Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False, index=True
)
category: Mapped[str] = mapped_column(String(20), nullable=False)
protocol: Mapped[str] = mapped_column(String(10), nullable=False)
type: Mapped[str] = mapped_column(String(10), nullable=False, default="password")
username: Mapped[str] = mapped_column(String(100), nullable=False)
password_enc: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
ssh_key_id: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("ssh_keys.id", ondelete="SET NULL"), nullable=True
)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
object: Mapped["Object"] = relationship("Object", back_populates="category_credentials")
ssh_key: Mapped[Optional["SSHKey"]] = relationship("SSHKey")
class GlobalSSHCredential(Base):
"""App-wide SSH credential used as last-resort fallback for all objects/devices.
Tried after all object-level credentials are exhausted.
type='password': username + password_enc required.
type='key': username + ssh_key_id required.
"""
__tablename__ = "global_ssh_credentials"
__table_args__ = (
CheckConstraint("type IN ('password', 'key')", name="ck_global_ssh_cred_type"),
)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
type: Mapped[str] = mapped_column(String(10), nullable=False)
username: Mapped[str] = mapped_column(String(100), nullable=False)
password_enc: Mapped[Optional[str]] = mapped_column(String(500), nullable=True)
ssh_key_id: Mapped[Optional[int]] = mapped_column(
Integer, ForeignKey("ssh_keys.id", ondelete="SET NULL"), nullable=True
)
priority: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
description: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
ssh_key: Mapped[Optional["SSHKey"]] = relationship("SSHKey")