""" User model. Roles ------ There are exactly two roles: - **admin** – full access to everything: create/edit/delete any item or user, view all storage bins, access audit logs. - **customer** – limited view: can browse listed inventory and manage their own storage bins. The role is stored as a simple boolean ``is_admin`` flag. The application config defines the one e-mail address that is ALWAYS admin (see ``settings.admin_email``). On every login that address will have its flag forced to True, so even if the database is tampered with, the admin cannot be locked out. """ from datetime import datetime from sqlalchemy import Boolean, DateTime, String, func from sqlalchemy.orm import Mapped, mapped_column from app.database import Base class User(Base): __tablename__ = "users" # ------------------------------------------------------------------ # Columns # ------------------------------------------------------------------ id: Mapped[int] = mapped_column(primary_key=True, index=True) # E-mail is the login identifier. Must be unique across the system. email: Mapped[str] = mapped_column( String(254), # RFC 5321 max e-mail length unique=True, index=True, nullable=False, ) # Display name shown in the UI. Defaults to the e-mail local part if # not set during registration. display_name: Mapped[str] = mapped_column(String(100), nullable=False) # bcrypt hash of the password. We never store plaintext. hashed_password: Mapped[str] = mapped_column(String(255), nullable=False) # True = admin, False = customer. The business rule that # settings.admin_email is always admin is enforced in the auth layer, # not here. is_admin: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False) # Soft-disable an account without deleting it. A disabled account cannot # log in. is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False) # Audit timestamps set automatically by the database. created_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), nullable=False ) updated_at: Mapped[datetime] = mapped_column( DateTime, server_default=func.now(), onupdate=func.now(), nullable=False, ) def __repr__(self) -> str: role = "admin" if self.is_admin else "customer" return f""