2026-04-21 17:11:03 +09:30
|
|
|
"""Superadmin model."""
|
|
|
|
|
import logging
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
|
|
|
|
from gatehouse_app.extensions import db
|
|
|
|
|
from gatehouse_app.models.base import BaseModel
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Superadmin(BaseModel):
|
|
|
|
|
"""Superadmin model for SaaS platform operators.
|
|
|
|
|
|
|
|
|
|
Completely separate from User model - has its own email/password auth.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
__tablename__ = "superadmins"
|
|
|
|
|
|
|
|
|
|
email = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
|
|
|
|
password_hash = db.Column(db.String(255), nullable=False)
|
|
|
|
|
full_name = db.Column(db.String(255), nullable=True)
|
|
|
|
|
is_active = db.Column(db.Boolean, default=True, nullable=False)
|
|
|
|
|
last_login_at = db.Column(db.DateTime, nullable=True)
|
|
|
|
|
|
2026-04-28 20:54:15 +09:30
|
|
|
# Relationship to sessions (unified model, scoped to superadmin owner_type)
|
2026-04-21 17:11:03 +09:30
|
|
|
sessions = db.relationship(
|
2026-04-28 20:54:15 +09:30
|
|
|
"Session",
|
|
|
|
|
primaryjoin=(
|
|
|
|
|
"and_(Superadmin.id == foreign(Session.owner_id), "
|
|
|
|
|
"Session.owner_type == 'superadmin')"
|
|
|
|
|
),
|
|
|
|
|
cascade="all, delete-orphan",
|
|
|
|
|
lazy="dynamic",
|
2026-04-21 17:11:03 +09:30
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Relationship to audit logs
|
|
|
|
|
audit_logs = db.relationship(
|
|
|
|
|
"SuperadminAuditLog",
|
|
|
|
|
back_populates="superadmin",
|
|
|
|
|
cascade="all, delete-orphan"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
|
return f"<Superadmin {self.email}>"
|
|
|
|
|
|
|
|
|
|
def has_password_auth(self):
|
|
|
|
|
"""Check if superadmin has password authentication."""
|
|
|
|
|
return bool(self.password_hash)
|
|
|
|
|
|
|
|
|
|
def has_totp_enabled(self):
|
|
|
|
|
"""Check if superadmin has TOTP enabled."""
|
|
|
|
|
# TODO: Implement TOTP for superadmin if needed
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
def to_dict(self, exclude=None):
|
|
|
|
|
"""Convert to dictionary, excluding sensitive fields."""
|
|
|
|
|
exclude = exclude or []
|
|
|
|
|
exclude.append("password_hash")
|
|
|
|
|
return super().to_dict(exclude=exclude)
|