Spaces:
Runtime error
Runtime error
Download app/models/user.py from rahim237cm/jurix-api: direct link, hf CLI and curl.
- Browser
- Download file 4.67 kB
-
https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/app/models/user.py
- Command line
-
hf download hf://spaces/rahim237cm/jurix-api/app/models/user.py
-
curl -L -o user.py https://huggingface.co/spaces/rahim237cm/jurix-api/resolve/main/app/models/user.py
4.67 kB
| """ | |
| User model for authentication and authorization. | |
| Provides user management with role-based access control (RBAC). | |
| Author: JuriX Team | |
| Date: 2026-01-12 | |
| """ | |
| from datetime import datetime | |
| from sqlalchemy import Boolean, Column, DateTime, Integer, String | |
| from sqlalchemy.orm import relationship | |
| from app.core.database import Base | |
| class User(Base): | |
| """ | |
| User model for authentication and authorization. | |
| Attributes: | |
| id: Unique user identifier | |
| email: User email (unique, used for login) | |
| username: Display username | |
| hashed_password: Bcrypt hashed password | |
| full_name: User's full name | |
| role: User role (user, admin, superadmin) | |
| is_active: Account active status | |
| is_verified: Email verification status | |
| created_at: Account creation timestamp | |
| updated_at: Last update timestamp | |
| last_login_at: Last login timestamp | |
| Relationships: | |
| conversations: User's chat conversations | |
| """ | |
| __tablename__ = "users" | |
| # Primary key | |
| id = Column(Integer, primary_key=True, index=True) | |
| # Authentication | |
| email = Column(String(255), unique=True, index=True, nullable=False) | |
| username = Column(String(100), unique=True, index=True, nullable=False) | |
| # NULLABLE : un compte cree par Google n'a pas de mot de passe. La | |
| # contrainte CHECK `ck_users_au_moins_une_identite` garantit qu'un compte | |
| # porte toujours au moins une identite — mot de passe ou Google. | |
| hashed_password = Column(String(255), nullable=True) | |
| # Claim `sub` du jeton d'identite Google, seul identifiant stable d'un | |
| # compte Google (l'adresse e-mail peut changer chez eux). Unique et | |
| # nullable : les comptes par mot de passe le laissent a NULL, et PostgreSQL | |
| # tolere plusieurs NULL sur un index unique. | |
| google_sub = Column(String(255), nullable=True, unique=True, index=True) | |
| # Profile | |
| full_name = Column(String(255), nullable=True) | |
| # Le profil choisi a l'entree du site (app/core/personas.py), garde pour | |
| # suivre l'utilisateur d'un appareil a l'autre. NULL : aucun choix | |
| # enregistre ; un refus de repondre n'est jamais stocke. La contrainte | |
| # `ck_users_persona_connu` (migration 015) n'admet que les quatre profils. | |
| persona = Column(String(50), nullable=True) | |
| # Authorization | |
| role = Column( | |
| String(50), | |
| nullable=False, | |
| default="user", | |
| index=True, | |
| comment="Role: user, admin, superadmin", | |
| ) | |
| # Status flags | |
| is_active = Column(Boolean, default=True, nullable=False) | |
| is_verified = Column(Boolean, default=False, nullable=False) | |
| # Timestamps | |
| created_at = Column(DateTime, default=datetime.utcnow, nullable=False) | |
| updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False) | |
| last_login_at = Column(DateTime, nullable=True) | |
| # Relationships | |
| # | |
| # `lazy="raise"` et NON `selectin`. `get_current_user` charge cet objet a | |
| # CHAQUE requete authentifiee : avec un chargement empresse, chacune | |
| # tirait en plus toutes les conversations du compte — la liste que | |
| # l'historique de chat fait precisement grossir, pour une relation que | |
| # personne ne lit. | |
| # | |
| # La cascade ORM est retiree en meme temps, et c'est indissociable : elle | |
| # exige de charger la collection pour supprimer un compte, ce que `raise` | |
| # interdit. La suppression des conversations d'un compte est desormais | |
| # EXPLICITE dans `admin.delete_user`. | |
| conversations = relationship("Conversation", back_populates="user", lazy="raise") | |
| def __repr__(self) -> str: | |
| return f"<User(id={self.id}, email='{self.email}', role='{self.role}')>" | |
| def has_role(self, required_role: str) -> bool: | |
| """ | |
| Check if user has required role or higher. | |
| Role hierarchy: superadmin > admin > user | |
| Args: | |
| required_role: Required role to check | |
| Returns: | |
| True if user has required role or higher | |
| Example: | |
| >>> user.role = "admin" | |
| >>> user.has_role("user") # True | |
| >>> user.has_role("superadmin") # False | |
| """ | |
| role_hierarchy = {"superadmin": 3, "admin": 2, "user": 1} | |
| user_level = role_hierarchy.get(self.role, 0) | |
| required_level = role_hierarchy.get(required_role, 0) | |
| return user_level >= required_level | |
| def is_admin(self) -> bool: | |
| """Check if user has admin or superadmin role.""" | |
| return self.role in ("admin", "superadmin") | |
| def is_superadmin(self) -> bool: | |
| """Check if user has superadmin role.""" | |
| return self.role == "superadmin" | |