Coverage for src / local_deep_research / database / models / auth.py: 100%
18 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-25 01:07 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-25 01:07 +0000
1"""
2Authentication models for managing users.
3Only stores username and metadata - passwords are never stored.
4Each user gets their own encrypted database file.
5"""
7from datetime import datetime, UTC
8from functools import partial
10from sqlalchemy import Column, Integer, String
11from sqlalchemy_utc import UtcDateTime
13from ...config.paths import get_user_database_filename
14from .base import Base
17class User(Base):
18 """
19 User model - stored in a central auth database.
21 IMPORTANT — Authentication Architecture:
22 Passwords are NEVER stored here (no password_hash column, no set_password
23 method). Authentication works by attempting to decrypt the user's
24 per-user SQLCipher database with the supplied password. If decryption
25 succeeds the password is correct; if it fails the password is wrong.
27 On password change only the SQLCipher database is rekeyed — there is
28 nothing to update in this model. Do NOT add password storage here
29 without reworking the entire auth flow.
30 """
32 __tablename__ = "users"
34 id = Column(Integer, primary_key=True)
35 username = Column(String(80), unique=True, nullable=False, index=True)
36 created_at = Column(UtcDateTime, default=partial(datetime.now, UTC))
37 last_login = Column(UtcDateTime)
39 # Metadata only - no sensitive data
40 database_version = Column(Integer, default=1)
42 def __repr__(self):
43 return f"<User {self.username}>"
45 @property
46 def database_path(self):
47 """Path to this user's encrypted database file."""
48 return get_user_database_filename(self.username)