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

1""" 

2Authentication models for managing users. 

3Only stores username and metadata - passwords are never stored. 

4Each user gets their own encrypted database file. 

5""" 

6 

7from datetime import datetime, UTC 

8from functools import partial 

9 

10from sqlalchemy import Column, Integer, String 

11from sqlalchemy_utc import UtcDateTime 

12 

13from ...config.paths import get_user_database_filename 

14from .base import Base 

15 

16 

17class User(Base): 

18 """ 

19 User model - stored in a central auth database. 

20 

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. 

26 

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 """ 

31 

32 __tablename__ = "users" 

33 

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) 

38 

39 # Metadata only - no sensitive data 

40 database_version = Column(Integer, default=1) 

41 

42 def __repr__(self): 

43 return f"<User {self.username}>" 

44 

45 @property 

46 def database_path(self): 

47 """Path to this user's encrypted database file.""" 

48 return get_user_database_filename(self.username)