06/15 Phase 1 + 2 codes
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
"""User model. Argon2 password hashing, role/trust fields, Flask-Login mixin."""
|
||||
from datetime import datetime
|
||||
from flask_login import UserMixin
|
||||
from app.extensions import db
|
||||
from app.models.enums import Role, UserStatus, TrustTier
|
||||
from app.utils.security import hash_password, verify_password
|
||||
|
||||
|
||||
class User(UserMixin, db.Model):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = db.Column(db.BigInteger().with_variant(db.Integer, "sqlite"), primary_key=True, autoincrement=True)
|
||||
email = db.Column(db.String(255), unique=True, nullable=False, index=True)
|
||||
password_hash = db.Column(db.String(255), nullable=False)
|
||||
display_name = db.Column(db.String(80), nullable=False)
|
||||
|
||||
role = db.Column(db.Enum(Role), nullable=False, default=Role.free)
|
||||
status = db.Column(db.Enum(UserStatus), nullable=False, default=UserStatus.active)
|
||||
locale = db.Column(db.String(5), nullable=False, default="en")
|
||||
|
||||
tier_id = db.Column(db.BigInteger, db.ForeignKey("plans.id"), nullable=True)
|
||||
tier = db.relationship("Plan", back_populates="users")
|
||||
|
||||
trust_score = db.Column(db.Integer, nullable=False, default=0)
|
||||
trust_tier = db.Column(db.Enum(TrustTier), nullable=False, default=TrustTier.new)
|
||||
|
||||
verified = db.Column(db.Boolean, nullable=False, default=False) # identity/badge
|
||||
email_verified = db.Column(db.Boolean, nullable=False, default=False)
|
||||
|
||||
last_login_at = db.Column(db.DateTime, nullable=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow,
|
||||
onupdate=datetime.utcnow, nullable=False)
|
||||
|
||||
trust_events = db.relationship("TrustEvent", back_populates="user",
|
||||
lazy="dynamic", cascade="all, delete-orphan")
|
||||
|
||||
# --- password ---
|
||||
def set_password(self, raw):
|
||||
self.password_hash = hash_password(raw)
|
||||
|
||||
def check_password(self, raw):
|
||||
return verify_password(self.password_hash, raw)
|
||||
|
||||
# --- RBAC helpers ---
|
||||
@property
|
||||
def is_admin(self):
|
||||
return self.role == Role.admin
|
||||
|
||||
@property
|
||||
def is_moderator(self):
|
||||
return self.role in (Role.moderator, Role.admin)
|
||||
|
||||
@property
|
||||
def is_subscriber(self):
|
||||
return self.role == Role.subscriber
|
||||
|
||||
def has_role(self, *roles):
|
||||
return self.role in roles
|
||||
|
||||
@property
|
||||
def is_active(self):
|
||||
# Flask-Login uses this to permit login.
|
||||
return self.status == UserStatus.active
|
||||
|
||||
def __repr__(self):
|
||||
return f"<User {self.email}>"
|
||||
Reference in New Issue
Block a user