user.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """
  2. User model - superadmin and organization users.
  3. """
  4. from datetime import datetime
  5. from sqlalchemy import Boolean, DateTime, ForeignKey, String
  6. from sqlalchemy.dialects.postgresql import INET
  7. from sqlalchemy.orm import Mapped, mapped_column, relationship
  8. from app.models.base import Base
  9. class User(Base):
  10. """
  11. User model for both superadmin and organization users.
  12. Roles:
  13. - superadmin: Full system access (organization_id = NULL)
  14. - owner: Organization owner (can manage users)
  15. - admin: Full organization access (cannot manage users)
  16. - manager: Read-only analytics access
  17. - operator: Access to devices and beacons
  18. - viewer: Read-only access
  19. """
  20. __tablename__ = "users"
  21. id: Mapped[int] = mapped_column(primary_key=True)
  22. email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False)
  23. hashed_password: Mapped[str] = mapped_column(String(255), nullable=False)
  24. full_name: Mapped[str | None] = mapped_column(String(255))
  25. phone: Mapped[str | None] = mapped_column(String(50))
  26. # Role: superadmin (cloud), admin (cloud), owner (org), user (org)
  27. role: Mapped[str] = mapped_column(String(20), nullable=False)
  28. # Status: pending, active, suspended, deleted
  29. status: Mapped[str] = mapped_column(
  30. String(20), default="pending", nullable=False
  31. )
  32. # Organization (NULL for superadmin/admin)
  33. organization_id: Mapped[int | None] = mapped_column(
  34. ForeignKey("organizations.id", ondelete="CASCADE")
  35. )
  36. # Admin notes (visible only to superadmin)
  37. notes: Mapped[str | None] = mapped_column(String)
  38. # Email verification
  39. email_verified: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
  40. email_verification_token: Mapped[str | None] = mapped_column(String(255))
  41. email_verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
  42. # Password reset
  43. password_reset_token: Mapped[str | None] = mapped_column(String(255))
  44. password_reset_expires: Mapped[datetime | None] = mapped_column(
  45. DateTime(timezone=True)
  46. )
  47. # Login tracking
  48. last_login_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
  49. last_login_ip: Mapped[str | None] = mapped_column(INET)
  50. # Relationships
  51. organization: Mapped["Organization | None"] = relationship(
  52. "Organization", back_populates="users"
  53. )
  54. refresh_tokens: Mapped[list["RefreshToken"]] = relationship(
  55. "RefreshToken", back_populates="user", cascade="all, delete-orphan"
  56. )
  57. audit_logs: Mapped[list["AuditLog"]] = relationship(
  58. "AuditLog", back_populates="user"
  59. )
  60. @property
  61. def is_superadmin(self) -> bool:
  62. """Check if user is superadmin."""
  63. return self.role == "superadmin"
  64. @property
  65. def is_owner(self) -> bool:
  66. """Check if user is organization owner."""
  67. return self.role == "owner"
  68. def __repr__(self) -> str:
  69. return f"<User {self.id}: {self.email} ({self.role})>"