Files
boc/landvex/user-management/app/models.py
T

85 lines
3.4 KiB
Python
Raw Normal View History

"""SQLAlchemy-modeller för user management."""
import uuid
from datetime import datetime
from enum import Enum as PyEnum
from sqlalchemy import Column, String, DateTime, Boolean, ForeignKey, Enum, Text
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import relationship
from app.database import Base
class UserRole(str, PyEnum):
ADMIN = "admin"
MANAGER = "manager"
ANALYST = "analyst"
VIEWER = "viewer"
class UserStatus(str, PyEnum):
ACTIVE = "active"
INACTIVE = "inactive"
PENDING = "pending" # Invited, not yet set password
class User(Base):
__tablename__ = "users"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String(255), unique=True, nullable=False, index=True)
hashed_password = Column(String(255), nullable=True) # NULL until invitation accepted
first_name = Column(String(100), nullable=False)
last_name = Column(String(100), nullable=False)
role = Column(Enum(UserRole), nullable=False, default=UserRole.VIEWER)
status = Column(Enum(UserStatus), nullable=False, default=UserStatus.PENDING)
is_superuser = Column(Boolean, default=False)
last_login_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
tenant = relationship("Tenant", back_populates="users")
invitations_sent = relationship("Invitation", foreign_keys="Invitation.invited_by_id", back_populates="inviter")
invitations_received = relationship("Invitation", foreign_keys="Invitation.email", primaryjoin="User.email == Invitation.email", viewonly=True)
def __repr__(self):
return f"<User {self.email} ({self.role.value})>"
class Tenant(Base):
__tablename__ = "tenants"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
name = Column(String(255), nullable=False)
slug = Column(String(100), unique=True, nullable=False, index=True)
description = Column(Text, nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
updated_at = Column(DateTime(timezone=True), default=datetime.utcnow, onupdate=datetime.utcnow)
users = relationship("User", back_populates="tenant")
def __repr__(self):
return f"<Tenant {self.name}>"
class Invitation(Base):
__tablename__ = "invitations"
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
email = Column(String(255), nullable=False, index=True)
token = Column(String(255), unique=True, nullable=False, index=True)
role = Column(Enum(UserRole), nullable=False, default=UserRole.VIEWER)
tenant_id = Column(UUID(as_uuid=True), ForeignKey("tenants.id"), nullable=True)
invited_by_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False)
expires_at = Column(DateTime(timezone=True), nullable=False)
accepted_at = Column(DateTime(timezone=True), nullable=True)
created_at = Column(DateTime(timezone=True), default=datetime.utcnow)
inviter = relationship("User", foreign_keys=[invited_by_id], back_populates="invitations_sent")
tenant = relationship("Tenant")
def __repr__(self):
return f"<Invitation {self.email} ({self.role.value})>"