Files
saas_backend/app/models/auth/module_environment_model.py
T

65 lines
2.6 KiB
Python
Raw Normal View History

import uuid
2026-08-31 20:04:12 -04:00
from sqlalchemy import Column, String, Boolean, DateTime, Text, func, ForeignKey, UniqueConstraint, JSON
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from app.config.database import Base
class ModuleEnvironment(Base):
__tablename__ = "module_environments"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
module_id = Column(UUID(as_uuid=True), ForeignKey("modules.id"), nullable=False, index=True)
slug = Column(String, nullable=False, index=True)
frontend_base_url = Column(String, nullable=False)
sso_entry_path = Column(String, default="/sso/start")
backend_base_url = Column(String, nullable=False)
sso_exchange_endpoint = Column(String, default="/internal/sso/exchange")
permission_sync_endpoint = Column(String, default="/internal/permissions/sync")
provisioning_endpoint = Column(String, default="/internal/tenants/provision")
trust_type = Column(String, nullable=False)
2026-08-31 20:04:12 -04:00
# Legacy plaintext column. Blanked by migration b2e1d4f5a602 and kept only so
# a downgrade has somewhere to put the secrets back. Read through
# `credentials` — never directly.
trust_credentials = Column(JSON, nullable=False)
2026-08-31 20:04:12 -04:00
trust_credentials_enc = Column(Text, nullable=True)
is_default = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
module = relationship("Module", back_populates="environments")
2026-08-31 20:04:12 -04:00
@property
def credentials(self) -> dict:
"""The trust credentials, decrypted.
Falls back to the legacy plaintext column so a row that has not been
migrated yet still works. Once migration b2e1d4f5a602 has run, that
fallback returns an empty dict and the encrypted column is the only
source.
"""
from app.core.crypto import decrypt_json, is_encrypted
if is_encrypted(self.trust_credentials_enc):
return decrypt_json(self.trust_credentials_enc)
return self.trust_credentials or {}
@credentials.setter
def credentials(self, value: dict) -> None:
from app.core.crypto import encrypt_json
self.trust_credentials_enc = encrypt_json(value or {})
# Never leave a plaintext copy behind.
self.trust_credentials = {}
__table_args__ = (
UniqueConstraint('module_id', 'slug', name='uq_module_env_slug'),
)
def __repr__(self):
return f"<ModuleEnvironment {self.slug} for {self.module_id}>"