Add superadmin routes to API
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Superadmin models."""
|
||||
from gatehouse_app.models.superadmin.superadmin import Superadmin
|
||||
from gatehouse_app.models.superadmin.superadmin_session import SuperadminSession, SuperadminSessionStatus
|
||||
|
||||
__all__ = ["Superadmin", "SuperadminSession", "SuperadminSessionStatus"]
|
||||
@@ -0,0 +1,56 @@
|
||||
"""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)
|
||||
|
||||
# Relationship to sessions
|
||||
sessions = db.relationship(
|
||||
"SuperadminSession",
|
||||
back_populates="superadmin",
|
||||
cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
# 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)
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Superadmin session model."""
|
||||
import logging
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from gatehouse_app.extensions import db
|
||||
from gatehouse_app.models.base import BaseModel
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SuperadminSessionStatus:
|
||||
"""Session status constants."""
|
||||
ACTIVE = "active"
|
||||
REVOKED = "revoked"
|
||||
EXPIRED = "expired"
|
||||
|
||||
|
||||
class SuperadminSession(BaseModel):
|
||||
"""Session model for superadmin authentication."""
|
||||
|
||||
__tablename__ = "superadmin_sessions"
|
||||
|
||||
superadmin_id = db.Column(
|
||||
db.String(36),
|
||||
db.ForeignKey("superadmins.id"),
|
||||
nullable=False,
|
||||
index=True
|
||||
)
|
||||
token = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||
expires_at = db.Column(db.DateTime, nullable=False)
|
||||
last_activity_at = db.Column(
|
||||
db.DateTime,
|
||||
nullable=False,
|
||||
default=lambda: datetime.now(timezone.utc)
|
||||
)
|
||||
ip_address = db.Column(db.String(45), nullable=True)
|
||||
user_agent = db.Column(db.Text, nullable=True)
|
||||
revoked_at = db.Column(db.DateTime, nullable=True)
|
||||
revoked_reason = db.Column(db.String(255), nullable=True)
|
||||
|
||||
# Relationship
|
||||
superadmin = db.relationship("Superadmin", back_populates="sessions")
|
||||
|
||||
def __repr__(self):
|
||||
return f"<SuperadminSession superadmin_id={self.superadmin_id}>"
|
||||
|
||||
def is_active(self):
|
||||
"""Check if session is currently active."""
|
||||
now = datetime.now(timezone.utc)
|
||||
expires_at = self.expires_at
|
||||
if expires_at.tzinfo is None:
|
||||
expires_at = expires_at.replace(tzinfo=timezone.utc)
|
||||
return (
|
||||
self.deleted_at is None
|
||||
and self.revoked_at is None
|
||||
and expires_at > now
|
||||
)
|
||||
|
||||
def is_expired(self):
|
||||
"""Check if session has expired."""
|
||||
now = datetime.now(timezone.utc)
|
||||
expires_at = self.expires_at
|
||||
if expires_at.tzinfo is None:
|
||||
expires_at = expires_at.replace(tzinfo=timezone.utc)
|
||||
return now > expires_at
|
||||
|
||||
def revoke(self, reason: str = None):
|
||||
"""Revoke the session."""
|
||||
self.revoked_at = datetime.now(timezone.utc)
|
||||
if reason:
|
||||
self.revoked_reason = reason
|
||||
from gatehouse_app import db
|
||||
db.session.commit()
|
||||
|
||||
def to_dict(self, exclude=None):
|
||||
"""Convert to dictionary, excluding sensitive fields."""
|
||||
exclude = exclude or []
|
||||
exclude.append("token")
|
||||
return super().to_dict(exclude=exclude)
|
||||
Reference in New Issue
Block a user