Files
customer-portal/customer_portal/models/admin_user.py

73 lines
2.1 KiB
Python
Executable File

"""Admin User model with separate authentication."""
from datetime import UTC, datetime
from sqlalchemy import Boolean, Column, DateTime, Integer, String
from werkzeug.security import check_password_hash, generate_password_hash
from customer_portal.models import Base
class AdminUser(Base):
"""Admin user with username/password authentication.
Separate from Customer - admins have their own login.
"""
__tablename__ = "admin_users"
id = Column(Integer, primary_key=True)
username = Column(String(100), unique=True, nullable=False, index=True)
password_hash = Column(String(255), nullable=False)
name = Column(String(255), nullable=False)
email = Column(String(255))
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=lambda: datetime.now(UTC))
last_login_at = Column(DateTime)
def __repr__(self) -> str:
return f"<AdminUser {self.username}>"
def set_password(self, password: str) -> None:
"""Hash and set password."""
self.password_hash = generate_password_hash(password)
def check_password(self, password: str) -> bool:
"""Verify password against hash."""
return check_password_hash(self.password_hash, password)
@classmethod
def get_by_username(cls, db, username: str):
"""Get admin by username.
Args:
db: Database session
username: Admin username
Returns:
AdminUser instance or None
"""
return (
db.query(cls)
.filter(cls.username == username, cls.is_active == True) # noqa: E712
.first()
)
@classmethod
def authenticate(cls, db, username: str, password: str):
"""Authenticate admin with username and password.
Args:
db: Database session
username: Admin username
password: Plain text password
Returns:
AdminUser instance if valid, None otherwise
"""
admin = cls.get_by_username(db, username)
if admin and admin.check_password(password):
return admin
return None